Merge branch '3006.x' into merge/3007.x/3006.x

This commit is contained in:
Daniel A. Wozniak 2024-10-25 14:50:34 -07:00
commit 1a834761bf
67 changed files with 666 additions and 300 deletions

4
changelog/58969.fixed.md Normal file
View file

@ -0,0 +1,4 @@
Issue 58969: Fixes an issue with `saltclass.expand_classes_in_order`
function where it was losing nested classes states during class
expansion. The logic now use `salt.utils.odict.OrderedDict` to keep
the inclusion ordering.

4
changelog/63933.fixed.md Normal file
View file

@ -0,0 +1,4 @@
Issue 63933: Fixes an issue with `saltclass.expanded_dict_from_minion`
function where it was passing a reference to minion `dict` which was
overridden by nested classes during class expansion. Copy the node
definition with `copy.deepcopy` instead of passing a reference.

1
changelog/66252.fixed.md Normal file
View file

@ -0,0 +1 @@
Applying `selinux.fcontext_policy_present` to a shorter path than an existing entry now works

1
changelog/66883.fixed.md Normal file
View file

@ -0,0 +1 @@
Added cryptogrpahy back to base.txt requirements as a dependency

1
changelog/66891.fixed.md Normal file
View file

@ -0,0 +1 @@
Remove "perms" from `linux_acl.list_absent()` documentation

2
changelog/66959.fixed.md Normal file
View file

@ -0,0 +1,2 @@
Removed the usage of wmic to get the disk and iscsi grains for Windows. The wmic
binary is being deprecated.

1
changelog/66996.fixed.md Normal file
View file

@ -0,0 +1 @@
Ensured global dunders like __env__ are defined in state module that are run in parallel on spawning platforms

1
changelog/66999.fixed.md Normal file
View file

@ -0,0 +1 @@
Filtered unpicklable objects from the context dict when invoking states in parallel on spawning platforms to avoid a crash

View file

@ -10,7 +10,8 @@ requests>=2.32.3 ; python_version >= '3.10'
certifi==2023.07.22; python_version < '3.10'
certifi>=2024.7.4; python_version >= '3.10'
distro>=1.0.1
psutil>=5.0.0
psutil<6.0.0; python_version <= '3.9'
psutil>=5.0.0; python_version >= '3.10'
packaging>=21.3
looseversion
tornado>=6.3.3

View file

@ -15,3 +15,5 @@ pyfakefs
trustme
pytest-skip-markers >= 1.5.2 ; python_version >= '3.8'
pytest-skip-markers <= 1.5.1 ; python_version < '3.8'
pytest-shell-utilities <= 1.9.0; python_version <= '3.9'
pytest-shell-utilities >= 1.9.7; python_version >= '3.10'

View file

@ -310,7 +310,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.10/darwin.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.10/darwin.txt
# -r requirements/base.txt
@ -373,8 +373,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -534,7 +536,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -314,7 +314,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.10/freebsd.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.10/freebsd.txt
# -r requirements/base.txt
@ -377,8 +377,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -539,7 +541,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -340,7 +340,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.10/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.10/linux.txt
# -r requirements/base.txt
@ -411,8 +411,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -603,7 +605,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -272,7 +272,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# -r requirements/base.txt
@ -335,8 +335,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -486,7 +488,6 @@ typing-extensions==4.8.0
# inflect
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -303,7 +303,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.11/darwin.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.11/darwin.txt
# -r requirements/base.txt
@ -366,8 +366,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -525,7 +527,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -307,7 +307,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.11/freebsd.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.11/freebsd.txt
# -r requirements/base.txt
@ -370,8 +370,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -531,7 +533,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -331,7 +331,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.11/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.11/linux.txt
# -r requirements/base.txt
@ -402,8 +402,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -593,7 +595,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -265,7 +265,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# -r requirements/base.txt
@ -328,8 +328,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -477,7 +479,6 @@ typing-extensions==4.8.0
# inflect
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -427,7 +427,7 @@ portend==3.1.0
# cherrypy
profitbricks==4.1.3
# via -r requirements/static/ci/cloud.in
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -c requirements/static/ci/py3.12/linux.txt
@ -525,9 +525,10 @@ pytest-salt-factories==1.0.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
@ -758,7 +759,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -303,7 +303,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.12/darwin.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/darwin.txt
# -r requirements/base.txt
@ -366,8 +366,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -525,7 +527,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -168,7 +168,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/py3.12/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/base.txt

View file

@ -307,7 +307,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.12/freebsd.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/freebsd.txt
# -r requirements/base.txt
@ -370,8 +370,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -531,7 +533,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -453,7 +453,7 @@ portend==3.1.0
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -c requirements/static/ci/py3.12/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -c requirements/static/ci/py3.12/linux.txt

View file

@ -331,7 +331,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -r requirements/base.txt
@ -402,8 +402,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -593,7 +595,6 @@ typing-extensions==4.8.0
# napalm
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -265,7 +265,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.12/windows.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via
# -c requirements/static/ci/../pkg/py3.12/windows.txt
# -r requirements/base.txt
@ -328,8 +328,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.9.7 ; python_version >= "3.10"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt
@ -477,7 +479,6 @@ typing-extensions==4.8.0
# inflect
# pydantic
# pydantic-core
# pytest-shell-utilities
# pytest-system-statistics
urllib3==1.26.18
# via

View file

@ -318,7 +318,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.8/freebsd.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.8/freebsd.txt
# -r requirements/base.txt
@ -381,8 +381,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -337,7 +337,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.8/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.8/linux.txt
# -r requirements/base.txt
@ -408,8 +408,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -276,7 +276,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# -r requirements/base.txt
@ -339,8 +339,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -310,7 +310,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.9/darwin.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.9/darwin.txt
# -r requirements/base.txt
@ -373,8 +373,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -314,7 +314,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.9/freebsd.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.9/freebsd.txt
# -r requirements/base.txt
@ -377,8 +377,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -333,7 +333,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.9/linux.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.9/linux.txt
# -r requirements/base.txt
@ -404,8 +404,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -272,7 +272,7 @@ portend==3.1.0
# via
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# -r requirements/base.txt
@ -335,8 +335,10 @@ pytest-httpserver==1.0.8
# via -r requirements/pytest.txt
pytest-salt-factories==1.0.1
# via -r requirements/pytest.txt
pytest-shell-utilities==1.8.0
# via pytest-salt-factories
pytest-shell-utilities==1.8.0 ; python_version <= "3.9"
# via
# -r requirements/pytest.txt
# pytest-salt-factories
pytest-skip-markers==1.5.2 ; python_version >= "3.8"
# via
# -r requirements/pytest.txt

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -97,7 +97,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -95,7 +95,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -87,7 +87,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -95,7 +95,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version >= "3.10"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -91,7 +91,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -91,7 +91,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -99,7 +99,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -89,7 +89,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -97,7 +97,7 @@ packaging==23.1
# via -r requirements/base.txt
portend==3.1.0
# via cherrypy
psutil==5.9.6
psutil==5.9.6 ; python_version <= "3.9"
# via -r requirements/base.txt
pycparser==2.21
# via cffi

View file

@ -16,6 +16,7 @@ import salt.utils.platform
__salt__ = {
"cmd.run": salt.modules.cmdmod._run_quiet,
"cmd.run_all": salt.modules.cmdmod._run_all_quiet,
"cmd.powershell": salt.modules.cmdmod.powershell,
}
log = logging.getLogger(__name__)
@ -153,41 +154,28 @@ def _linux_disks():
def _windows_disks():
wmic = salt.utils.path.which("wmic")
namespace = r"\\root\microsoft\windows\storage"
path = "MSFT_PhysicalDisk"
get = "DeviceID,MediaType"
cmd = "Get-PhysicalDisk | Select DeviceID, MediaType"
ret = {"disks": [], "ssds": []}
cmdret = __salt__["cmd.run_all"](
"{} /namespace:{} path {} get {} /format:table".format(
wmic, namespace, path, get
)
)
drive_info = __salt__["cmd.powershell"](cmd)
if cmdret["retcode"] != 0:
log.trace("Disk grain does not support this version of Windows")
else:
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 = rf"\\.\PhysicalDrive{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)
else:
log.trace("Device %s reports itself as Unspecified", device)
ret["disks"].append(device)
if not drive_info:
log.trace("No physical discs found")
return ret
# We need a list of dict
if isinstance(drive_info, dict):
drive_info = [drive_info]
for drive in drive_info:
# Make sure we have a valid drive type
if drive["MediaType"].lower() not in ["hdd", "ssd", "scm", "unspecified"]:
log.trace(f'Unknown media type: {drive["MediaType"]}')
continue
device = rf'\\.\PhysicalDrive{drive["DeviceID"]}'
ret["disks"].append(device)
if drive["MediaType"].lower() == "ssd":
ret["ssds"].append(device)
return ret

View file

@ -85,28 +85,25 @@ def _aix_iqn():
def _windows_iqn():
"""
Return iSCSI IQN from a Windows host.
Return iSCSI nodes from a Windows host.
"""
cmd = "Get-InitiatorPort | Select NodeAddress"
ret = []
wmic = salt.utils.path.which("wmic")
nodes = salt.modules.cmdmod.powershell(cmd)
if not wmic:
if not nodes:
log.trace("No iSCSI nodes found")
return ret
namespace = r"\\root\WMI"
path = "MSiSCSIInitiator_MethodClass"
get = "iSCSINodeName"
# A single node will return a dictionary with a single entry
# {"NodeAddress": "iqn.1991-05.com.microsoft:johnj99-pc2.contoso.com"}
# Multiple nodes will return a list of single entry dicts
# We need a list of dict
if isinstance(nodes, dict):
nodes = [nodes]
cmd_ret = salt.modules.cmdmod.run_all(
"{} /namespace:{} path {} get {} /format:table".format(
wmic, namespace, path, get
)
)
for line in cmd_ret["stdout"].splitlines():
if line.startswith("iqn."):
line = line.rstrip()
ret.append(line.rstrip())
for node in nodes:
ret.append(node["NodeAddress"])
return ret

View file

@ -266,7 +266,7 @@ def _prep_powershell_cmd(win_shell, cmd, encoded_cmd):
win_shell = salt.utils.path.which(win_shell)
if not win_shell:
raise CommandExecutionError("PowerShell binary not found")
raise CommandExecutionError(f"PowerShell binary not found: {win_shell}")
new_cmd = [win_shell, "-NonInteractive", "-NoProfile", "-ExecutionPolicy", "Bypass"]

View file

@ -490,7 +490,7 @@ def fcontext_get_policy(
"[[:alpha:] ]+" if filetype is None else filetype_id_to_string(filetype)
)
cmd = (
"semanage fcontext -l | egrep "
"semanage fcontext -l | grep -E "
+ "'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}{ospacer}$'".format(
**cmd_kwargs
)
@ -616,7 +616,7 @@ def _fcontext_add_or_delete_policy(
if "add" == action:
# need to use --modify if context for name file exists, otherwise ValueError
filespec = re.escape(name)
cmd = f"semanage fcontext -l | egrep '{filespec}'"
cmd = f"semanage fcontext -l | grep -E '{filespec} '"
current_entry_text = __salt__["cmd.shell"](cmd, ignore_retcode=True)
if current_entry_text != "":
action = "modify"
@ -762,7 +762,7 @@ def port_get_policy(name, sel_type=None, protocol=None, port=None):
"port": port,
}
cmd = (
"semanage port -l | egrep "
"semanage port -l | grep -E "
+ "'^{sel_type}{spacer}{protocol}{spacer}((.*)*)[ ]{port}($|,)'".format(
**cmd_kwargs
)

View file

@ -318,7 +318,8 @@ def _get_options(**kwargs):
ret.append(f"--branch={branch}")
for item in setopt:
ret.extend(["--setopt", str(item)])
log.info("Adding configuration option '%s'", item)
ret.extend([f"--setopt={item}"])
if get_extra_options:
# sorting here to make order uniform, makes unit testing more reliable

View file

@ -18,6 +18,7 @@ import importlib
import inspect
import logging
import os
import pickle
import random
import re
import site
@ -757,21 +758,21 @@ class State:
loader="states",
initial_pillar=None,
file_client=None,
__invocation_id=None,
_invocation_id=None,
):
"""
When instantiating an object of this class, do not pass
``__invocation_id``. It is an internal field for tracking
``_invocation_id``. It is an internal field for tracking
parallel executions where no jid is available (Salt-SSH) and
only exposed as an init argument to work on spawning platforms.
"""
if jid is not None:
__invocation_id = jid
if __invocation_id is None:
_invocation_id = jid
if _invocation_id is None:
# For salt-ssh parallel states, we need a unique identifier
# for a single execution. self.jid should not be set there
# since it's used for other purposes as well.
__invocation_id = salt.utils.jid.gen_jid(opts)
_invocation_id = salt.utils.jid.gen_jid(opts)
self._init_kwargs = {
"opts": opts,
"pillar_override": pillar_override,
@ -782,7 +783,7 @@ class State:
"mocked": mocked,
"loader": loader,
"initial_pillar": initial_pillar,
"__invocation_id": __invocation_id,
"_invocation_id": _invocation_id,
}
self.states_loader = loader
if "grains" not in opts:
@ -829,7 +830,7 @@ class State:
self.pre = {}
self.__run_num = 0
self.jid = jid
self.invocation_id = __invocation_id
self.invocation_id = _invocation_id
self.instance_id = str(id(self))
self.inject_globals = {}
self.mocked = mocked
@ -2157,12 +2158,15 @@ class State:
return req_in_high, errors
@classmethod
def _call_parallel_target(cls, instance, init_kwargs, name, cdata, low):
def _call_parallel_target(
cls, instance, init_kwargs, name, cdata, low, inject_globals
):
"""
The target function to call that will create the parallel thread/process
"""
if instance is None:
instance = cls(**init_kwargs)
instance.states.inject_globals = inject_globals
# 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()
@ -2267,7 +2271,7 @@ class State:
with salt.utils.files.fopen(tfile, "wb+") as fp_:
fp_.write(msgpack_serialize(ret))
def call_parallel(self, cdata, low):
def call_parallel(self, cdata, low, inject_globals):
"""
Call the state defined in the given cdata in parallel
"""
@ -2284,13 +2288,37 @@ class State:
instance = None
else:
instance = self
inject_globals = None
proc = salt.utils.process.Process(
target=self._call_parallel_target,
args=(instance, self._init_kwargs, name, cdata, low),
args=(instance, self._init_kwargs, name, cdata, low, inject_globals),
name=f"ParallelState({name})",
)
proc.start()
try:
proc.start()
except TypeError as err:
# Some modules use the context to cache unpicklable objects like
# database connections or loader instances.
# Ensure we don't crash because of that on spawning platforms.
if "cannot pickle" not in str(err):
raise
clean_context = {}
for var, val in self._init_kwargs["context"].items():
try:
pickle.dumps(val)
except TypeError:
pass
else:
clean_context[var] = val
init_kwargs = self._init_kwargs.copy()
init_kwargs["context"] = clean_context
proc = salt.utils.process.Process(
target=self._call_parallel_target,
args=(instance, init_kwargs, name, cdata, low, inject_globals),
name=f"ParallelState({name})",
)
proc.start()
ret = {
"name": name,
"result": None,
@ -2434,7 +2462,7 @@ class State:
)
elif 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)
ret = self.call_parallel(cdata, low, inject_globals)
else:
self.format_slots(cdata)
with salt.utils.files.set_umask(low.get("__umask__")):

View file

@ -708,9 +708,6 @@ def list_absent(name, acl_type, acl_names=None, recurse=False):
acl_names
The list of users or groups
perms
Remove the permissions eg.: rwx
recurse
Set the permissions recursive in the path

View file

@ -203,7 +203,7 @@ def which(exe=None):
# now to search through our system_path
for path in system_path:
p = join(path, exe)
p = join(os.path.expandvars(path), exe)
# iterate through all extensions to see which one is executable
for ext in pathext:

View file

@ -1,3 +1,4 @@
import copy
import glob
import logging
import os
@ -5,6 +6,7 @@ import re
from jinja2 import Environment, FileSystemLoader
import salt.utils.odict
import salt.utils.path
import salt.utils.yaml
@ -277,9 +279,27 @@ def expand_classes_glob(classes, salt_data):
return expanded_classes
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, classes_to_expand):
"""
Expand the list of `classes_to_expand` and return them in the order found
The return order is `[C, B, A, M, B, L, MINION_ID]` when:
- minion node include classes `A` and `L`
- `A` include class `B`
- `B` include class `C`
- `L` include class `M` and `B`
:param dict minion_dict: definition of minion node
:param dict salt_data: configuration data
:param iterable(str) seen_classes: classes already processed
:param iterable(str) classes_to_expand: classes to recursivly expand
:return: Expanded classes in proper order
:rtype: salt.utils.odict.OrderedDict
"""
expanded_classes = salt.utils.odict.OrderedDict()
# Get classes to expand from minion dictionary
if not classes_to_expand and "classes" in minion_dict:
classes_to_expand = minion_dict["classes"]
@ -290,71 +310,37 @@ def expand_classes_in_order(
for klass in classes_to_expand:
if klass not in seen_classes:
seen_classes.append(klass)
expanded_classes[klass] = get_class(klass, salt_data)
klass_dict = salt.utils.odict.OrderedDict(
{klass: get_class(klass, salt_data)}
)
# Fix corner case where class is loaded but doesn't contain anything
if expanded_classes[klass] is None:
expanded_classes[klass] = {}
if klass_dict[klass] is None:
klass_dict[klass] = {}
# Merge newly found pillars into existing ones
new_pillars = expanded_classes[klass].get("pillars", {})
new_pillars = klass_dict[klass].get("pillars", {})
if 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"):
l_id = classes_to_expand.index(klass)
classes_to_expand[l_id:l_id] = expanded_classes[klass]["classes"]
expand_classes_in_order(
minion_dict,
if "classes" in klass_dict[klass]:
nested_classes = expand_classes_in_order(
{},
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,
klass_dict[klass].get("classes", {}),
)
# We may have duplicates here and we want to remove them
tmp = []
for t_element in classes_to_expand:
if t_element not in tmp:
tmp.append(t_element)
# Put current class after nested classes
klass_dict.update(nested_classes)
klass_dict.move_to_end(klass)
classes_to_expand = tmp
expanded_classes.update(klass_dict)
# Now that we've retrieved every class in order,
# let's return an ordered list of dicts
ord_expanded_classes = []
ord_expanded_states = []
for ord_klass in classes_to_expand:
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"] = {}
# Minion dict must be at the end
if minion_dict:
expanded_classes.update({salt_data["minion_id"]: minion_dict})
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:
ord_expanded_states.extend(minion_dict["states"])
ord_expanded_classes.append(minion_dict)
return ord_expanded_classes, classes_to_expand, ord_expanded_states
return expanded_classes
def expanded_dict_from_minion(minion_id, salt_data):
@ -377,22 +363,35 @@ def expanded_dict_from_minion(minion_id, salt_data):
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__"], copy.deepcopy(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, [], {}, []
)
expanded_classes = expand_classes_in_order(node_dict[minion_id], salt_data, [], [])
# Here merge the pillars together
pillars_dict = {}
for exp_dict in expanded_classes:
states_list = []
classes_list = list(expanded_classes.keys())[:-1]
classes_values = list(expanded_classes.values())
for exp_dict in classes_values:
if "pillars" in exp_dict:
dict_merge(pillars_dict, exp_dict)
if "states" in exp_dict:
states_list.extend(exp_dict["states"])
return expanded_classes, pillars_dict, classes_list, states_list
# Avoid duplicates, keep first
state_seen = set()
states_list = [
state
for state in states_list
if not (state in state_seen or state_seen.add(state))
]
return classes_values, pillars_dict, classes_list, states_list
def get_pillars(minion_id, salt_data):

View file

@ -3,6 +3,7 @@ import os
import textwrap
import threading
import time
from textwrap import dedent
import pytest
@ -1081,3 +1082,97 @@ def test_state_sls_mock_ret(state_tree):
ret["cmd_|-echo1_|-echo 'This is a test!'_|-run"]["comment"]
== "Not called, mocked"
)
@pytest.fixture
def _state_requires_env(loaders, state_tree):
mod_contents = dedent(
r"""
def test_it(name):
return {
"name": name,
"result": __env__ == "base",
"comment": "",
"changes": {},
}
"""
)
sls = "test_spawning"
sls_contents = dedent(
"""
This should not fail on spawning platforms:
requires_env.test_it:
- name: foo
- parallel: true
"""
)
with pytest.helpers.temp_file(
f"{sls}.sls", sls_contents, state_tree
), pytest.helpers.temp_file("_states/requires_env.py", mod_contents, state_tree):
res = loaders.modules.saltutil.sync_states()
assert "states.requires_env" in res
yield sls
def test_state_apply_parallel_spawning_with_global_dunders(state, _state_requires_env):
"""
Ensure state modules called via `parallel: true` have access to injected
global dunders like `__env__`.
"""
ret = state.apply(_state_requires_env)
assert (
ret[
"requires_env_|-This should not fail on spawning platforms_|-foo_|-test_it"
]["result"]
is True
)
@pytest.fixture
def _state_unpicklable_ctx(loaders, state_tree):
mod_contents = dedent(
r"""
import threading
class Unpicklable:
def __init__(self):
self._lock = threading.RLock()
def test_it():
__context__["booh"] = Unpicklable()
"""
)
sls = "test_spawning_unpicklable"
sls_contents = dedent(
r"""
{%- do salt["unpicklable.test_it"]() %}
This should not fail on spawning platforms:
test.nop:
- name: foo
- parallel: true
"""
)
with pytest.helpers.temp_file(
f"{sls}.sls", sls_contents, state_tree
), pytest.helpers.temp_file("_modules/unpicklable.py", mod_contents, state_tree):
res = loaders.modules.saltutil.sync_modules()
assert "modules.unpicklable" in res
yield sls
@pytest.mark.skip_unless_on_spawning_platform(
reason="Pickling is only relevant on spawning platforms"
)
def test_state_apply_parallel_spawning_with_unpicklable_context(
state, _state_unpicklable_ctx
):
"""
Ensure that if the __context__ dictionary contains unpicklable objects,
they are filtered out instead of causing a crash.
"""
ret = state.apply(_state_unpicklable_ctx)
assert (
ret["test_|-This should not fail on spawning platforms_|-foo_|-nop"]["result"]
is True
)

View file

@ -2,8 +2,6 @@
:codeauthor: :email:`Shane Lee <slee@saltstack.com>`
"""
import textwrap
import pytest
import salt.grains.disks as disks
@ -17,63 +15,58 @@ def configure_loader_modules():
}
def test__windows_disks():
def test__windows_disks_dict():
"""
Test grains._windows_disks, normal return
Should return a populated dictionary
Test grains._windows_disks with a single disk returned as a dict
Should return 1 disk and no ssds
"""
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})
devices = {"DeviceID": 0, "MediaType": "HDD"}
mock_powershell = MagicMock(return_value=devices)
with patch("salt.utils.path.which", mock_which), patch.dict(
disks.__salt__, {"cmd.run_all": mock_run_all}
):
with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}):
result = disks._windows_disks()
expected = {"disks": ["\\\\.\\PhysicalDrive0"], "ssds": []}
assert result == expected
def test__windows_disks_list():
"""
test grains._windows_disks with multiple disks and types as a list of dicts
Should return 4 disks and 1 ssd
"""
devices = [
{"DeviceID": 0, "MediaType": "SSD"},
{"DeviceID": 1, "MediaType": "HDD"},
{"DeviceID": 2, "MediaType": "HDD"},
{"DeviceID": 3, "MediaType": "HDD"},
]
mock_powershell = MagicMock(return_value=devices)
with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}):
result = disks._windows_disks()
expected = {
"ssds": ["\\\\.\\PhysicalDrive0"],
"disks": [
"\\\\.\\PhysicalDrive0",
"\\\\.\\PhysicalDrive1",
"\\\\.\\PhysicalDrive2",
"\\\\.\\PhysicalDrive3",
],
"ssds": ["\\\\.\\PhysicalDrive0"],
}
assert result == expected
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():
def test__windows_disks_empty():
"""
Test grains._windows_disks, retcode 1
Test grains._windows_disks when nothing is returned
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}
):
devices = {}
mock_powershell = MagicMock(return_value=devices)
with patch.dict(disks.__salt__, {"cmd.powershell": mock_powershell}):
expected = {"disks": [], "ssds": []}
result = disks._windows_disks()
expected = {"ssds": [], "disks": []}
assert result == expected

View file

@ -9,16 +9,39 @@ import salt.grains.iscsi as iscsi
from tests.support.mock import MagicMock, mock_open, patch
def test_windows_iscsi_iqn_grains():
cmd_run_mock = MagicMock(
return_value={"stdout": "iSCSINodeName\niqn.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()
def test_windows_iscsi_iqn_grains_empty():
nodes_dict = {}
cmd_powershell_mock = MagicMock(return_value=nodes_dict)
with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock):
result = iscsi._windows_iqn()
expected = []
assert result == expected
assert _grains.get("iscsi_iqn") == ["iqn.1991-05.com.microsoft:simon-x1"]
def test_windows_iscsi_iqn_grains_single():
nodes_dict = {"NodeAddress": "iqn.1991-05.com.microsoft:simon-x1"}
cmd_powershell_mock = MagicMock(return_value=nodes_dict)
with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock):
result = iscsi._windows_iqn()
expected = ["iqn.1991-05.com.microsoft:simon-x1"]
assert result == expected
def test_windows_iscsi_iqn_grains_multiple():
nodes_list = [
{"NodeAddress": "iqn.1991-05.com.microsoft:simon-x1"},
{"NodeAddress": "iqn.1991-05.com.microsoft:simon-x2"},
{"NodeAddress": "iqn.1991-05.com.microsoft:simon-x3"},
]
cmd_powershell_mock = MagicMock(return_value=nodes_list)
with patch("salt.modules.cmdmod.powershell", cmd_powershell_mock):
result = iscsi._windows_iqn()
expected = [
"iqn.1991-05.com.microsoft:simon-x1",
"iqn.1991-05.com.microsoft:simon-x2",
"iqn.1991-05.com.microsoft:simon-x3",
]
assert result == expected
def test_aix_iscsi_iqn_grains():

View file

@ -6,6 +6,8 @@ import salt.modules.selinux as selinux
from salt.exceptions import SaltInvocationError
from tests.support.mock import MagicMock, mock_open, patch
pytestmark = [pytest.mark.skip_unless_on_linux]
@pytest.fixture
def configure_loader_modules():
@ -399,7 +401,39 @@ def test_selinux_add_policy_regex(name, sel_type):
):
selinux.fcontext_add_policy(name, sel_type=sel_type)
filespec = re.escape(name)
expected_cmd_shell = f"semanage fcontext -l | egrep '{filespec}'"
expected_cmd_shell = f"semanage fcontext -l | grep -E '{filespec} '"
mock_cmd_shell.assert_called_once_with(
expected_cmd_shell,
ignore_retcode=True,
)
expected_cmd_run_all = (
f"semanage fcontext --modify --type {sel_type} {filespec}"
)
mock_cmd_run_all.assert_called_once_with(
expected_cmd_run_all,
)
@pytest.mark.parametrize(
"name,sel_type",
(
("/usr/share/munin/plugins/mysql_queries", "services_munin_plugin_exec_t"),
("/usr/share/munin/plugins/mysql_", "unconfined_munin_plugin_exec_t"),
),
)
def test_selinux_add_policy_shorter_path(name, sel_type):
"""
Test adding policy with a shorter path than an existing entry
"""
mock_cmd_shell = MagicMock(return_value={"retcode": 0})
mock_cmd_run_all = MagicMock(return_value={"retcode": 0})
with patch.dict(selinux.__salt__, {"cmd.shell": mock_cmd_shell}), patch.dict(
selinux.__salt__, {"cmd.run_all": mock_cmd_run_all}
):
selinux.fcontext_add_policy(name, sel_type=sel_type)
filespec = re.escape(name)
expected_cmd_shell = f"semanage fcontext -l | grep -E '{filespec} '"
mock_cmd_shell.assert_called_once_with(
expected_cmd_shell,
ignore_retcode=True,

View file

@ -1428,10 +1428,8 @@ def test_install_with_options():
"--disablerepo=*",
"--enablerepo=good",
"--branch=foo",
"--setopt",
"obsoletes=0",
"--setopt",
"plugins=0",
"--setopt=obsoletes=0",
"--setopt=plugins=0",
"install",
"foo",
],
@ -1459,10 +1457,8 @@ def test_install_with_options():
"--disablerepo=bad",
"--enablerepo=good",
"--branch=foo",
"--setopt",
"obsoletes=0",
"--setopt",
"plugins=0",
"--setopt=obsoletes=0",
"--setopt=plugins=0",
"install",
"foo",
],
@ -1857,10 +1853,8 @@ def test_upgrade_with_options():
"--disablerepo=*",
"--enablerepo=good",
"--branch=foo",
"--setopt",
"obsoletes=0",
"--setopt",
"plugins=0",
"--setopt=obsoletes=0",
"--setopt=plugins=0",
"--exclude=kernel*",
"--nogpgcheck",
"upgrade",
@ -1902,10 +1896,8 @@ def test_upgrade_with_options():
"--disablerepo=bad",
"--enablerepo=good",
"--branch=foo",
"--setopt",
"obsoletes=0",
"--setopt",
"plugins=0",
"--setopt=obsoletes=0",
"--setopt=plugins=0",
"--exclude=kernel*",
"upgrade",
],
@ -3051,10 +3043,8 @@ def test_pkg_update_dnf():
"dnf",
"--quiet",
"-y",
"--setopt",
"plugins=0",
"--setopt",
"obsoletes=False",
"--setopt=plugins=0",
"--setopt=obsoletes=False",
"upgrade",
"foo",
],

View file

@ -23,6 +23,8 @@ def temp_saltclass_tree(tmp_path, minion_id):
nodes_dir.mkdir(parents=True, exist_ok=True)
default_dir = classes_dir / "default"
default_dir.mkdir(parents=True, exist_ok=True)
users_dir = default_dir / "users"
users_dir.mkdir(parents=True, exist_ok=True)
roles_dir = classes_dir / "roles"
roles_dir.mkdir(parents=True, exist_ok=True)
nginx_subdir = roles_dir / "nginx"
@ -35,6 +37,9 @@ def temp_saltclass_tree(tmp_path, minion_id):
- default.motd
- default.empty
states:
- default
pillars:
default:
network:
@ -50,6 +55,13 @@ def temp_saltclass_tree(tmp_path, minion_id):
test_list:
- a: ${default:network:ntp:srv1}
- ${default:network:ntp:srv2}
global_scalar: from default
test_dict:
a_scalar: from default
a_list:
- element1
- element2
"""
default_init.write_text(test_list)
@ -57,24 +69,91 @@ def temp_saltclass_tree(tmp_path, minion_id):
nodes_text = """
environment: base
states:
- minion_node
classes:
{% for class in ['default', 'roles.*', 'empty.*'] %}
- {{ class }}
{% endfor %}
pillars:
global_scalar: from minion_node
test_dict:
a_scalar: from minion_node
"""
minion_node_file.write_text(nodes_text)
(default_dir / "users.yml").write_text("test: this is a test")
(users_dir / "init.yml").write_text(
"""
classes:
- default.users.foo
states:
- users
pillars:
default:
ntp:
srv1: 192.168.20.10
global_scalar: from users
test_dict:
a_scalar: from users
"""
)
(users_dir / "foo.yml").write_text(
"""
states:
- users.foo
global_scalar: from users.foo
users_foo_scalar: from users.foo
test_dict:
a_scalar: from users.foo
"""
)
(default_dir / "empty.yml").write_text("test: this is a test")
(default_dir / "motd.yml").write_text("test: this is a test")
(roles_dir / "app.yml").write_text("test: this is a test")
(nginx_subdir / "init.yml").write_text("test: this is a test")
(default_dir / "motd.yml").write_text(
"""
states:
- motd
pillars:
global_scalar: from motd
"""
)
(roles_dir / "app.yml").write_text(
"""
states:
- app
pillars:
global_scalar: from app
"""
)
(nginx_subdir / "init.yml").write_text(
"""
states:
- nginx
pillars:
global_scalar: from nginx
nginx_scalar: from nginx
"""
)
return dirname
def test_succeeds(temp_saltclass_tree):
def test_classes_order(temp_saltclass_tree):
"""
Classes must be correctly ordered.
See: https://github.com/saltstack/salt/issues/58969
"""
expected_ret = [
"default.users.foo",
"default.users",
"default.motd",
"default.empty",
@ -110,3 +189,102 @@ def test_list_expansion_succeeds(temp_saltclass_tree):
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret
def test_node_override_classes_scalars(temp_saltclass_tree):
"""
Scalars pillars defined in a node definition must override the
definition from classes.
"""
expected_ret = "from minion_node"
fake_args = {"path": str(temp_saltclass_tree)}
fake_pillar = {}
fake_minion_id = "fake_id"
try:
full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args)
parsed_ret = full_ret["global_scalar"]
# Fail the test if we hit our NoneType error
except TypeError as err:
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret
def test_node_override_classes_scalar_in_dict(temp_saltclass_tree):
"""
Scalars defined in `dict` pillars defined in a node definition must override the
same dict definition from classes.
See: https://github.com/saltstack/salt/issues/63933
"""
expected_ret = "from minion_node"
fake_args = {"path": str(temp_saltclass_tree)}
fake_pillar = {}
fake_minion_id = "fake_id"
try:
full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args)
parsed_ret = full_ret["test_dict"]["a_scalar"]
# Fail the test if we hit our NoneType error
except TypeError as err:
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret
def test_node_override_classes_list_in_dict(temp_saltclass_tree):
"""
`list` under a `dict` defined in a node definition must override the
same definition from classes.
See: https://github.com/saltstack/salt/issues/63933
"""
expected_ret = {"srv1": "192.168.10.10", "srv2": "192.168.10.20"}
fake_args = {"path": str(temp_saltclass_tree)}
fake_pillar = {}
fake_minion_id = "fake_id"
try:
full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args)
parsed_ret = full_ret["default"]["network"]["ntp"]
# Fail the test if we hit our NoneType error
except TypeError as err:
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret
def test_list_in_dict_no_duplication(temp_saltclass_tree):
"""
`list` under a `dict` in pillar must not be duplicated.
See:
"""
expected_ret = ["element1", "element2"]
fake_args = {"path": str(temp_saltclass_tree)}
fake_pillar = {}
fake_minion_id = "fake_id"
try:
full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args)
parsed_ret = full_ret["test_dict"]["a_list"]
# Fail the test if we hit our NoneType error
except TypeError as err:
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret
def test_nested_classes_has_pillars(temp_saltclass_tree):
"""
pillars defined in nested classes are present
"""
expected_ret = "from nginx"
fake_args = {"path": str(temp_saltclass_tree)}
fake_pillar = {}
fake_minion_id = "fake_id"
try:
full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args)
parsed_ret = full_ret["nginx_scalar"]
# Fail the test if we hit our NoneType error
except TypeError as err:
pytest.fail(err)
# Else give the parsed content result
assert expected_ret == parsed_ret

View file

@ -345,7 +345,6 @@ MISSING_DOCSTRINGS = {
"get_pillars",
"expand_variables",
"render_jinja",
"expand_classes_in_order",
"dict_search_and_replace",
"expanded_dict_from_minion",
"find_value_to_expand",