mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
Merge 3006.x into 3007.x
This commit is contained in:
commit
f7570047bd
54 changed files with 1466 additions and 299 deletions
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -1,7 +1,7 @@
|
|||
### What does this PR do?
|
||||
|
||||
### What issues does this PR fix or reference?
|
||||
Fixes:
|
||||
Fixes
|
||||
|
||||
### Previous Behavior
|
||||
Remove this section if not relevant
|
||||
|
|
1
changelog/61807.fixed.md
Normal file
1
changelog/61807.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
pkg.refresh_db on Windows now honors saltenv
|
1
changelog/63667.fixed.md
Normal file
1
changelog/63667.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
Fix user and group management on Windows to handle the Everyone group
|
2
changelog/63848.fixed.md
Normal file
2
changelog/63848.fixed.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fixes an issue in pkg.refresh_db on Windows where new package definition
|
||||
files were not being picked up on the first run
|
1
changelog/64933.fixed.md
Normal file
1
changelog/64933.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
Display a proper error when pki commands fail in the win_pki module
|
7
changelog/65611.fixed.md
Normal file
7
changelog/65611.fixed.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
When using s3fs, if files are deleted from the bucket, they were not deleted in
|
||||
the master or minion local cache, which could lead to unexpected file copies or
|
||||
even state applications. This change makes the local cache consistent with the
|
||||
remote bucket by deleting files locally that are deleted from the bucket.
|
||||
|
||||
**NOTE** this could lead to **breakage** on your affected systems if it was
|
||||
inadvertently depending on previously deleted files.
|
2
changelog/66049.fixed.md
Normal file
2
changelog/66049.fixed.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
Fixed an issue with file.directory state where paths would be modified in test
|
||||
mode if backupname is used.
|
1
changelog/66705.fixed.md
Normal file
1
changelog/66705.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
backport the fix from #66164 to fix #65703. use OrderedDict to fix bad indexing.
|
|
@ -397,16 +397,17 @@ winrepo_source_dir
|
|||
|
||||
:conf_minion:`winrepo_source_dir` (str)
|
||||
|
||||
The location of the .sls files on the Salt file server. This allows for using
|
||||
different environments. Default is ``salt://win/repo-ng/``\.
|
||||
The location of the .sls files on the Salt file server. Default is
|
||||
``salt://win/repo-ng/``.
|
||||
|
||||
.. warning::
|
||||
If the default for ``winrepo_dir_ng`` is changed, this setting may need to
|
||||
be changed on each minion. The default setting for ``winrepo_dir_ng`` is
|
||||
``/srv/salt/win/repo-ng``\. If that were changed to
|
||||
``/srv/salt/new/repo-ng``\, then the ``winrepo_source_dir`` would need to be
|
||||
If the default for ``winrepo_dir_ng`` is changed, then this setting will
|
||||
also need to be changed on each minion. The default setting for
|
||||
``winrepo_dir_ng`` is ``/srv/salt/win/repo-ng``. If that were changed to
|
||||
``/srv/salt/new/repo-ng`` then the ``winrepo_source_dir`` would need to be
|
||||
changed to ``salt://new/repo-ng``
|
||||
|
||||
|
||||
.. _masterless-minion-config:
|
||||
|
||||
Masterless Minion Configuration
|
||||
|
@ -430,7 +431,7 @@ winrepo_dir
|
|||
|
||||
This setting is maintained for backwards compatibility with legacy minions. It
|
||||
points to the location in the ``file_roots`` where the winrepo files are kept.
|
||||
The default is: ``C:\salt\srv\salt\win\repo``
|
||||
The default is: ``C:\ProgramData\Salt Project\Salt\srv\salt\win\repo``
|
||||
|
||||
winrepo_dir_ng
|
||||
--------------
|
||||
|
@ -438,7 +439,7 @@ winrepo_dir_ng
|
|||
:conf_minion:`winrepo_dir_ng` (str)
|
||||
|
||||
The location in the ``file_roots`` where the winrepo files are kept. The default
|
||||
is ``C:\salt\srv\salt\win\repo-ng``\.
|
||||
is ``C:\ProgramData\Salt Project\Salt\srv\salt\win\repo-ng``.
|
||||
|
||||
.. warning::
|
||||
You can change the location of the winrepo directory. However, it must
|
||||
|
@ -483,6 +484,137 @@ default is a list containing a single URL:
|
|||
|
||||
.. _usage:
|
||||
|
||||
|
||||
Sample Configurations
|
||||
*********************
|
||||
|
||||
Masterless
|
||||
==========
|
||||
|
||||
The configs in this section are for working with winrepo on a Windows minion
|
||||
using ``salt-call --local``.
|
||||
|
||||
Default Configuration
|
||||
---------------------
|
||||
|
||||
This is the default configuration if nothing is configured in the minion config.
|
||||
The config is shown here for clarity. These are the defaults:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- C:\ProgramData\Salt Project\Salt\srv\salt
|
||||
winrepo_source_dir: 'salt://win/repo-ng'
|
||||
winrepo_dir_ng: C:\ProgramData\Salt Project\Salt\srv\salt\win\repo-ng
|
||||
|
||||
The :mod:`winrepo.update_git_repos <salt.modules.winrepo.update_git_repos>`
|
||||
command will clone the repository to ``win\repo-ng`` on the file_roots.
|
||||
|
||||
Multiple Salt Environments
|
||||
--------------------------
|
||||
|
||||
This starts to get a little tricky. The winrepo repository doesn't
|
||||
get cloned to each environment when you run
|
||||
:mod:`winrepo.update_git_repos <salt.runners.winrepo.update_git_repos>`, so to
|
||||
make this work, all environments share the same winrepo. Applying states using
|
||||
the ``saltenv`` option will find the state files in the appropriate environment,
|
||||
but the package definition files will always be pulled from the same location.
|
||||
Therefore, you have to put the same winrepo location in each saltenv. Here's how
|
||||
this would look:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- C:\ProgramData\Salt Project\Salt\srv\salt\base
|
||||
- C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
|
||||
test:
|
||||
- C:\ProgramData\Salt Project\Salt\srv\salt\test
|
||||
- C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
|
||||
winrepo_source_dir: 'salt://salt-winrepo-ng'
|
||||
winrepo_dir_ng: C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
|
||||
winrepo_dir: C:\ProgramData\Salt Project\Salt\srv\salt\winrepo
|
||||
|
||||
When you run
|
||||
:mod:`winrepo.update_git_repos <salt.runners.winrepo.update_git_repos>` the
|
||||
Git repository will be cloned to the location specified in the
|
||||
``winrepo_dir_ng`` setting. I specified the ``winrepo_dir`` setting just so
|
||||
everything gets cloned to the same place. The directory that gets cloned is
|
||||
named ``salt-winrepo-ng`` so you specify that in the ``winrepo_source_dir``
|
||||
setting.
|
||||
|
||||
The ``winrepo`` directory should only contain the package definition files. You
|
||||
wouldn't want to place any states in the ``winrepo`` directory as they will be
|
||||
available to both environments.
|
||||
|
||||
Master
|
||||
======
|
||||
|
||||
When working in a Master/Minion environment you have to split up some of the
|
||||
config settings between the master and the minion. Here are some sample configs
|
||||
for winrepo in a Master/Minion environment.
|
||||
|
||||
Default Configuration
|
||||
---------------------
|
||||
|
||||
This is the default configuration if nothing is configured. The config is shown
|
||||
here for clarity. These are the defaults on the master:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- /srv/salt
|
||||
winrepo_dir_ng: /srv/salt/win/repo-ng
|
||||
|
||||
This is the default in the minion config:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
winrepo_source_dir: 'salt://win/repo-ng'
|
||||
|
||||
The :mod:`winrepo.update_git_repos <salt.runners.winrepo.update_git_repos>`
|
||||
command will clone the repository to ``win\repo-ng`` on the file_roots.
|
||||
|
||||
Multiple Salt Environments
|
||||
--------------------------
|
||||
|
||||
To set up multiple saltenvs using a Master/Minion configuration set the
|
||||
following in the master config:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
file_roots:
|
||||
base:
|
||||
- /srv/salt/base
|
||||
- /srv/salt/winrepo
|
||||
test:
|
||||
- /srv/salt/test
|
||||
- /srv/salt/winrepo
|
||||
winrepo_dir_ng: /srv/salt/winrepo
|
||||
winrepo_dir: /srv/salt/winrepo
|
||||
|
||||
Use the winrepo runner to set up the winrepo repository on the master.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run winrepo.update_git_repos
|
||||
|
||||
The winrepo will be cloned to ``/srv/salt/winrepo`` under a directory named
|
||||
``salt-winrepo-ng``.
|
||||
|
||||
Set the following on the minion config so the minion knows where to find the
|
||||
package definition files in the file_roots:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
winrepo_source_dir: 'salt://salt-winrepo-ng'
|
||||
|
||||
The same stipulations apply in a Master/Minion configuration as they do in a
|
||||
Masterless configuration
|
||||
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ pytest >= 7.2.0
|
|||
pytest-salt-factories >= 1.0.0rc29
|
||||
pytest-helpers-namespace >= 2019.1.8
|
||||
pytest-subtests
|
||||
pytest-timeout
|
||||
pytest-timeout >= 2.3.1
|
||||
pytest-httpserver
|
||||
pytest-custom-exit-code >= 0.3
|
||||
flaky
|
||||
|
|
|
@ -45,6 +45,7 @@ vcert; sys_platform != 'win32'
|
|||
virtualenv>=20.3.0
|
||||
watchdog>=0.9.0
|
||||
xmldiff>=2.4
|
||||
textfsm
|
||||
# Available template libraries that can be used
|
||||
genshi>=0.7.3
|
||||
cheetah3>=3.2.2
|
||||
|
|
|
@ -126,14 +126,14 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/darwin.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -299,7 +299,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -375,9 +375,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -477,7 +477,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -489,6 +489,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -125,14 +125,14 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/freebsd.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -303,7 +303,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -379,9 +379,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -482,7 +482,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -494,6 +494,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -141,14 +141,14 @@ exceptiongroup==1.1.1
|
|||
# pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/linux.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -329,7 +329,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -413,9 +413,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -539,7 +539,7 @@ slack-bolt==1.18.0
|
|||
# via -r requirements/static/ci/linux.in
|
||||
slack-sdk==3.21.3
|
||||
# via slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
|
@ -556,6 +556,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -124,13 +124,15 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/windows.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==1.0.0
|
||||
# via textfsm
|
||||
genshi==0.7.7
|
||||
# via -r requirements/static/ci/common.in
|
||||
geomet==0.2.1.post1
|
||||
|
@ -256,7 +258,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -332,9 +334,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -434,6 +436,7 @@ six==1.15.0
|
|||
# python-dateutil
|
||||
# pyvmomi
|
||||
# pywinrm
|
||||
# textfsm
|
||||
# websocket-client
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
|
@ -445,6 +448,8 @@ tempora==5.3.0
|
|||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/windows.txt
|
||||
# portend
|
||||
textfsm==1.1.3
|
||||
# via -r requirements/static/ci/common.in
|
||||
timelib==0.3.0
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.10/windows.txt
|
||||
|
|
|
@ -123,14 +123,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/darwin.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -296,7 +296,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -376,9 +376,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -478,7 +478,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -490,6 +490,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -122,14 +122,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/freebsd.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -300,7 +300,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -380,9 +380,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -484,7 +484,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -496,6 +496,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -136,14 +136,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/linux.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -324,7 +324,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -412,9 +412,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -539,7 +539,7 @@ slack-bolt==1.18.0
|
|||
# via -r requirements/static/ci/linux.in
|
||||
slack-sdk==3.21.3
|
||||
# via slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
|
@ -556,6 +556,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -121,13 +121,15 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/windows.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==1.0.0
|
||||
# via textfsm
|
||||
genshi==0.7.7
|
||||
# via -r requirements/static/ci/common.in
|
||||
geomet==0.2.1.post1
|
||||
|
@ -253,7 +255,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -333,9 +335,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -435,6 +437,7 @@ six==1.15.0
|
|||
# python-dateutil
|
||||
# pyvmomi
|
||||
# pywinrm
|
||||
# textfsm
|
||||
# websocket-client
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
|
@ -446,6 +449,8 @@ tempora==5.3.0
|
|||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/windows.txt
|
||||
# portend
|
||||
textfsm==1.1.3
|
||||
# via -r requirements/static/ci/common.in
|
||||
timelib==0.3.0
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.11/windows.txt
|
||||
|
|
|
@ -170,7 +170,7 @@ filelock==3.13.1
|
|||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# -r requirements/pytest.txt
|
||||
|
@ -180,7 +180,7 @@ frozenlist==1.4.1
|
|||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# napalm
|
||||
|
@ -416,7 +416,7 @@ platformdirs==4.0.0
|
|||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# pytest
|
||||
|
@ -543,11 +543,11 @@ pytest-system-statistics==1.0.2
|
|||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# -r requirements/pytest.txt
|
||||
|
@ -690,7 +690,7 @@ smbprotocol==1.10.1
|
|||
# via
|
||||
# -r requirements/static/ci/cloud.in
|
||||
# pypsexec
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# gitdb
|
||||
|
@ -710,6 +710,7 @@ tempora==5.3.0
|
|||
textfsm==1.1.3
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -123,14 +123,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/darwin.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -296,7 +296,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -376,9 +376,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -478,7 +478,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -490,6 +490,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -122,14 +122,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/freebsd.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -300,7 +300,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -380,9 +380,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -484,7 +484,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -496,6 +496,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -192,7 +192,7 @@ frozenlist==1.4.1
|
|||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# napalm
|
||||
|
@ -691,7 +691,7 @@ slack-sdk==3.21.3
|
|||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# gitdb
|
||||
|
@ -717,6 +717,7 @@ tempora==5.3.0
|
|||
textfsm==1.1.3
|
||||
# via
|
||||
# -c requirements/static/ci/py3.12/linux.txt
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -136,14 +136,14 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/linux.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==0.18.3
|
||||
future==1.0.0
|
||||
# via
|
||||
# napalm
|
||||
# textfsm
|
||||
|
@ -324,7 +324,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -412,9 +412,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==1.4.2
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -539,7 +539,7 @@ slack-bolt==1.18.0
|
|||
# via -r requirements/static/ci/linux.in
|
||||
slack-sdk==3.21.3
|
||||
# via slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
|
@ -556,6 +556,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -121,13 +121,15 @@ etcd3-py==0.1.6
|
|||
# via -r requirements/static/ci/common.in
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/windows.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==1.0.0
|
||||
# via textfsm
|
||||
genshi==0.7.7
|
||||
# via -r requirements/static/ci/common.in
|
||||
geomet==0.2.1.post1
|
||||
|
@ -253,7 +255,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -333,9 +335,9 @@ pytest-subtests==0.4.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -435,6 +437,7 @@ six==1.15.0
|
|||
# python-dateutil
|
||||
# pyvmomi
|
||||
# pywinrm
|
||||
# textfsm
|
||||
# websocket-client
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
|
@ -446,6 +449,8 @@ tempora==5.3.0
|
|||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/windows.txt
|
||||
# portend
|
||||
textfsm==1.1.3
|
||||
# via -r requirements/static/ci/common.in
|
||||
timelib==0.3.0
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.12/windows.txt
|
||||
|
|
|
@ -125,7 +125,7 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
|
@ -307,7 +307,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -383,9 +383,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -486,7 +486,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -498,6 +498,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -136,7 +136,7 @@ exceptiongroup==1.1.1
|
|||
# pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
|
@ -326,7 +326,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -410,9 +410,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -533,7 +533,7 @@ slack-bolt==1.18.0
|
|||
# via -r requirements/static/ci/linux.in
|
||||
slack-sdk==3.21.3
|
||||
# via slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
|
@ -550,6 +550,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -124,13 +124,15 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.8/windows.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==1.0.0
|
||||
# via textfsm
|
||||
genshi==0.7.7
|
||||
# via -r requirements/static/ci/common.in
|
||||
geomet==0.2.1.post1
|
||||
|
@ -260,7 +262,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -336,9 +338,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -439,6 +441,7 @@ six==1.15.0
|
|||
# python-dateutil
|
||||
# pyvmomi
|
||||
# pywinrm
|
||||
# textfsm
|
||||
# websocket-client
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
|
@ -450,6 +453,8 @@ tempora==5.3.0
|
|||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.8/windows.txt
|
||||
# portend
|
||||
textfsm==1.1.3
|
||||
# via -r requirements/static/ci/common.in
|
||||
timelib==0.3.0
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.8/windows.txt
|
||||
|
|
|
@ -126,7 +126,7 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
|
@ -299,7 +299,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -375,9 +375,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -477,7 +477,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -489,6 +489,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -125,7 +125,7 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
|
@ -303,7 +303,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -379,9 +379,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -482,7 +482,7 @@ six==1.16.0
|
|||
# transitions
|
||||
# vcert
|
||||
# websocket-client
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sqlparse==0.4.4
|
||||
# via -r requirements/static/ci/common.in
|
||||
|
@ -494,6 +494,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -136,7 +136,7 @@ exceptiongroup==1.1.1
|
|||
# pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
|
@ -322,7 +322,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -406,9 +406,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -529,7 +529,7 @@ slack-bolt==1.18.0
|
|||
# via -r requirements/static/ci/linux.in
|
||||
slack-sdk==3.21.3
|
||||
# via slack-bolt
|
||||
smmap==5.0.0
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
sniffio==1.3.0
|
||||
# via
|
||||
|
@ -546,6 +546,7 @@ tempora==5.3.0
|
|||
# portend
|
||||
textfsm==1.1.3
|
||||
# via
|
||||
# -r requirements/static/ci/common.in
|
||||
# napalm
|
||||
# netmiko
|
||||
# ntc-templates
|
||||
|
|
|
@ -124,13 +124,15 @@ exceptiongroup==1.1.1
|
|||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via virtualenv
|
||||
flaky==3.7.0
|
||||
flaky==3.8.1
|
||||
# via -r requirements/pytest.txt
|
||||
frozenlist==1.4.1
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.9/windows.txt
|
||||
# aiohttp
|
||||
# aiosignal
|
||||
future==1.0.0
|
||||
# via textfsm
|
||||
genshi==0.7.7
|
||||
# via -r requirements/static/ci/common.in
|
||||
geomet==0.2.1.post1
|
||||
|
@ -256,7 +258,7 @@ pathspec==0.11.1
|
|||
# via yamllint
|
||||
platformdirs==4.0.0
|
||||
# via virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.4.0
|
||||
# via pytest
|
||||
portend==3.1.0
|
||||
# via
|
||||
|
@ -332,9 +334,9 @@ pytest-subtests==0.11.0
|
|||
# via -r requirements/pytest.txt
|
||||
pytest-system-statistics==1.0.2
|
||||
# via pytest-salt-factories
|
||||
pytest-timeout==2.1.0
|
||||
pytest-timeout==2.3.1
|
||||
# via -r requirements/pytest.txt
|
||||
pytest==7.3.2
|
||||
pytest==8.1.1
|
||||
# via
|
||||
# -r requirements/pytest.txt
|
||||
# pytest-custom-exit-code
|
||||
|
@ -435,6 +437,7 @@ six==1.15.0
|
|||
# python-dateutil
|
||||
# pyvmomi
|
||||
# pywinrm
|
||||
# textfsm
|
||||
# websocket-client
|
||||
smmap==5.0.1
|
||||
# via gitdb
|
||||
|
@ -446,6 +449,8 @@ tempora==5.3.0
|
|||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.9/windows.txt
|
||||
# portend
|
||||
textfsm==1.1.3
|
||||
# via -r requirements/static/ci/common.in
|
||||
timelib==0.3.0
|
||||
# via
|
||||
# -c requirements/static/ci/../pkg/py3.9/windows.txt
|
||||
|
|
|
@ -135,6 +135,7 @@ def update():
|
|||
cached_file_path = _get_cached_file_name(
|
||||
bucket, saltenv, file_path
|
||||
)
|
||||
|
||||
log.debug("%s - %s : %s", bucket, saltenv, file_path)
|
||||
|
||||
# load the file from S3 if it's not in the cache or it's old
|
||||
|
@ -356,6 +357,7 @@ def _init():
|
|||
|
||||
# check mtime of the buckets files cache
|
||||
metadata = None
|
||||
|
||||
try:
|
||||
if os.path.getmtime(cache_file) > exp:
|
||||
metadata = _read_buckets_cache_file(cache_file)
|
||||
|
@ -366,6 +368,8 @@ def _init():
|
|||
# bucket files cache expired or does not exist
|
||||
metadata = _refresh_buckets_cache_file(cache_file)
|
||||
|
||||
_prune_deleted_files(metadata)
|
||||
|
||||
return metadata
|
||||
|
||||
|
||||
|
@ -374,7 +378,6 @@ 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")
|
||||
|
||||
|
||||
|
@ -383,26 +386,15 @@ 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)
|
||||
|
||||
# make sure bucket and saltenv directories exist
|
||||
if not os.path.exists(os.path.dirname(file_path)):
|
||||
os.makedirs(os.path.dirname(file_path))
|
||||
|
||||
return file_path
|
||||
return os.path.join(_get_cache_dir(), saltenv, bucket_name, 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(_get_cache_dir(), "buckets_files.cache")
|
||||
|
||||
|
||||
def _refresh_buckets_cache_file(cache_file):
|
||||
|
@ -423,6 +415,7 @@ def _refresh_buckets_cache_file(cache_file):
|
|||
path_style,
|
||||
https_enable,
|
||||
) = _get_s3_key()
|
||||
|
||||
metadata = {}
|
||||
|
||||
# helper s3 query function
|
||||
|
@ -571,10 +564,72 @@ def _refresh_buckets_cache_file(cache_file):
|
|||
return metadata
|
||||
|
||||
|
||||
def _prune_deleted_files(metadata):
|
||||
cache_dir = _get_cache_dir()
|
||||
cached_files = set()
|
||||
roots = set()
|
||||
|
||||
if _is_env_per_bucket():
|
||||
for env, env_data in metadata.items():
|
||||
for bucket_meta in env_data:
|
||||
for bucket, bucket_data in bucket_meta.items():
|
||||
root = os.path.join(cache_dir, env, bucket)
|
||||
|
||||
if os.path.exists(root):
|
||||
roots.add(root)
|
||||
|
||||
for meta in bucket_data:
|
||||
path = meta["Key"]
|
||||
cached_files.add(path)
|
||||
|
||||
else:
|
||||
for env, env_data in metadata.items():
|
||||
for bucket in _get_buckets():
|
||||
root = os.path.join(cache_dir, bucket)
|
||||
|
||||
if os.path.exists(root):
|
||||
roots.add(root)
|
||||
|
||||
for meta in env_data:
|
||||
cached_files.add(meta["Key"])
|
||||
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
import pprint
|
||||
|
||||
log.debug("cached file list:\n%s", pprint.pformat(cached_files))
|
||||
|
||||
for root in roots:
|
||||
for base, dirs, files in os.walk(root):
|
||||
for file_name in files:
|
||||
path = os.path.join(base, file_name)
|
||||
relpath = os.path.relpath(path, root)
|
||||
|
||||
if relpath not in cached_files:
|
||||
log.debug("File '%s' not found in cached file list", path)
|
||||
log.info(
|
||||
"File '%s' was deleted from bucket, deleting local copy",
|
||||
relpath,
|
||||
)
|
||||
|
||||
os.unlink(path)
|
||||
dirname = os.path.dirname(path)
|
||||
|
||||
# delete empty dirs all the way up to the cache dir
|
||||
while dirname != cache_dir and len(os.listdir(dirname)) == 0:
|
||||
log.debug("Directory '%s' is now empty, removing", dirname)
|
||||
os.rmdir(dirname)
|
||||
dirname = os.path.dirname(dirname)
|
||||
|
||||
|
||||
def _write_buckets_cache_file(metadata, cache_file):
|
||||
"""
|
||||
Write the contents of the buckets cache file
|
||||
"""
|
||||
cache_dir = _get_cache_dir()
|
||||
|
||||
if not os.path.exists(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
|
||||
if os.path.isfile(cache_file):
|
||||
os.remove(cache_file)
|
||||
|
||||
|
@ -591,6 +646,10 @@ def _read_buckets_cache_file(cache_file):
|
|||
|
||||
log.debug("Reading buckets cache file")
|
||||
|
||||
if not os.path.exists(cache_file):
|
||||
log.debug("Cache file does not exist")
|
||||
return None
|
||||
|
||||
with salt.utils.files.fopen(cache_file, "rb") as fp_:
|
||||
try:
|
||||
data = pickle.load(fp_)
|
||||
|
@ -698,6 +757,13 @@ 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
|
||||
"""
|
||||
|
||||
# make sure bucket and saltenv directories exist
|
||||
target_dir = os.path.dirname(cached_file_path)
|
||||
|
||||
if not os.path.exists(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
|
||||
(
|
||||
key,
|
||||
keyid,
|
||||
|
|
|
@ -20,6 +20,7 @@ import re
|
|||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import Request as _Request
|
||||
from urllib.request import urlopen as _urlopen
|
||||
|
@ -204,23 +205,24 @@ if not HAS_APT:
|
|||
repo_line.append(self.type)
|
||||
opts = _get_opts(self.line)
|
||||
if self.architectures:
|
||||
archs = ",".join(self.architectures)
|
||||
opts["arch"]["full"] = f"arch={archs}"
|
||||
if "arch" not in opts:
|
||||
opts["arch"] = {}
|
||||
opts["arch"]["full"] = f"arch={','.join(self.architectures)}"
|
||||
opts["arch"]["value"] = self.architectures
|
||||
if self.signedby:
|
||||
if "signedby" not in opts:
|
||||
opts["signedby"] = {}
|
||||
opts["signedby"]["full"] = f"signed-by={self.signedby}"
|
||||
opts["signedby"]["value"] = self.signedby
|
||||
|
||||
ordered_opts = [
|
||||
opt_type for opt_type, opt in opts.items() if opt["full"] != ""
|
||||
]
|
||||
ordered_opts = []
|
||||
|
||||
for opt in opts.values():
|
||||
if opt["full"] != "":
|
||||
ordered_opts[opt["index"]] = opt["full"]
|
||||
ordered_opts.append(opt["full"])
|
||||
|
||||
if ordered_opts:
|
||||
repo_line.append("[{}]".format(" ".join(ordered_opts)))
|
||||
repo_line.append(f"[{' '.join(ordered_opts)}]")
|
||||
|
||||
repo_line += [self.uri, self.dist, " ".join(self.comps)]
|
||||
if self.comment:
|
||||
|
@ -237,10 +239,12 @@ if not HAS_APT:
|
|||
if repo_line[1].startswith("["):
|
||||
repo_line = [x for x in (line.strip("[]") for line in repo_line) if x]
|
||||
opts = _get_opts(self.line)
|
||||
self.architectures.extend(opts["arch"]["value"])
|
||||
self.signedby = opts["signedby"]["value"]
|
||||
for opt in opts:
|
||||
opt = opts[opt]["full"]
|
||||
if "arch" in opts:
|
||||
self.architectures.extend(opts["arch"]["value"])
|
||||
if "signedby" in opts:
|
||||
self.signedby = opts["signedby"]["value"]
|
||||
for opt in opts.values():
|
||||
opt = opt["full"]
|
||||
if opt:
|
||||
try:
|
||||
repo_line.pop(repo_line.index(opt))
|
||||
|
@ -1743,31 +1747,27 @@ def _get_opts(line):
|
|||
Return all opts in [] for a repo line
|
||||
"""
|
||||
get_opts = re.search(r"\[(.*=.*)\]", line)
|
||||
ret = {
|
||||
"arch": {"full": "", "value": "", "index": 0},
|
||||
"signedby": {"full": "", "value": "", "index": 0},
|
||||
}
|
||||
|
||||
ret = OrderedDict()
|
||||
if not get_opts:
|
||||
return ret
|
||||
opts = get_opts.group(0).strip("[]")
|
||||
architectures = []
|
||||
for idx, opt in enumerate(opts.split()):
|
||||
for opt in opts.split():
|
||||
if opt.startswith("arch"):
|
||||
architectures.extend(opt.split("=", 1)[1].split(","))
|
||||
ret["arch"] = {}
|
||||
ret["arch"]["full"] = opt
|
||||
ret["arch"]["value"] = architectures
|
||||
ret["arch"]["index"] = idx
|
||||
elif opt.startswith("signed-by"):
|
||||
ret["signedby"] = {}
|
||||
ret["signedby"]["full"] = opt
|
||||
ret["signedby"]["value"] = opt.split("=", 1)[1]
|
||||
ret["signedby"]["index"] = idx
|
||||
else:
|
||||
other_opt = opt.split("=", 1)[0]
|
||||
ret[other_opt] = {}
|
||||
ret[other_opt]["full"] = opt
|
||||
ret[other_opt]["value"] = opt.split("=", 1)[1]
|
||||
ret[other_opt]["index"] = idx
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -1780,7 +1780,11 @@ def _split_repo_str(repo):
|
|||
if not HAS_APT:
|
||||
signedby = entry.signedby
|
||||
else:
|
||||
signedby = _get_opts(line=repo)["signedby"].get("value", "")
|
||||
opts = _get_opts(line=repo)
|
||||
if "signedby" in opts:
|
||||
signedby = opts["signedby"].get("value", "")
|
||||
else:
|
||||
signedby = ""
|
||||
if signedby:
|
||||
# python3-apt does not support signedby. So if signedby
|
||||
# is in the repo we have to check our code to see if the
|
||||
|
@ -1948,7 +1952,12 @@ def list_repos(**kwargs):
|
|||
if not HAS_APT:
|
||||
signedby = source.signedby
|
||||
else:
|
||||
signedby = _get_opts(line=source.line)["signedby"].get("value", "")
|
||||
opts = _get_opts(line=source.line)
|
||||
if "signedby" in opts:
|
||||
signedby = opts["signedby"].get("value", "")
|
||||
else:
|
||||
signedby = ""
|
||||
|
||||
repo = {}
|
||||
repo["file"] = source.file
|
||||
repo["comps"] = getattr(source, "comps", [])
|
||||
|
@ -2968,7 +2977,11 @@ def mod_repo(repo, saltenv="base", aptkey=True, **kwargs):
|
|||
if not HAS_APT:
|
||||
signedby = mod_source.signedby
|
||||
else:
|
||||
signedby = _get_opts(repo)["signedby"].get("value", "")
|
||||
opts = _get_opts(repo)
|
||||
if "signedby" in opts:
|
||||
signedby = opts["signedby"].get("value", "")
|
||||
else:
|
||||
signedby = ""
|
||||
|
||||
return {
|
||||
repo: {
|
||||
|
@ -3069,7 +3082,11 @@ def _expand_repo_def(os_name, os_codename=None, **kwargs):
|
|||
signedby = source_entry.signedby
|
||||
kwargs["signedby"] = signedby
|
||||
else:
|
||||
signedby = _get_opts(repo)["signedby"].get("value", "")
|
||||
opts = _get_opts(repo)
|
||||
if "signedby" in opts:
|
||||
signedby = opts["signedby"].get("value", "")
|
||||
else:
|
||||
signedby = ""
|
||||
|
||||
_source_entry = source_list.add(
|
||||
type=source_entry.type,
|
||||
|
|
|
@ -19,7 +19,7 @@ inside the renderer (Jinja, Mako, Genshi, etc.).
|
|||
import logging
|
||||
import os
|
||||
|
||||
from salt.utils.files import fopen
|
||||
import salt.utils.files
|
||||
|
||||
try:
|
||||
import textfsm
|
||||
|
@ -188,11 +188,14 @@ def extract(template_path, raw_text=None, raw_text_file=None, saltenv="base"):
|
|||
# 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")
|
||||
# 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)
|
||||
with salt.utils.files.fopen(tpl_cached_path, "r") as tpl_file_handle:
|
||||
# pylint: disable=W8470
|
||||
tpl_file_data = tpl_file_handle.read()
|
||||
log.debug(tpl_file_data)
|
||||
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"] = (
|
||||
|
|
|
@ -47,6 +47,7 @@ import time
|
|||
import urllib.parse
|
||||
from functools import cmp_to_key
|
||||
|
||||
import salt.fileserver
|
||||
import salt.payload
|
||||
import salt.syspaths
|
||||
import salt.utils.args
|
||||
|
@ -907,7 +908,7 @@ def refresh_db(**kwargs):
|
|||
The database is stored in a serialized format located by default at the
|
||||
following location:
|
||||
|
||||
``C:\salt\var\cache\salt\minion\files\base\win\repo-ng\winrepo.p``
|
||||
``C:\ProgramData\Salt Project\Salt\var\cache\salt\minion\files\base\win\repo-ng\winrepo.p``
|
||||
|
||||
This module performs the following steps to generate the software metadata
|
||||
database:
|
||||
|
@ -915,7 +916,7 @@ def refresh_db(**kwargs):
|
|||
- Fetch the package definition files (.sls) from `winrepo_source_dir`
|
||||
(default `salt://win/repo-ng`) and cache them in
|
||||
`<cachedir>\files\<saltenv>\<winrepo_source_dir>`
|
||||
(default: ``C:\salt\var\cache\salt\minion\files\base\win\repo-ng``)
|
||||
(default: ``C:\ProgramData\Salt Project\Salt\var\cache\salt\minion\files\base\win\repo-ng``)
|
||||
- Call :py:func:`pkg.genrepo <salt.modules.win_pkg.genrepo>` to parse the
|
||||
package definition files and generate the repository metadata database
|
||||
file (`winrepo.p`)
|
||||
|
@ -976,7 +977,7 @@ def refresh_db(**kwargs):
|
|||
|
||||
.. warning::
|
||||
When calling this command from a state using `module.run` be sure to
|
||||
pass `failhard: False`. Otherwise the state will report failure if it
|
||||
pass `failhard: False`. Otherwise, the state will report failure if it
|
||||
encounters a bad software definition file.
|
||||
|
||||
CLI Example:
|
||||
|
@ -1020,6 +1021,11 @@ def refresh_db(**kwargs):
|
|||
"Failed to clear one or more winrepo cache files", info={"failed": failed}
|
||||
)
|
||||
|
||||
# Clear the cache so that newly copied package definitions will be picked up
|
||||
fileserver = salt.fileserver.Fileserver(__opts__)
|
||||
load = {"saltenv": saltenv, "fsbackend": None}
|
||||
fileserver.clear_file_list_cache(load=load)
|
||||
|
||||
# Cache repo-ng locally
|
||||
log.info("Fetching *.sls files from %s", repo_details.winrepo_source_dir)
|
||||
try:
|
||||
|
@ -1170,10 +1176,11 @@ def genrepo(**kwargs):
|
|||
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,
|
||||
filename=os.path.join(root, name),
|
||||
short_path_name=os.path.join(short_path, name),
|
||||
ret=ret,
|
||||
successful_verbose=successful_verbose,
|
||||
saltenv=saltenv,
|
||||
)
|
||||
|
||||
with salt.utils.files.fopen(repo_details.winrepo_file, "wb") as repo_cache:
|
||||
|
@ -1212,7 +1219,9 @@ def genrepo(**kwargs):
|
|||
return results
|
||||
|
||||
|
||||
def _repo_process_pkg_sls(filename, short_path_name, ret, successful_verbose):
|
||||
def _repo_process_pkg_sls(
|
||||
filename, short_path_name, ret, successful_verbose, saltenv="base"
|
||||
):
|
||||
renderers = salt.loader.render(__opts__, __salt__)
|
||||
|
||||
def _failed_compile(prefix_msg, error_msg):
|
||||
|
@ -1227,6 +1236,7 @@ def _repo_process_pkg_sls(filename, short_path_name, ret, successful_verbose):
|
|||
__opts__["renderer"],
|
||||
__opts__.get("renderer_blacklist", ""),
|
||||
__opts__.get("renderer_whitelist", ""),
|
||||
saltenv=saltenv,
|
||||
)
|
||||
except SaltRenderError as exc:
|
||||
return _failed_compile("Failed to compile", exc)
|
||||
|
@ -2359,7 +2369,23 @@ def _get_name_map(saltenv="base"):
|
|||
|
||||
def get_package_info(name, saltenv="base"):
|
||||
"""
|
||||
Return package info. Returns empty map if package not available.
|
||||
Get information about the package as found in the winrepo database
|
||||
|
||||
Args:
|
||||
|
||||
name (str): The name of the package
|
||||
|
||||
saltenv (str): The salt environment to use. Default is "base"
|
||||
|
||||
Returns:
|
||||
dict: A dictionary of package info, empty if package not available
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' pkg.get_package_info chrome
|
||||
|
||||
"""
|
||||
return _get_package_info(name=name, saltenv=saltenv)
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import salt.utils.json
|
|||
import salt.utils.platform
|
||||
import salt.utils.powershell
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
_DEFAULT_CONTEXT = "LocalMachine"
|
||||
_DEFAULT_FORMAT = "cer"
|
||||
|
@ -73,15 +73,19 @@ def _cmd_run(cmd, as_json=False):
|
|||
"".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["stderr"]:
|
||||
raise CommandExecutionError(
|
||||
"Unable to execute command: {}\nError: {}".format(cmd, cmd_ret["stderr"])
|
||||
)
|
||||
|
||||
if as_json:
|
||||
try:
|
||||
items = salt.utils.json.loads(cmd_ret["stdout"], strict=False)
|
||||
return items
|
||||
except ValueError:
|
||||
_LOG.error("Unable to parse return data as Json.")
|
||||
raise CommandExecutionError(
|
||||
"Unable to parse return data as JSON:\n{}".format(cmd_ret["stdout"])
|
||||
)
|
||||
|
||||
return cmd_ret["stdout"]
|
||||
|
||||
|
|
|
@ -4005,13 +4005,25 @@ def directory(
|
|||
if not force:
|
||||
return _error(
|
||||
ret,
|
||||
"File exists where the backup target {} should go".format(
|
||||
backupname
|
||||
),
|
||||
f"File exists where the backup target {backupname} should go",
|
||||
)
|
||||
if __opts__["test"]:
|
||||
ret["changes"][
|
||||
"forced"
|
||||
] = f"Existing file at backup path {backupname} would be removed"
|
||||
else:
|
||||
__salt__["file.remove"](backupname)
|
||||
os.rename(name, backupname)
|
||||
|
||||
if __opts__["test"]:
|
||||
ret["changes"]["backup"] = f"{name} would be renamed to {backupname}"
|
||||
ret["changes"][name] = {"directory": "new"}
|
||||
ret["comment"] = (
|
||||
f"{name} would be backed up and replaced with a new directory"
|
||||
)
|
||||
ret["result"] = None
|
||||
return ret
|
||||
else:
|
||||
os.rename(name, backupname)
|
||||
elif force:
|
||||
# Remove whatever is in the way
|
||||
if os.path.isfile(name):
|
||||
|
|
|
@ -184,11 +184,23 @@ def get_sam_name(username):
|
|||
|
||||
.. note:: Long computer names are truncated to 15 characters
|
||||
"""
|
||||
# Some special identity groups require special handling. They do not have
|
||||
# the domain prepended to the name. They should be added here as they are
|
||||
# discovered. Use the SID to be locale agnostic.
|
||||
# Everyone: S-1-1-0
|
||||
special_id_groups = ["S-1-1-0"]
|
||||
|
||||
try:
|
||||
sid_obj = win32security.LookupAccountName(None, username)[0]
|
||||
except pywintypes.error:
|
||||
return "\\".join([platform.node()[:15].upper(), username])
|
||||
|
||||
sid = win32security.ConvertSidToStringSid(sid_obj)
|
||||
username, domain, _ = win32security.LookupAccountSid(None, sid_obj)
|
||||
|
||||
if sid in special_id_groups:
|
||||
return username
|
||||
|
||||
return "\\".join([domain, username])
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import re
|
|||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
from functools import lru_cache, partial, wraps
|
||||
from functools import lru_cache
|
||||
from unittest import TestCase # pylint: disable=blacklisted-module
|
||||
|
||||
import _pytest.logging
|
||||
|
@ -448,8 +448,6 @@ def pytest_collection_modifyitems(config, items):
|
|||
groups_collection_modifyitems(config, items)
|
||||
from_filenames_collection_modifyitems(config, items)
|
||||
|
||||
log.warning("Mofifying collected tests to keep track of fixture usage")
|
||||
|
||||
timeout_marker_tests_paths = (
|
||||
str(PYTESTS_DIR / "pkg"),
|
||||
str(PYTESTS_DIR / "scenarios"),
|
||||
|
@ -478,103 +476,6 @@ def pytest_collection_modifyitems(config, items):
|
|||
# Default to counting only the test execution for the timeouts, ie,
|
||||
# withough including the fixtures setup time towards the timeout.
|
||||
item.add_marker(pytest.mark.timeout(90, func_only=True))
|
||||
for fixture in item.fixturenames:
|
||||
if fixture not in item._fixtureinfo.name2fixturedefs:
|
||||
continue
|
||||
for fixturedef in item._fixtureinfo.name2fixturedefs[fixture]:
|
||||
if fixturedef.scope != "package":
|
||||
continue
|
||||
try:
|
||||
fixturedef.finish.__wrapped__
|
||||
except AttributeError:
|
||||
original_func = fixturedef.finish
|
||||
|
||||
def wrapper(func, fixturedef):
|
||||
@wraps(func)
|
||||
def wrapped(self, request, nextitem=False):
|
||||
try:
|
||||
return self._finished
|
||||
except AttributeError:
|
||||
if nextitem:
|
||||
fpath = pathlib.Path(self.baseid).resolve()
|
||||
tpath = pathlib.Path(
|
||||
nextitem.fspath.strpath
|
||||
).resolve()
|
||||
try:
|
||||
tpath.relative_to(fpath)
|
||||
# The test module is within the same package that the fixture is
|
||||
if (
|
||||
not request.session.shouldfail
|
||||
and not request.session.shouldstop
|
||||
):
|
||||
log.debug(
|
||||
"The next test item is still under the"
|
||||
" fixture package path. Not"
|
||||
" terminating %s",
|
||||
self,
|
||||
)
|
||||
return
|
||||
except ValueError:
|
||||
pass
|
||||
log.debug("Finish called on %s", self)
|
||||
try:
|
||||
return func(request)
|
||||
except (
|
||||
BaseException # pylint: disable=broad-except
|
||||
) as exc:
|
||||
pytest.fail(
|
||||
"Failed to run finish() on {}: {}".format(
|
||||
fixturedef, exc
|
||||
),
|
||||
pytrace=True,
|
||||
)
|
||||
finally:
|
||||
self._finished = True
|
||||
|
||||
return partial(wrapped, fixturedef)
|
||||
|
||||
fixturedef.finish = wrapper(fixturedef.finish, fixturedef)
|
||||
try:
|
||||
fixturedef.finish.__wrapped__
|
||||
except AttributeError:
|
||||
fixturedef.finish.__wrapped__ = original_func
|
||||
|
||||
|
||||
@pytest.hookimpl(trylast=True, hookwrapper=True)
|
||||
def pytest_runtest_protocol(item, nextitem):
|
||||
"""
|
||||
implements the runtest_setup/call/teardown protocol for
|
||||
the given test item, including capturing exceptions and calling
|
||||
reporting hooks.
|
||||
|
||||
:arg item: test item for which the runtest protocol is performed.
|
||||
|
||||
:arg nextitem: the scheduled-to-be-next test item (or None if this
|
||||
is the end my friend). This argument is passed on to
|
||||
:py:func:`pytest_runtest_teardown`.
|
||||
|
||||
:return boolean: True if no further hook implementations should be invoked.
|
||||
|
||||
|
||||
Stops at first non-None result, see :ref:`firstresult`
|
||||
"""
|
||||
request = item._request
|
||||
used_fixture_defs = []
|
||||
for fixture in item.fixturenames:
|
||||
if fixture not in item._fixtureinfo.name2fixturedefs:
|
||||
continue
|
||||
for fixturedef in reversed(item._fixtureinfo.name2fixturedefs[fixture]):
|
||||
if fixturedef.scope != "package":
|
||||
continue
|
||||
used_fixture_defs.append(fixturedef)
|
||||
try:
|
||||
# Run the test
|
||||
yield
|
||||
finally:
|
||||
for fixturedef in used_fixture_defs:
|
||||
fixturedef.finish(request, nextitem=nextitem)
|
||||
del request
|
||||
del used_fixture_defs
|
||||
|
||||
|
||||
def pytest_markeval_namespace(config):
|
||||
|
|
|
@ -17,6 +17,7 @@ salt/_logging/(impl|handlers).py:
|
|||
salt/modules/(apkpkg|aptpkg|ebuildpkg|dpkg_lowpkg|freebsdpkg|mac_brew_pkg|mac_ports_pkg|openbsdpkg|opkg|pacmanpkg|pkgin|pkgng|pkg_resource|rpm_lowpkg|solarisipspkg|solarispkg|win_pkg|xbpspkg|yumpkg|zypperpkg)\.py:
|
||||
- pytests.unit.states.test_pkg
|
||||
- pytests.functional.modules.test_pkg
|
||||
- pytests.functional.modules.test_win_pkg
|
||||
- pytests.functional.states.test_pkg
|
||||
- pytests.functional.states.pkgrepo.test_centos
|
||||
- pytests.functional.states.pkgrepo.test_debian
|
||||
|
|
|
@ -156,6 +156,9 @@ def test_get_offset(timezone):
|
|||
"""
|
||||
Test timezone.get_offset
|
||||
"""
|
||||
pytz = pytest.importorskip("pytz")
|
||||
now = datetime.datetime.now(tz=pytz.UTC)
|
||||
|
||||
ret = timezone.set_zone("Pacific/Wake")
|
||||
assert ret
|
||||
ret = timezone.get_offset()
|
||||
|
@ -166,7 +169,11 @@ def test_get_offset(timezone):
|
|||
assert ret
|
||||
ret = timezone.get_offset()
|
||||
assert isinstance(ret, str)
|
||||
assert ret == "-0800"
|
||||
|
||||
if now.astimezone(pytz.timezone("America/Los_Angeles")).dst():
|
||||
assert ret == "-0700"
|
||||
else:
|
||||
assert ret == "-0800"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_reset_zone")
|
||||
|
|
35
tests/pytests/functional/modules/test_win_pkg.py
Normal file
35
tests/pytests/functional/modules/test_win_pkg.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import pytest
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.windows_whitelisted,
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
pytest.mark.slow_test,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def pkg_def_contents(state_tree):
|
||||
return r"""
|
||||
my-software:
|
||||
'1.0.1':
|
||||
full_name: 'My Software'
|
||||
installer: 'C:\files\mysoftware.msi'
|
||||
install_flags: '/qn /norestart'
|
||||
uninstaller: 'C:\files\mysoftware.msi'
|
||||
uninstall_flags: '/qn /norestart'
|
||||
msiexec: True
|
||||
reboot: False
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def pkg(modules):
|
||||
yield modules.pkg
|
||||
|
||||
|
||||
def test_refresh_db(pkg, pkg_def_contents, state_tree, minion_opts):
|
||||
assert len(pkg.get_package_info("my-software")) == 0
|
||||
repo_dir = state_tree / "win" / "repo-ng"
|
||||
with pytest.helpers.temp_file("my-software.sls", pkg_def_contents, repo_dir):
|
||||
pkg.refresh_db()
|
||||
assert len(pkg.get_package_info("my-software")) == 1
|
|
@ -436,3 +436,55 @@ def test_issue_12209_follow_symlinks(
|
|||
assert one_group_check == state_file_account.group.name
|
||||
two_group_check = modules.file.get_group(str(twodir), follow_symlinks=False)
|
||||
assert two_group_check == state_file_account.group.name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("backupname_isfile", [False, True])
|
||||
def test_directory_backupname_force_test_mode_noclobber(
|
||||
file, tmp_path, backupname_isfile
|
||||
):
|
||||
"""
|
||||
Ensure that file.directory does not make changes when backupname is used
|
||||
alongside force=True and test=True.
|
||||
|
||||
See https://github.com/saltstack/salt/issues/66049
|
||||
"""
|
||||
source_dir = tmp_path / "source_directory"
|
||||
source_dir.mkdir()
|
||||
dest_dir = tmp_path / "dest_directory"
|
||||
backupname = tmp_path / "backup_dir"
|
||||
dest_dir.symlink_to(source_dir.resolve())
|
||||
|
||||
if backupname_isfile:
|
||||
backupname.touch()
|
||||
assert backupname.is_file()
|
||||
|
||||
ret = file.directory(
|
||||
name=str(dest_dir),
|
||||
allow_symlink=False,
|
||||
force=True,
|
||||
backupname=str(backupname),
|
||||
test=True,
|
||||
)
|
||||
|
||||
# Confirm None result
|
||||
assert ret.result is None
|
||||
try:
|
||||
# Confirm dest_dir not modified
|
||||
assert salt.utils.path.readlink(str(dest_dir)) == str(source_dir)
|
||||
except OSError:
|
||||
pytest.fail(f"{dest_dir} was modified")
|
||||
|
||||
# Confirm that comment and changes match what we expect
|
||||
assert (
|
||||
ret.comment
|
||||
== f"{dest_dir} would be backed up and replaced with a new directory"
|
||||
)
|
||||
assert ret.changes[str(dest_dir)] == {"directory": "new"}
|
||||
assert ret.changes["backup"] == f"{dest_dir} would be renamed to {backupname}"
|
||||
|
||||
if backupname_isfile:
|
||||
assert ret.changes["forced"] == (
|
||||
f"Existing file at backup path {backupname} would be removed"
|
||||
)
|
||||
else:
|
||||
assert "forced" not in ret.changes
|
||||
|
|
|
@ -78,6 +78,7 @@ def test_update(bucket, s3):
|
|||
"top.sls": {"content": yaml.dump({"base": {"*": ["foo"]}})},
|
||||
"foo.sls": {"content": yaml.dump({"nginx": {"pkg.installed": []}})},
|
||||
"files/nginx.conf": {"content": "server {}"},
|
||||
"files/conf.d/foo.conf": {"content": "server {}"},
|
||||
}
|
||||
|
||||
make_keys(bucket, s3, keys)
|
||||
|
@ -90,6 +91,41 @@ def test_update(bucket, s3):
|
|||
s3fs.update()
|
||||
verify_cache(bucket, keys)
|
||||
|
||||
# verify that when files get deleted from s3, they also get deleted in
|
||||
# the local cache
|
||||
delete_file = "files/nginx.conf"
|
||||
del keys[delete_file]
|
||||
s3.delete_object(Bucket=bucket, Key=delete_file)
|
||||
|
||||
s3fs.update()
|
||||
verify_cache(bucket, keys)
|
||||
|
||||
cache_file = s3fs._get_cached_file_name(bucket, "base", delete_file)
|
||||
assert not os.path.exists(cache_file)
|
||||
|
||||
# we want empty directories to get deleted from the local cache
|
||||
|
||||
# after this one, `files` should still exist
|
||||
files_dir = os.path.dirname(cache_file)
|
||||
assert os.path.exists(files_dir)
|
||||
|
||||
# but after the last file is deleted, the directory and any parents
|
||||
# should be deleted too
|
||||
delete_file = "files/conf.d/foo.conf"
|
||||
del keys[delete_file]
|
||||
s3.delete_object(Bucket=bucket, Key=delete_file)
|
||||
|
||||
s3fs.update()
|
||||
verify_cache(bucket, keys)
|
||||
|
||||
cache_file = s3fs._get_cached_file_name(bucket, "base", delete_file)
|
||||
assert not os.path.exists(cache_file)
|
||||
|
||||
# after this, `files/conf.d` and `files` should be deleted
|
||||
conf_d_dir = os.path.dirname(cache_file)
|
||||
assert not os.path.exists(conf_d_dir)
|
||||
assert not os.path.exists(files_dir)
|
||||
|
||||
|
||||
@pytest.mark.skip_on_fips_enabled_platform
|
||||
def test_s3_hash(bucket, s3):
|
||||
|
@ -124,8 +160,7 @@ def test_s3_hash(bucket, s3):
|
|||
@pytest.mark.skip_on_fips_enabled_platform
|
||||
def test_cache_round_trip(bucket):
|
||||
metadata = {"foo": "bar"}
|
||||
cache_file = s3fs._get_cached_file_name(bucket, "base", "somefile")
|
||||
|
||||
cache_file = s3fs._get_buckets_cache_filename()
|
||||
s3fs._write_buckets_cache_file(metadata, cache_file)
|
||||
assert s3fs._read_buckets_cache_file(cache_file) == metadata
|
||||
|
||||
|
|
|
@ -2329,3 +2329,39 @@ def test_latest_version_calls_aptcache_once_per_run():
|
|||
ret = aptpkg.latest_version("sudo", "unzip", refresh=False)
|
||||
mock_apt_cache.assert_called_once()
|
||||
assert ret == {"sudo": "6.0-23+deb10u3", "unzip": ""}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"oneline,result",
|
||||
(
|
||||
(
|
||||
"deb [signed-by=/etc/apt/keyrings/example.key arch=amd64] https://example.com/pub/repos/apt xenial main",
|
||||
{
|
||||
"signedby": {
|
||||
"full": "signed-by=/etc/apt/keyrings/example.key",
|
||||
"value": "/etc/apt/keyrings/example.key",
|
||||
},
|
||||
"arch": {"full": "arch=amd64", "value": ["amd64"]},
|
||||
},
|
||||
),
|
||||
(
|
||||
"deb [arch=amd64 signed-by=/etc/apt/keyrings/example.key] https://example.com/pub/repos/apt xenial main",
|
||||
{
|
||||
"arch": {"full": "arch=amd64", "value": ["amd64"]},
|
||||
"signedby": {
|
||||
"full": "signed-by=/etc/apt/keyrings/example.key",
|
||||
"value": "/etc/apt/keyrings/example.key",
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
"deb [arch=amd64] https://example.com/pub/repos/apt xenial main",
|
||||
{
|
||||
"arch": {"full": "arch=amd64", "value": ["amd64"]},
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
def test__get_opts(oneline, result):
|
||||
ret = aptpkg._get_opts(oneline)
|
||||
assert ret == result
|
||||
|
|
681
tests/pytests/unit/modules/test_textfsm_mod.py
Normal file
681
tests/pytests/unit/modules/test_textfsm_mod.py
Normal file
|
@ -0,0 +1,681 @@
|
|||
"""
|
||||
:codeauthor: Gareth J. Greenaway <ggreenaway@vmware.com>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import salt.modules.textfsm_mod as textfsm_mod
|
||||
from tests.support.mock import MagicMock, mock_open, patch
|
||||
|
||||
textfsm = pytest.importorskip(
|
||||
"textfsm", reason="Install textfsm to be able to run this test."
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def configure_loader_modules():
|
||||
return {textfsm_mod: {"__opts__": {}}}
|
||||
|
||||
|
||||
def test_dunder_virtual():
|
||||
"""
|
||||
Test __virtual__
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_TEXTFSM", False):
|
||||
ret = textfsm_mod.__virtual__()
|
||||
assert ret == (
|
||||
False,
|
||||
"The textfsm execution module failed to load: requires the textfsm library.",
|
||||
)
|
||||
|
||||
|
||||
def test_extract_cache_file_false():
|
||||
"""
|
||||
Test extract
|
||||
"""
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__, {"cp.cache_file": MagicMock(return_value=False)}
|
||||
):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template",
|
||||
raw_text_file="s3://junos_ver.txt",
|
||||
)
|
||||
assert not ret["result"]
|
||||
assert ret["out"] is None
|
||||
assert (
|
||||
ret["comment"]
|
||||
== "Unable to read the TextFSM template from salt://textfsm/juniper_version_template"
|
||||
)
|
||||
|
||||
|
||||
def test_extract_cache_file_valid():
|
||||
"""
|
||||
Test extract
|
||||
"""
|
||||
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{
|
||||
"cp.cache_file": MagicMock(
|
||||
return_value="/path/to/cache/juniper_version_template"
|
||||
)
|
||||
},
|
||||
):
|
||||
|
||||
textfsm_template = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Value Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
raw_text = """Hostname: router.abc
|
||||
Model: mx960
|
||||
JUNOS Base OS boot [9.1S3.5]
|
||||
JUNOS Base OS Software Suite [9.1S3.5]
|
||||
JUNOS Kernel Software Suite [9.1S3.5]
|
||||
JUNOS Crypto Software Suite [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5]
|
||||
JUNOS Online Documentation [9.1S3.5]
|
||||
JUNOS Routing Software Suite [9.1S3.5]"""
|
||||
|
||||
with patch("salt.utils.files.fopen", mock_open(read_data=textfsm_template)):
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=raw_text)},
|
||||
):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template",
|
||||
raw_text_file="s3://junos_ver.txt",
|
||||
)
|
||||
assert ret == {
|
||||
"result": True,
|
||||
"comment": "",
|
||||
"out": [
|
||||
{
|
||||
"chassis": "",
|
||||
"model": "mx960",
|
||||
"boot": "9.1S3.5",
|
||||
"base": "9.1S3.5",
|
||||
"kernel": "9.1S3.5",
|
||||
"crypto": "9.1S3.5",
|
||||
"documentation": "9.1S3.5",
|
||||
"routing": "9.1S3.5",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
with patch("salt.utils.files.fopen", mock_open(read_data=textfsm_template)):
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=raw_text)},
|
||||
):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template", raw_text=raw_text
|
||||
)
|
||||
assert ret == {
|
||||
"result": True,
|
||||
"comment": "",
|
||||
"out": [
|
||||
{
|
||||
"chassis": "",
|
||||
"model": "mx960",
|
||||
"boot": "9.1S3.5",
|
||||
"base": "9.1S3.5",
|
||||
"kernel": "9.1S3.5",
|
||||
"crypto": "9.1S3.5",
|
||||
"documentation": "9.1S3.5",
|
||||
"routing": "9.1S3.5",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_extract_cache_file_raw_text_get_file_str_false():
|
||||
"""
|
||||
Test extract
|
||||
"""
|
||||
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{
|
||||
"cp.cache_file": MagicMock(
|
||||
return_value="/path/to/cache/juniper_version_template"
|
||||
)
|
||||
},
|
||||
):
|
||||
|
||||
textfsm_template = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Value Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
raw_text = """Hostname: router.abc
|
||||
Model: mx960
|
||||
JUNOS Base OS boot [9.1S3.5]
|
||||
JUNOS Base OS Software Suite [9.1S3.5]
|
||||
JUNOS Kernel Software Suite [9.1S3.5]
|
||||
JUNOS Crypto Software Suite [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5]
|
||||
JUNOS Online Documentation [9.1S3.5]
|
||||
JUNOS Routing Software Suite [9.1S3.5]"""
|
||||
|
||||
with patch("salt.utils.files.fopen", mock_open(read_data=textfsm_template)):
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__, {"cp.get_file_str": MagicMock(return_value=False)}
|
||||
):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template",
|
||||
raw_text_file="s3://junos_ver.txt",
|
||||
)
|
||||
assert ret == {
|
||||
"result": False,
|
||||
"comment": "Unable to read from s3://junos_ver.txt. Please specify a valid input file or text.",
|
||||
"out": None,
|
||||
}
|
||||
|
||||
|
||||
def test_extract_cache_file_raw_text_exception():
|
||||
"""
|
||||
Test extract
|
||||
"""
|
||||
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{
|
||||
"cp.cache_file": MagicMock(
|
||||
return_value="/path/to/cache/juniper_version_template"
|
||||
)
|
||||
},
|
||||
):
|
||||
|
||||
textfsm_template = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Xalue Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
raw_text = """Hostname: router.abc
|
||||
Model: mx960
|
||||
JUNOS Base OS boot [9.1S3.5]
|
||||
JUNOS Base OS Software Suite [9.1S3.5]
|
||||
JUNOS Kernel Software Suite [9.1S3.5]
|
||||
JUNOS Crypto Software Suite [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5]
|
||||
JUNOS Online Documentation [9.1S3.5]
|
||||
JUNOS Routing Software Suite [9.1S3.5]"""
|
||||
|
||||
with patch("salt.utils.files.fopen", mock_open(read_data=textfsm_template)):
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__, {"cp.get_file_str": MagicMock(return_value=False)}
|
||||
):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template",
|
||||
raw_text_file="s3://junos_ver.txt",
|
||||
)
|
||||
|
||||
assert not ret["result"]
|
||||
assert "Unable to parse the TextFSM template from " in ret["comment"]
|
||||
assert ret["out"] is None
|
||||
|
||||
|
||||
def test_extract_cache_file_raw_text_false():
|
||||
"""
|
||||
Test extract
|
||||
"""
|
||||
|
||||
with patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{
|
||||
"cp.cache_file": MagicMock(
|
||||
return_value="/path/to/cache/juniper_version_template"
|
||||
)
|
||||
},
|
||||
):
|
||||
|
||||
textfsm_template = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Value Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
with patch("salt.utils.files.fopen", mock_open(read_data=textfsm_template)):
|
||||
ret = textfsm_mod.extract(
|
||||
"salt://textfsm/juniper_version_template", raw_text=""
|
||||
)
|
||||
assert ret == {
|
||||
"result": False,
|
||||
"comment": "Please specify a valid input file or text.",
|
||||
"out": None,
|
||||
}
|
||||
|
||||
|
||||
def test_index_not_clitable():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", False):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="Juniper",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "TextFSM does not seem that has clitable embedded.",
|
||||
}
|
||||
|
||||
|
||||
def test_index_no_textsm_path():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="Juniper",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "No TextFSM templates path specified. Please configure in opts/pillar/function args.",
|
||||
}
|
||||
|
||||
|
||||
def test_index_no_platform():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "No platform specified, no platform grain identifier configured.",
|
||||
}
|
||||
|
||||
|
||||
def test_index_no_platform_name_grains():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True):
|
||||
with patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "Unable to identify the platform name using the textfsm_platform_grain grain.",
|
||||
}
|
||||
|
||||
|
||||
def test_index_platform_name_grains_no_cachedir():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True), patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
), patch.dict(
|
||||
textfsm_mod.__grains__,
|
||||
{"textfsm_platform_grain": "textfsm_platform_grain"},
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.cache_dir": MagicMock(return_value=False)},
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "Unable to fetch from salt://textfsm/. Is the TextFSM path correctly specified?",
|
||||
}
|
||||
|
||||
|
||||
def test_index_platform_name_grains_output_false():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
mock_open_index = """
|
||||
Template, Hostname, Vendor, Command
|
||||
juniper_version_template, .*, Juniper, sh[[ow]] ve[[rsion]]"""
|
||||
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True), patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
), patch.dict(
|
||||
textfsm_mod.__grains__,
|
||||
{"textfsm_platform_grain": "textfsm_platform_grain"},
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.cache_dir": MagicMock(return_value="/path/to/cache/")},
|
||||
), patch.object(
|
||||
textfsm_mod.clitable,
|
||||
"open",
|
||||
mock_open(read_data=mock_open_index),
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=False)},
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "Unable to read from salt://textfsm/juniper_version_example. Please specify a valid file or text.",
|
||||
}
|
||||
|
||||
|
||||
def test_index_platform_name_grains_no_output_specified():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
mock_open_index = """
|
||||
Template, Hostname, Vendor, Command
|
||||
juniper_version_template, .*, Juniper, sh[[ow]] ve[[rsion]]"""
|
||||
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True), patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
), patch.dict(
|
||||
textfsm_mod.__grains__,
|
||||
{"textfsm_platform_grain": "textfsm_platform_grain"},
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.cache_dir": MagicMock(return_value="/path/to/cache/")},
|
||||
), patch.object(
|
||||
textfsm.clitable, "open", mock_open(read_data=mock_open_index)
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=False)},
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "Please specify a valid output text or file",
|
||||
}
|
||||
|
||||
|
||||
def test_index_platform_name_grains_output_specified():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
mock_open_index = """
|
||||
Template, Hostname, Vendor, Command
|
||||
juniper_version_template, .*, Juniper, sh[[ow]] ve[[rsion]]"""
|
||||
|
||||
juniper_version_template_one = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Value Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
juniper_version_template_two = r"""Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
output_text = """
|
||||
Hostname: router.abc
|
||||
Model: mx960
|
||||
JUNOS Base OS boot [9.1S3.5]
|
||||
JUNOS Base OS Software Suite [9.1S3.5]
|
||||
JUNOS Kernel Software Suite [9.1S3.5]
|
||||
JUNOS Crypto Software Suite [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5]
|
||||
JUNOS Online Documentation [9.1S3.5]
|
||||
JUNOS Routing Software Suite [9.1S3.5]"""
|
||||
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True), patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
), patch.dict(
|
||||
textfsm_mod.__grains__,
|
||||
{"textfsm_platform_grain": "textfsm_platform_grain"},
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.cache_dir": MagicMock(return_value="/path/to/cache/")},
|
||||
):
|
||||
mock_read_data = {
|
||||
"/index": [mock_open_index],
|
||||
"/juniper_version_template": [
|
||||
juniper_version_template_one,
|
||||
juniper_version_template_two,
|
||||
],
|
||||
}
|
||||
with patch.object(
|
||||
textfsm.clitable, "open", mock_open(read_data=mock_read_data)
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=output_text)},
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sh ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
assert ret == {
|
||||
"out": [
|
||||
{
|
||||
"chassis": "",
|
||||
"model": "mx960",
|
||||
"boot": "9.1S3.5",
|
||||
"base": "9.1S3.5",
|
||||
"kernel": "9.1S3.5",
|
||||
"crypto": "9.1S3.5",
|
||||
"documentation": "9.1S3.5",
|
||||
"routing": "9.1S3.5",
|
||||
}
|
||||
],
|
||||
"result": True,
|
||||
"comment": "",
|
||||
}
|
||||
|
||||
|
||||
def test_index_platform_name_grains_output_specified_no_attribute():
|
||||
"""
|
||||
Test index
|
||||
"""
|
||||
mock_open_index = """
|
||||
Template, Hostname, Vendor, Command
|
||||
juniper_version_template, .*, Juniper, sh[[ow]] ve[[rsion]]"""
|
||||
|
||||
juniper_version_template_one = r"""Value Chassis (\S+)
|
||||
Value Required Model (\S+)
|
||||
Value Boot (.*)
|
||||
Value Base (.*)
|
||||
Value Kernel (.*)
|
||||
Value Crypto (.*)
|
||||
Value Documentation (.*)
|
||||
Value Routing (.*)
|
||||
|
||||
Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
juniper_version_template_two = r"""Start
|
||||
# Support multiple chassis systems.
|
||||
^\S+:$$ -> Continue.Record
|
||||
^${Chassis}:$$
|
||||
^Model: ${Model}
|
||||
^JUNOS Base OS boot \[${Boot}\]
|
||||
^JUNOS Software Release \[${Base}\]
|
||||
^JUNOS Base OS Software Suite \[${Base}\]
|
||||
^JUNOS Kernel Software Suite \[${Kernel}\]
|
||||
^JUNOS Crypto Software Suite \[${Crypto}\]
|
||||
^JUNOS Online Documentation \[${Documentation}\]
|
||||
^JUNOS Routing Software Suite \[${Routing}\]"""
|
||||
|
||||
output_text = """
|
||||
Hostname: router.abc
|
||||
Model: mx960
|
||||
JUNOS Base OS boot [9.1S3.5]
|
||||
JUNOS Base OS Software Suite [9.1S3.5]
|
||||
JUNOS Kernel Software Suite [9.1S3.5]
|
||||
JUNOS Crypto Software Suite [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (M/T Common) [9.1S3.5]
|
||||
JUNOS Packet Forwarding Engine Support (MX Common) [9.1S3.5]
|
||||
JUNOS Online Documentation [9.1S3.5]
|
||||
JUNOS Routing Software Suite [9.1S3.5]"""
|
||||
|
||||
with patch.object(textfsm_mod, "HAS_CLITABLE", True), patch.dict(
|
||||
textfsm_mod.__opts__, {"textfsm_platform_grain": "textfsm_platform_grain"}
|
||||
), patch.dict(
|
||||
textfsm_mod.__grains__,
|
||||
{"textfsm_platform_grain": "textfsm_platform_grain"},
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.cache_dir": MagicMock(return_value="/path/to/cache/")},
|
||||
):
|
||||
mock_read_data = {
|
||||
"/index": [mock_open_index],
|
||||
"/juniper_version_template": [
|
||||
juniper_version_template_one,
|
||||
juniper_version_template_two,
|
||||
],
|
||||
}
|
||||
with patch.object(
|
||||
textfsm.clitable, "open", mock_open(read_data=mock_read_data)
|
||||
), patch.dict(
|
||||
textfsm_mod.__salt__,
|
||||
{"cp.get_file_str": MagicMock(return_value=output_text)},
|
||||
):
|
||||
ret = textfsm_mod.index(
|
||||
command="sr ver",
|
||||
platform="",
|
||||
output_file="salt://textfsm/juniper_version_example",
|
||||
textfsm_path="salt://textfsm/",
|
||||
)
|
||||
|
||||
assert ret == {
|
||||
"out": None,
|
||||
"result": False,
|
||||
"comment": "Unable to process the output: No template found for attributes: \"{'Command': 'sr ver', 'Platform': 'textfsm_platform_grain'}\"",
|
||||
}
|
|
@ -755,3 +755,21 @@ def test__reverse_cmp_pkg_versions(v1, v2, expected):
|
|||
assert result == expected, "cmp({}, {}) should be {}, got {}".format(
|
||||
v1, v2, expected, result
|
||||
)
|
||||
|
||||
|
||||
def test__repo_process_pkg_sls():
|
||||
patch_render = patch("salt.loader.render")
|
||||
patch_opts = patch.dict(win_pkg.__opts__, {"renderer": None})
|
||||
patch_compile = patch("salt.template.compile_template", return_value="junk")
|
||||
with patch_opts, patch_render as render, patch_compile as test:
|
||||
ret = win_pkg._repo_process_pkg_sls(
|
||||
filename="junk",
|
||||
short_path_name="junk",
|
||||
ret={},
|
||||
successful_verbose=False,
|
||||
saltenv="spongebob",
|
||||
)
|
||||
assert ret is False
|
||||
test.assert_called_once_with(
|
||||
"junk", render(), None, "", "", saltenv="spongebob"
|
||||
)
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
"""
|
||||
Test cases for salt.modules.win_pki
|
||||
Test cases for salt.modules.win_pki
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import salt.modules.win_pki as win_pki
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from tests.support.mock import MagicMock, patch
|
||||
|
||||
|
||||
|
@ -181,3 +182,46 @@ def test_remove_cert(thumbprint, certs):
|
|||
"salt.modules.win_pki.get_certs", MagicMock(return_value=certs)
|
||||
):
|
||||
assert win_pki.remove_cert(thumbprint=thumbprint[::-1])
|
||||
|
||||
|
||||
def test__cmd_run():
|
||||
"""
|
||||
Test the _cmd_run function
|
||||
"""
|
||||
mock_run = MagicMock(
|
||||
return_value={"retcode": 0, "stderr": "", "stdout": "some result"}
|
||||
)
|
||||
with patch.dict(win_pki.__salt__, {"cmd.run_all": mock_run}):
|
||||
result = win_pki._cmd_run(cmd="command")
|
||||
assert result == "some result"
|
||||
|
||||
|
||||
def test__cmd_run_as_json():
|
||||
mock_run = MagicMock(
|
||||
return_value={"retcode": 0, "stderr": "", "stdout": '{"key": "value"}'}
|
||||
)
|
||||
with patch.dict(win_pki.__salt__, {"cmd.run_all": mock_run}):
|
||||
result = win_pki._cmd_run(cmd="command", as_json=True)
|
||||
assert result == {"key": "value"}
|
||||
|
||||
|
||||
def test__cmd_run_stderr():
|
||||
mock_run = MagicMock(
|
||||
return_value={"retcode": 0, "stderr": "some error", "stdout": ""}
|
||||
)
|
||||
with patch.dict(win_pki.__salt__, {"cmd.run_all": mock_run}):
|
||||
with pytest.raises(CommandExecutionError) as exc_info:
|
||||
win_pki._cmd_run(cmd="command")
|
||||
expected = "Unable to execute command: command\nError: some error"
|
||||
assert exc_info.value.args[0] == expected
|
||||
|
||||
|
||||
def test__cmd_run_bad_json():
|
||||
mock_run = MagicMock(
|
||||
return_value={"retcode": 0, "stderr": "", "stdout": "not : valid\njson"}
|
||||
)
|
||||
with patch.dict(win_pki.__salt__, {"cmd.run_all": mock_run}):
|
||||
with pytest.raises(CommandExecutionError) as exc_info:
|
||||
win_pki._cmd_run(cmd="command", as_json=True)
|
||||
expected = "Unable to parse return data as JSON:\nnot : valid\njson"
|
||||
assert exc_info.value.args[0] == expected
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import platform
|
||||
|
||||
import pytest
|
||||
|
||||
import salt.utils.win_functions as win_functions
|
||||
|
@ -6,6 +8,11 @@ from tests.support.mock import MagicMock, patch
|
|||
HAS_WIN32 = False
|
||||
HAS_PYWIN = False
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.windows_whitelisted,
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
]
|
||||
|
||||
try:
|
||||
import win32net
|
||||
|
||||
|
@ -32,7 +39,6 @@ except ImportError:
|
|||
# Test cases for salt.utils.win_functions.
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_escape_argument_simple():
|
||||
"""
|
||||
Test to make sure we encode simple arguments correctly
|
||||
|
@ -41,7 +47,6 @@ def test_escape_argument_simple():
|
|||
assert encoded == "simple"
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_escape_argument_with_space():
|
||||
"""
|
||||
Test to make sure we encode arguments containing spaces correctly
|
||||
|
@ -50,7 +55,6 @@ def test_escape_argument_with_space():
|
|||
assert encoded == '^"with space^"'
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_escape_argument_simple_path():
|
||||
"""
|
||||
Test to make sure we encode simple path arguments correctly
|
||||
|
@ -59,7 +63,6 @@ def test_escape_argument_simple_path():
|
|||
assert encoded == "C:\\some\\path"
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_escape_argument_path_with_space():
|
||||
"""
|
||||
Test to make sure we encode path arguments containing spaces correctly
|
||||
|
@ -68,7 +71,6 @@ def test_escape_argument_path_with_space():
|
|||
assert encoded == '^"C:\\Some Path\\With Spaces^"'
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_broadcast_setting_change():
|
||||
"""
|
||||
Test to rehash the Environment variables
|
||||
|
@ -76,14 +78,12 @@ def test_broadcast_setting_change():
|
|||
assert win_functions.broadcast_setting_change()
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_get_user_groups():
|
||||
groups = ["Administrators", "Users"]
|
||||
with patch("win32net.NetUserGetLocalGroups", return_value=groups):
|
||||
assert win_functions.get_user_groups("Administrator") == groups
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_get_user_groups_sid():
|
||||
groups = ["Administrators", "Users"]
|
||||
expected = ["S-1-5-32-544", "S-1-5-32-545"]
|
||||
|
@ -91,14 +91,12 @@ def test_get_user_groups_sid():
|
|||
assert win_functions.get_user_groups("Administrator", sid=True) == expected
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
def test_get_user_groups_system():
|
||||
groups = ["SYSTEM"]
|
||||
with patch("win32net.NetUserGetLocalGroups", return_value=groups):
|
||||
assert win_functions.get_user_groups("SYSTEM") == groups
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_WIN32, reason="Requires Win32 libraries")
|
||||
def test_get_user_groups_unavailable_dc():
|
||||
groups = ["Administrators", "Users"]
|
||||
|
@ -109,7 +107,6 @@ def test_get_user_groups_unavailable_dc():
|
|||
assert win_functions.get_user_groups("Administrator") == groups
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_WIN32, reason="Requires Win32 libraries")
|
||||
def test_get_user_groups_unknown_dc():
|
||||
groups = ["Administrators", "Users"]
|
||||
|
@ -120,7 +117,6 @@ def test_get_user_groups_unknown_dc():
|
|||
assert win_functions.get_user_groups("Administrator") == groups
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_WIN32, reason="Requires Win32 libraries")
|
||||
def test_get_user_groups_missing_permission():
|
||||
groups = ["Administrators", "Users"]
|
||||
|
@ -131,7 +127,6 @@ def test_get_user_groups_missing_permission():
|
|||
assert win_functions.get_user_groups("Administrator") == groups
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_WIN32, reason="Requires Win32 libraries")
|
||||
def test_get_user_groups_error():
|
||||
win_error = WinError()
|
||||
|
@ -142,7 +137,6 @@ def test_get_user_groups_error():
|
|||
win_functions.get_user_groups("Administrator")
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_PYWIN, reason="Requires pywintypes libraries")
|
||||
def test_get_user_groups_local_pywin_error():
|
||||
win_error = PyWinError()
|
||||
|
@ -153,7 +147,6 @@ def test_get_user_groups_local_pywin_error():
|
|||
win_functions.get_user_groups("Administrator")
|
||||
|
||||
|
||||
@pytest.mark.skip_unless_on_windows(reason="Test is only applicable to Windows.")
|
||||
@pytest.mark.skipif(not HAS_PYWIN, reason="Requires pywintypes libraries")
|
||||
def test_get_user_groups_pywin_error():
|
||||
win_error = PyWinError()
|
||||
|
@ -163,3 +156,27 @@ def test_get_user_groups_pywin_error():
|
|||
with patch("win32net.NetUserGetGroups", side_effect=mock_error):
|
||||
with pytest.raises(PyWinError):
|
||||
win_functions.get_user_groups("Administrator")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYWIN, reason="Requires pywintypes libraries")
|
||||
def test_get_sam_name_lookup_fails():
|
||||
win_error = PyWinError()
|
||||
mock_error = MagicMock(side_effect=win_error)
|
||||
with patch("win32security.LookupAccountName", side_effect=mock_error):
|
||||
expected = "\\".join([platform.node()[:15].upper(), "junk"])
|
||||
result = win_functions.get_sam_name("junk")
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYWIN, reason="Requires pywintypes libraries")
|
||||
def test_get_sam_name_everyone():
|
||||
expected = "Everyone"
|
||||
result = win_functions.get_sam_name("Everyone")
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYWIN, reason="Requires pywintypes libraries")
|
||||
def test_get_sam_name():
|
||||
expected = "\\".join([platform.node()[:15], "Administrator"])
|
||||
result = win_functions.get_sam_name("Administrator")
|
||||
assert result == expected
|
||||
|
|
|
@ -71,7 +71,7 @@ class MockFH:
|
|||
self.write = Mock(side_effect=self._write)
|
||||
self.writelines = Mock(side_effect=self._writelines)
|
||||
self.close = Mock()
|
||||
self.seek = Mock()
|
||||
self.seek = Mock(side_effect=self._seek)
|
||||
self.__loc = 0
|
||||
self.__read_data_ok = False
|
||||
|
||||
|
@ -219,6 +219,14 @@ class MockFH:
|
|||
def __exit__(self, exc_type, exc_val, exc_tb): # pylint: disable=unused-argument
|
||||
pass
|
||||
|
||||
# For some reason this gets called with additional args on Windows when
|
||||
# running the following test:
|
||||
# tests/pytests/unit/beacons/test_log_beacon.py::test_log_match
|
||||
# Let's just absorb them with *args
|
||||
def _seek(self, pos=0, *args):
|
||||
self.__loc = pos
|
||||
self.read_data_iter = self._iterate_read_data(self.read_data)
|
||||
|
||||
|
||||
class MockCall:
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -316,7 +316,7 @@ def debian(
|
|||
_rpm_distro_info = {
|
||||
"amazon": ["2", "2023"],
|
||||
"redhat": ["7", "8", "9"],
|
||||
"fedora": ["36", "37", "38"],
|
||||
"fedora": ["36", "37", "38", "39"],
|
||||
"photon": ["3", "4", "5"],
|
||||
}
|
||||
|
||||
|
|
|
@ -749,7 +749,6 @@ MISSING_EXAMPLES = {
|
|||
"delete_advanced_configs",
|
||||
"get_vm",
|
||||
],
|
||||
"salt/modules/win_pkg.py": ["get_package_info"],
|
||||
"salt/modules/win_timezone.py": ["zone_compare"],
|
||||
"salt/modules/zk_concurrency.py": [
|
||||
"lock",
|
||||
|
|
|
@ -226,7 +226,11 @@ def download_file(
|
|||
headers: dict[str, str] | None = None,
|
||||
) -> pathlib.Path:
|
||||
ctx.info(f"Downloading {dest.name!r} @ {url} ...")
|
||||
curl = shutil.which("curl")
|
||||
if sys.platform == "win32":
|
||||
# We don't want to use curl on Windows, it doesn't work
|
||||
curl = None
|
||||
else:
|
||||
curl = shutil.which("curl")
|
||||
if curl is not None:
|
||||
command = [curl, "-sS", "-L"]
|
||||
if headers:
|
||||
|
|
Loading…
Add table
Reference in a new issue