zosmfrestapi 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/PKG-INFO +18 -24
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/README.md +15 -23
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/pyproject.toml +51 -4
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/__init__.py +5 -3
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/client.py +2 -1
- zosmfrestapi-0.2.0/zosmfrestapi/models/__init__.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/models/software.py +1 -1
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/models/software_updates.py +1 -4
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/models/topology.py +1 -4
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/models/workflow.py +1 -3
- zosmfrestapi-0.2.0/zosmfrestapi/py.typed +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/__init__.py +21 -23
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/authentication.py +7 -8
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/software.py +6 -18
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/software_updates.py +7 -21
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/workflow.py +2 -6
- zosmfrestapi-0.2.0/zosmfrestapi/testing/__init__.py +11 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/authentication.py +48 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/helpers.py +9 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/software.py +392 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/software_updates.py +281 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/topology.py +19 -0
- zosmfrestapi-0.2.0/zosmfrestapi/testing/workflows.py +320 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/utils.py +1 -3
- zosmfrestapi-0.2.0/zosmfrestapi/zcrypto.py +55 -0
- zosmfrestapi-0.2.0/zosmfrestapi/zcrypto_client.py +68 -0
- zosmfrestapi-0.1.0/zosmfrestapi/models/__init__.py +0 -1
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/LICENSE +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/exceptions/__init__.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/exceptions/authentication.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/exceptions/dataset.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/exceptions/software.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/exceptions/workflow.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/models/authentication.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/dataset.py +0 -0
- {zosmfrestapi-0.1.0 → zosmfrestapi-0.2.0}/zosmfrestapi/services/topology.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zosmfrestapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Python wrapper for the z/OSMF REST API
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,49 +13,43 @@ Classifier: Programming Language :: Python :: 3
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
15
15
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Provides-Extra: zcrypto
|
|
16
17
|
Requires-Dist: dacite (>=1.9.2,<2.0.0)
|
|
17
18
|
Requires-Dist: httpx2 (>=2.12.0,<3.0.0)
|
|
18
19
|
Requires-Dist: pendulum (>=3.2.0,<4.0.0)
|
|
20
|
+
Requires-Dist: py_zcrypto ; extra == "zcrypto"
|
|
19
21
|
Project-URL: Homepage, https://sr.ht/~ocurero/zosmfrestapi/
|
|
20
22
|
Project-URL: PyPI, https://pypi.org/project/zosmfrestapi/
|
|
21
23
|
Project-URL: Repository, https://sr.ht/~ocurero/zosmfrestapi/
|
|
22
24
|
Description-Content-Type: text/markdown
|
|
23
25
|
|
|
24
|
-
A Python wrapper for the z/OSMF© REST API
|
|
25
|
-
=========================================
|
|
26
|
+
# A Python wrapper for the z/OSMF© REST API
|
|
26
27
|
|
|
27
|
-
[](https://www.repostatus.org/#active)
|
|
28
|
+
[](https://www.repostatus.org/#active)
|
|
29
|
+
[](https://builds.sr.ht/~ocurero/zosmfrestapi/commits/main/.build.yml?)
|
|
30
|
+

|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
[](https://github.com/psf/black)
|
|
28
33
|
|
|
29
34
|
* Open Source: MIT License.
|
|
30
35
|
* Website: <https://sr.ht/~ocurero/zosmfrestapi/>
|
|
31
36
|
* Pypi: <https://pypi.org/project/zosmfrestapi/>
|
|
37
|
+
* Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
38
|
+
* Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
39
|
+
|
|
40
|
+
Key features:
|
|
41
|
+
* **Pure Python**: Package and its dependencies are 100% pure Python (no C extensions required).
|
|
42
|
+
* **Cross-platform**: Works on Windows, Linux, macOS, and z/OS UNIX System Services.
|
|
43
|
+
* **Flexible authentication**: Supports user/password, LTPA and JWT tokens, and mutual TLS (mTLS).
|
|
44
|
+
* **Proxy ready**: Full support for standard HTTP/HTTPS proxies as well as authenticated proxies.
|
|
32
45
|
|
|
33
46
|
|
|
34
47
|
Install
|
|
35
48
|
-------
|
|
36
49
|
|
|
37
50
|
**zosmfrestapi** requires Python 3.12 or newer and access to a z/OSMF server.
|
|
38
|
-
You can check your Python version with:
|
|
39
|
-
|
|
40
|
-
```console
|
|
41
|
-
python --version
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
It is a good idea to use a virtual environment so this package does not affect
|
|
45
|
-
other Python projects. From a terminal, create and activate one in your project
|
|
46
|
-
folder:
|
|
47
|
-
|
|
48
|
-
```console
|
|
49
|
-
# Windows (PowerShell)
|
|
50
|
-
py -3.12 -m venv .venv
|
|
51
|
-
.venv\Scripts\Activate.ps1
|
|
52
|
-
|
|
53
|
-
# macOS or Linux
|
|
54
|
-
python3 -m venv .venv
|
|
55
|
-
source .venv/bin/activate
|
|
56
|
-
```
|
|
57
51
|
|
|
58
|
-
|
|
52
|
+
Install the package from PyPI:
|
|
59
53
|
|
|
60
54
|
```console
|
|
61
55
|
python -m pip install zosmfrestapi
|
|
@@ -1,38 +1,30 @@
|
|
|
1
|
-
A Python wrapper for the z/OSMF© REST API
|
|
2
|
-
=========================================
|
|
1
|
+
# A Python wrapper for the z/OSMF© REST API
|
|
3
2
|
|
|
4
|
-
[](https://www.repostatus.org/#active)
|
|
3
|
+
[](https://www.repostatus.org/#active)
|
|
4
|
+
[](https://builds.sr.ht/~ocurero/zosmfrestapi/commits/main/.build.yml?)
|
|
5
|
+

|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://github.com/psf/black)
|
|
5
8
|
|
|
6
9
|
* Open Source: MIT License.
|
|
7
10
|
* Website: <https://sr.ht/~ocurero/zosmfrestapi/>
|
|
8
11
|
* Pypi: <https://pypi.org/project/zosmfrestapi/>
|
|
12
|
+
* Changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
13
|
+
* Contributing: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
14
|
+
|
|
15
|
+
Key features:
|
|
16
|
+
* **Pure Python**: Package and its dependencies are 100% pure Python (no C extensions required).
|
|
17
|
+
* **Cross-platform**: Works on Windows, Linux, macOS, and z/OS UNIX System Services.
|
|
18
|
+
* **Flexible authentication**: Supports user/password, LTPA and JWT tokens, and mutual TLS (mTLS).
|
|
19
|
+
* **Proxy ready**: Full support for standard HTTP/HTTPS proxies as well as authenticated proxies.
|
|
9
20
|
|
|
10
21
|
|
|
11
22
|
Install
|
|
12
23
|
-------
|
|
13
24
|
|
|
14
25
|
**zosmfrestapi** requires Python 3.12 or newer and access to a z/OSMF server.
|
|
15
|
-
You can check your Python version with:
|
|
16
|
-
|
|
17
|
-
```console
|
|
18
|
-
python --version
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
It is a good idea to use a virtual environment so this package does not affect
|
|
22
|
-
other Python projects. From a terminal, create and activate one in your project
|
|
23
|
-
folder:
|
|
24
|
-
|
|
25
|
-
```console
|
|
26
|
-
# Windows (PowerShell)
|
|
27
|
-
py -3.12 -m venv .venv
|
|
28
|
-
.venv\Scripts\Activate.ps1
|
|
29
|
-
|
|
30
|
-
# macOS or Linux
|
|
31
|
-
python3 -m venv .venv
|
|
32
|
-
source .venv/bin/activate
|
|
33
|
-
```
|
|
34
26
|
|
|
35
|
-
|
|
27
|
+
Install the package from PyPI:
|
|
36
28
|
|
|
37
29
|
```console
|
|
38
30
|
python -m pip install zosmfrestapi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "zosmfrestapi"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A Python wrapper for the z/OSMF REST API"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -27,6 +27,11 @@ Homepage = "https://sr.ht/~ocurero/zosmfrestapi/"
|
|
|
27
27
|
Repository = "https://sr.ht/~ocurero/zosmfrestapi/"
|
|
28
28
|
PyPI = "https://pypi.org/project/zosmfrestapi/"
|
|
29
29
|
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
zcrypto = [
|
|
32
|
+
"py_zcrypto",
|
|
33
|
+
]
|
|
34
|
+
|
|
30
35
|
[dependency-groups]
|
|
31
36
|
dev = [
|
|
32
37
|
"pytest (>=9.1.1,<10.0.0)",
|
|
@@ -34,15 +39,57 @@ dev = [
|
|
|
34
39
|
"pytest-recording (>=0.14.0,<0.15.0)",
|
|
35
40
|
"pytest-httpx2 (>=1.0.0,<2.0.0)",
|
|
36
41
|
"pytest-cov (>=7.1.0,<8.0.0)",
|
|
42
|
+
"flake8 (>=7.5.0,<8.0.0)",
|
|
43
|
+
"flake8-pyproject (>=1.2.4,<2.0.0)",
|
|
44
|
+
"mypy (>=2.4.1,<3.0.0)",
|
|
45
|
+
"poethepoet (>=0.49.0,<0.50.0)",
|
|
46
|
+
"pyright (>=1.1.414,<2.0.0)",
|
|
47
|
+
]
|
|
48
|
+
zcrypto-dev = [
|
|
49
|
+
"cryptography (>=50.0.2,<51.0.0)"
|
|
37
50
|
]
|
|
38
51
|
|
|
39
52
|
[tool.black]
|
|
40
|
-
line-length =
|
|
41
|
-
|
|
53
|
+
line-length = 96
|
|
54
|
+
|
|
55
|
+
[tool.flake8]
|
|
56
|
+
max-line-length = 96
|
|
57
|
+
extend-ignore = ["E203", "W503"]
|
|
58
|
+
per-file-ignores = "tests/*.py:E501"
|
|
42
59
|
|
|
43
60
|
[tool.pytest.ini_options]
|
|
44
61
|
testpaths = ["tests"]
|
|
45
|
-
addopts = ["-ra",
|
|
62
|
+
addopts = ["-ra",
|
|
63
|
+
"--record-mode=none",
|
|
64
|
+
"--cov-fail-under=100",
|
|
65
|
+
"--cov=zosmfrestapi",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
[tool.poe.tasks]
|
|
70
|
+
format = "black ."
|
|
71
|
+
|
|
72
|
+
lint = [
|
|
73
|
+
{ cmd = "black --check ." },
|
|
74
|
+
{ cmd = "flake8" },
|
|
75
|
+
{ cmd = "mypy zosmfrestapi" },
|
|
76
|
+
{ cmd = "pyright --warnings" }
|
|
77
|
+
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
test = "pytest"
|
|
81
|
+
|
|
82
|
+
check = [
|
|
83
|
+
{ cmd = "black --check ." },
|
|
84
|
+
{ cmd = "flake8 zosmfrestapi" },
|
|
85
|
+
{ cmd = "pyright --warnings" },
|
|
86
|
+
{ cmd = "mypy zosmfrestapi" },
|
|
87
|
+
{ cmd = "pytest" }
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
[[tool.mypy.overrides]]
|
|
91
|
+
module = ["OpenSSL.*", "py_zcrypto.*"]
|
|
92
|
+
ignore_missing_imports = true
|
|
46
93
|
|
|
47
94
|
[build-system]
|
|
48
95
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Synchronous Python client for the z/OSMF REST API.
|
|
2
2
|
|
|
3
|
-
The package root re-exports :class:`ZosmfClient`,
|
|
4
|
-
exceptions. Create one client per server/configuration and
|
|
5
|
-
manager to close the shared HTTP session when finished.
|
|
3
|
+
The package root re-exports :class:`ZosmfClient`, :class:`ZosmfZcryptoClient`,
|
|
4
|
+
response models, and public exceptions. Create one client per server/configuration and
|
|
5
|
+
use it as a context manager to close the shared HTTP session when finished.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from zosmfrestapi.client import ZosmfClient
|
|
@@ -48,9 +48,11 @@ from zosmfrestapi.models.workflow import (
|
|
|
48
48
|
WorkflowItem,
|
|
49
49
|
WorkflowListItem,
|
|
50
50
|
)
|
|
51
|
+
from zosmfrestapi.zcrypto_client import ZosmfZcryptoClient
|
|
51
52
|
|
|
52
53
|
__all__ = [
|
|
53
54
|
"ZosmfClient",
|
|
55
|
+
"ZosmfZcryptoClient",
|
|
54
56
|
"LoginOk",
|
|
55
57
|
"ZosmfDatasetNotFoundError",
|
|
56
58
|
"ZosmfError",
|
|
@@ -41,6 +41,7 @@ class ZosmfClient(
|
|
|
41
41
|
|
|
42
42
|
with ZosmfClient("https://zosmf.example",
|
|
43
43
|
username="demouser", password="DEMOPASS",
|
|
44
|
-
cert=("/path/to/cert", "/path/to/key")
|
|
44
|
+
cert=("/path/to/cert", "/path/to/key"),
|
|
45
|
+
key_password="secret") as client:
|
|
45
46
|
systems = client.get_systems()
|
|
46
47
|
"""
|
|
File without changes
|
|
File without changes
|
|
@@ -4,7 +4,6 @@ from types import TracebackType
|
|
|
4
4
|
from typing import Literal, Self
|
|
5
5
|
|
|
6
6
|
import httpx2
|
|
7
|
-
|
|
8
7
|
from zosmfrestapi.exceptions import ZosmfAuthError, ZosmfError
|
|
9
8
|
|
|
10
9
|
logger = logging.getLogger(__name__)
|
|
@@ -25,6 +24,7 @@ class ZosmfBase:
|
|
|
25
24
|
username: str | None = None,
|
|
26
25
|
password: str | None = None,
|
|
27
26
|
cert: str | tuple[str, str] | None = None,
|
|
27
|
+
key_password: str | None = None,
|
|
28
28
|
ltpa_token: str | None = None,
|
|
29
29
|
jwt_token: str | None = None,
|
|
30
30
|
jwt_cookie_name: str = "jwtToken",
|
|
@@ -49,6 +49,8 @@ class ZosmfBase:
|
|
|
49
49
|
PEM file containing both the certificate and private key, or
|
|
50
50
|
a ``(certificate_path, private_key_path)`` tuple for separate
|
|
51
51
|
files.
|
|
52
|
+
key_password: Optional private key password. Pass the private key
|
|
53
|
+
password if the key is password protected.
|
|
52
54
|
ltpa_token: Optional LTPA token sent as the ``LtpaToken2`` cookie.
|
|
53
55
|
jwt_token: Optional JWT sent as the cookie named by
|
|
54
56
|
``jwt_cookie_name``. If both token types are provided, both
|
|
@@ -69,30 +71,28 @@ class ZosmfBase:
|
|
|
69
71
|
ZosmfError: If ``insecure`` is combined with ``cert`` or ``cafile``.
|
|
70
72
|
"""
|
|
71
73
|
if insecure and (cafile or cert):
|
|
72
|
-
raise ZosmfError(
|
|
73
|
-
"Cannot set insecure mode when cafile or cert is provided."
|
|
74
|
-
)
|
|
74
|
+
raise ZosmfError("Cannot set insecure mode when cafile or cert is provided.")
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
if isinstance(cert, tuple):
|
|
78
|
-
certfile, keyfile = cert
|
|
79
|
-
ctx.load_cert_chain(certfile=certfile, keyfile=keyfile)
|
|
80
|
-
elif cert:
|
|
81
|
-
ctx.load_cert_chain(certfile=cert)
|
|
82
|
-
if cafile:
|
|
83
|
-
ctx.load_verify_locations(cafile=cafile)
|
|
76
|
+
verify: bool | ssl.SSLContext
|
|
84
77
|
if insecure:
|
|
85
|
-
logger.debug(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
ctx =
|
|
78
|
+
logger.debug("Insecure mode enabled, TLS certificate verification is disabled.")
|
|
79
|
+
verify = False
|
|
80
|
+
else:
|
|
81
|
+
ctx = ssl.create_default_context()
|
|
82
|
+
if isinstance(cert, tuple):
|
|
83
|
+
certfile, keyfile = cert
|
|
84
|
+
ctx.load_cert_chain(certfile=certfile, keyfile=keyfile, password=key_password)
|
|
85
|
+
elif cert:
|
|
86
|
+
ctx.load_cert_chain(certfile=cert)
|
|
87
|
+
if cafile:
|
|
88
|
+
ctx.load_verify_locations(cafile=cafile)
|
|
89
|
+
verify = ctx # <------ linea 88
|
|
90
|
+
|
|
89
91
|
proxy_config: str | httpx2.Proxy | None = proxy
|
|
90
92
|
if proxy:
|
|
91
93
|
proxy_config = httpx2.Proxy(proxy)
|
|
92
94
|
proxy_config.auth = (
|
|
93
|
-
(proxyusername, proxypassword)
|
|
94
|
-
if proxyusername or proxypassword
|
|
95
|
-
else None
|
|
95
|
+
(proxyusername, proxypassword) if proxyusername or proxypassword else None
|
|
96
96
|
)
|
|
97
97
|
auth = (username, password) if username and password else None
|
|
98
98
|
headers = {}
|
|
@@ -101,13 +101,11 @@ class ZosmfBase:
|
|
|
101
101
|
if jwt_token:
|
|
102
102
|
headers = {"Cookie": f"{jwt_cookie_name}={jwt_token}"}
|
|
103
103
|
if ltpa_token and jwt_token:
|
|
104
|
-
headers = {
|
|
105
|
-
"Cookie": f"LtpaToken2={ltpa_token}; {jwt_cookie_name}={jwt_token}"
|
|
106
|
-
}
|
|
104
|
+
headers = {"Cookie": f"LtpaToken2={ltpa_token}; {jwt_cookie_name}={jwt_token}"}
|
|
107
105
|
self.http = httpx2.Client(
|
|
108
106
|
base_url=server,
|
|
109
107
|
proxy=proxy_config,
|
|
110
|
-
verify=
|
|
108
|
+
verify=verify,
|
|
111
109
|
auth=auth,
|
|
112
110
|
headers=headers,
|
|
113
111
|
)
|
|
@@ -26,18 +26,16 @@ class AuthenticationServices(ZosmfBase):
|
|
|
26
26
|
token cookies.
|
|
27
27
|
|
|
28
28
|
Returns:
|
|
29
|
-
A ``LoginOk``
|
|
30
|
-
plus any LTPA or JWT tokens returned as response cookies
|
|
31
|
-
endpoint responds with HTTP 200. A successful non-200 response
|
|
32
|
-
returns ``None``.
|
|
29
|
+
A ``LoginOk`` containing the response return/reason codes and
|
|
30
|
+
message, plus any LTPA or JWT tokens returned as response cookies.
|
|
33
31
|
|
|
34
32
|
Raises:
|
|
35
33
|
ZosmfLoginError: If z/OSMF responds with HTTP 401.
|
|
36
34
|
ZosmfAuthError: If another unsuccessful HTTP status is returned.
|
|
37
35
|
httpx2.RequestError: If the request fails before an HTTP response
|
|
38
|
-
|
|
36
|
+
is received.
|
|
39
37
|
"""
|
|
40
|
-
logger.debug(
|
|
38
|
+
logger.debug("Authenticating with z/OSMF using all available methods")
|
|
41
39
|
resp = self.http.post("/zosmf/services/authenticate")
|
|
42
40
|
if resp.status_code == 200:
|
|
43
41
|
logger.debug("Authentication succeeded with z/OSMF")
|
|
@@ -47,7 +45,8 @@ class AuthenticationServices(ZosmfBase):
|
|
|
47
45
|
jwt_token=resp.cookies.get(self.jwt_cookie_name, self.jwt_token),
|
|
48
46
|
)
|
|
49
47
|
elif resp.status_code == 401:
|
|
50
|
-
logger.debug(
|
|
48
|
+
logger.debug("Authentication failed with z/OSMF")
|
|
51
49
|
raise ZosmfLoginError(resp)
|
|
52
|
-
logger.debug(
|
|
50
|
+
logger.debug("Unexpected authentication failure with z/OSMF")
|
|
53
51
|
self._raise_for_status(resp, exc_handler=ZosmfAuthError)
|
|
52
|
+
raise AssertionError("Unreachable") # pragma: no cover
|
|
@@ -125,9 +125,7 @@ class SoftwareServices(ZosmfBase):
|
|
|
125
125
|
|
|
126
126
|
def _get_swi_datasets(self, path: str) -> dict[
|
|
127
127
|
str,
|
|
128
|
-
list[SmpeManagedDataset]
|
|
129
|
-
| list[SmpeManagedUnixDataset]
|
|
130
|
-
| list[NonSmpeManagedDataset],
|
|
128
|
+
list[SmpeManagedDataset] | list[SmpeManagedUnixDataset] | list[NonSmpeManagedDataset],
|
|
131
129
|
]:
|
|
132
130
|
resp = self.http.post(path)
|
|
133
131
|
if resp.status_code == 404:
|
|
@@ -156,9 +154,7 @@ class SoftwareServices(ZosmfBase):
|
|
|
156
154
|
|
|
157
155
|
def get_swi_datasets_by_uuid(self, swiuuid: str) -> dict[
|
|
158
156
|
str,
|
|
159
|
-
list[SmpeManagedDataset]
|
|
160
|
-
| list[SmpeManagedUnixDataset]
|
|
161
|
-
| list[NonSmpeManagedDataset],
|
|
157
|
+
list[SmpeManagedDataset] | list[SmpeManagedUnixDataset] | list[NonSmpeManagedDataset],
|
|
162
158
|
]:
|
|
163
159
|
"""Retrieve dataset inventories for a software instance by UUID.
|
|
164
160
|
|
|
@@ -185,9 +181,7 @@ class SoftwareServices(ZosmfBase):
|
|
|
185
181
|
|
|
186
182
|
def get_swi_datasets_by_name(self, name: str, system: str) -> dict[
|
|
187
183
|
str,
|
|
188
|
-
list[SmpeManagedDataset]
|
|
189
|
-
| list[SmpeManagedUnixDataset]
|
|
190
|
-
| list[NonSmpeManagedDataset],
|
|
184
|
+
list[SmpeManagedDataset] | list[SmpeManagedUnixDataset] | list[NonSmpeManagedDataset],
|
|
191
185
|
]:
|
|
192
186
|
"""Retrieve dataset inventories for a software instance by name.
|
|
193
187
|
|
|
@@ -264,9 +258,7 @@ class SoftwareServices(ZosmfBase):
|
|
|
264
258
|
else []
|
|
265
259
|
),
|
|
266
260
|
"workflows": (
|
|
267
|
-
[asdict(workflow) for workflow in swi.workflows]
|
|
268
|
-
if swi.workflows
|
|
269
|
-
else []
|
|
261
|
+
[asdict(workflow) for workflow in swi.workflows] if swi.workflows else []
|
|
270
262
|
),
|
|
271
263
|
},
|
|
272
264
|
)
|
|
@@ -333,9 +325,7 @@ class SoftwareServices(ZosmfBase):
|
|
|
333
325
|
wait: bool,
|
|
334
326
|
) -> list[CsiEntry] | dict[str, Any]:
|
|
335
327
|
if "*" in entries and len(entries) > 1:
|
|
336
|
-
raise ZosmfSoftwareManagementError(
|
|
337
|
-
"'*' cannot be combined with other entry types."
|
|
338
|
-
)
|
|
328
|
+
raise ZosmfSoftwareManagementError("'*' cannot be combined with other entry types.")
|
|
339
329
|
elif "*" in subentries and len(subentries) > 1:
|
|
340
330
|
raise ZosmfSoftwareManagementError(
|
|
341
331
|
"'*' cannot be combined with other subentry types."
|
|
@@ -484,6 +474,4 @@ class SoftwareServices(ZosmfBase):
|
|
|
484
474
|
while status["status"] == "running":
|
|
485
475
|
time.sleep(2)
|
|
486
476
|
status = self._get_query_csi_status(statusurl)
|
|
487
|
-
return [
|
|
488
|
-
from_dict(data_class=CsiEntry, data=entry) for entry in status["entries"]
|
|
489
|
-
]
|
|
477
|
+
return [from_dict(data_class=CsiEntry, data=entry) for entry in status["entries"]]
|
|
@@ -47,9 +47,7 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
47
47
|
for upd in resp.json()["update-processes"]
|
|
48
48
|
]
|
|
49
49
|
|
|
50
|
-
def list_sw_updates_by_uuid(
|
|
51
|
-
self, swiuuid: str, last: bool = False
|
|
52
|
-
) -> list[UpdateProcess]:
|
|
50
|
+
def list_sw_updates_by_uuid(self, swiuuid: str, last: bool = False) -> list[UpdateProcess]:
|
|
53
51
|
"""List update processes for a software instance identified by UUID.
|
|
54
52
|
|
|
55
53
|
If z/OSMF reports that the software instance has no update-process
|
|
@@ -102,9 +100,7 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
102
100
|
f"/zosmf/swmgmt/swi/{system}/{name}/swupdate{'' if last else '/all'}"
|
|
103
101
|
)
|
|
104
102
|
|
|
105
|
-
def get_sw_updates(
|
|
106
|
-
self, process_id: int, wait: bool = False
|
|
107
|
-
) -> list[UpdateProcess]:
|
|
103
|
+
def get_sw_updates(self, process_id: int, wait: bool = False) -> list[UpdateProcess]:
|
|
108
104
|
"""Get the update-process records for a process ID.
|
|
109
105
|
|
|
110
106
|
Args:
|
|
@@ -158,9 +154,7 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
158
154
|
while status.json()["status"] == "running":
|
|
159
155
|
time.sleep(2)
|
|
160
156
|
status = self.http.get(status_monitor_url)
|
|
161
|
-
self._raise_for_status(
|
|
162
|
-
status, exc_handler=ZosmfSoftwareManagementRequestError
|
|
163
|
-
)
|
|
157
|
+
self._raise_for_status(status, exc_handler=ZosmfSoftwareManagementRequestError)
|
|
164
158
|
if updates:
|
|
165
159
|
return {
|
|
166
160
|
"lastholdrecvdate": pendulum.parse(status.json()["lastholdrecvdate"]),
|
|
@@ -396,9 +390,7 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
396
390
|
while status.json()["status"] == "running":
|
|
397
391
|
time.sleep(2)
|
|
398
392
|
status = self.http.get(status_monitor_url)
|
|
399
|
-
self._raise_for_status(
|
|
400
|
-
status, exc_handler=ZosmfSoftwareManagementRequestError
|
|
401
|
-
)
|
|
393
|
+
self._raise_for_status(status, exc_handler=ZosmfSoftwareManagementRequestError)
|
|
402
394
|
return [
|
|
403
395
|
from_dict(
|
|
404
396
|
data_class=SoftwareUpdate,
|
|
@@ -658,9 +650,7 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
658
650
|
httpx2.RequestError: If the request fails before an HTTP response
|
|
659
651
|
is received.
|
|
660
652
|
"""
|
|
661
|
-
return self._cancel_sw_update(
|
|
662
|
-
f"/zosmf/swmgmt/swi/{system}/{name}/swupdate/cancel"
|
|
663
|
-
)
|
|
653
|
+
return self._cancel_sw_update(f"/zosmf/swmgmt/swi/{system}/{name}/swupdate/cancel")
|
|
664
654
|
|
|
665
655
|
def _resume_sw_update(
|
|
666
656
|
self,
|
|
@@ -882,16 +872,12 @@ class SoftwareUpdatesServices(ZosmfBase):
|
|
|
882
872
|
for process in resp.json()["update-processes"]
|
|
883
873
|
]
|
|
884
874
|
|
|
885
|
-
def _copy_output_sw_update(
|
|
886
|
-
self, path: str, output_dir: str
|
|
887
|
-
) -> list[dict[str, Any]]:
|
|
875
|
+
def _copy_output_sw_update(self, path: str, output_dir: str) -> list[dict[str, Any]]:
|
|
888
876
|
resp = self.http.put(path, params={"dir": output_dir})
|
|
889
877
|
self._raise_for_status(resp, exc_handler=ZosmfSoftwareManagementError)
|
|
890
878
|
return resp.json()["files"]
|
|
891
879
|
|
|
892
|
-
def copy_output_sw_update_by_uuid(
|
|
893
|
-
self, uuid: str, output_dir: str
|
|
894
|
-
) -> list[dict[str, Any]]:
|
|
880
|
+
def copy_output_sw_update_by_uuid(self, uuid: str, output_dir: str) -> list[dict[str, Any]]:
|
|
895
881
|
"""Copy output files for a software instance identified by UUID.
|
|
896
882
|
|
|
897
883
|
Args:
|
|
@@ -26,9 +26,7 @@ class WorkflowServices(ZosmfBase):
|
|
|
26
26
|
an iterator so callers can observe each status update while polling.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
def _get_workflow(
|
|
30
|
-
self, workflow_id: str, steps: bool, variables: bool
|
|
31
|
-
) -> dict[str, Any]:
|
|
29
|
+
def _get_workflow(self, workflow_id: str, steps: bool, variables: bool) -> dict[str, Any]:
|
|
32
30
|
"""Fetch the raw workflow response, optionally including steps/variables.
|
|
33
31
|
|
|
34
32
|
This internal helper returns the JSON object before model conversion.
|
|
@@ -187,9 +185,7 @@ class WorkflowServices(ZosmfBase):
|
|
|
187
185
|
httpx2.RequestError: If either request fails before an HTTP response
|
|
188
186
|
is received.
|
|
189
187
|
"""
|
|
190
|
-
resp = self.http.post(
|
|
191
|
-
"/zosmf/workflow/rest/1.0/workflows", json=to_dict(workflow)
|
|
192
|
-
)
|
|
188
|
+
resp = self.http.post("/zosmf/workflow/rest/1.0/workflows", json=to_dict(workflow))
|
|
193
189
|
self._raise_for_status(resp, exc_handler=ZosmfWorkflowError)
|
|
194
190
|
return self.get_workflow(resp.json()["workflowKey"])
|
|
195
191
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
|
|
3
|
+
from zosmfrestapi.testing.authentication import *
|
|
4
|
+
from zosmfrestapi.testing.software import *
|
|
5
|
+
from zosmfrestapi.testing.software_updates import *
|
|
6
|
+
from zosmfrestapi.testing.topology import *
|
|
7
|
+
from zosmfrestapi.testing.workflows import *
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
name for name in globals() if name.startswith("make_")
|
|
11
|
+
] # pyright: ignore[reportUnsupportedDunderAll]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from unittest.mock import ANY
|
|
2
|
+
|
|
3
|
+
from zosmfrestapi.models.authentication import LoginOk
|
|
4
|
+
|
|
5
|
+
ltpa_token = "x2q29998U26AwfYy4vc0GlK5zItaZkkXEbGQ/...<REDACTED>.../NiM1t3FQpvWRbzTsmyr2"
|
|
6
|
+
|
|
7
|
+
jwt_token = (
|
|
8
|
+
"eyJraWQiOiJBTk9OWU1JWkVEX0tJRCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJ0b2tlbl90"
|
|
9
|
+
"eXBlIjoiQmVhcmVyIiwic3ViIjoiPFVTRVI+IiwidXBuIjoiPFVTRVI+IiwiZ3JvdXBzIjpbIkdST1VQX"
|
|
10
|
+
"0EiLCJHUk9VUF9CIiwiR1JPVVBfQyJdLCJyZWFsbSI6IjxSRUFMTT4iLCJpc3MiOiI8SVNTVUVSPiIsImV"
|
|
11
|
+
"4cCI6MTIzNDU2Nzg5MCwiaWF0IjoxMjM0NTYwMDAwfQ.<SIGNATURE_REDACTED>"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def make_loginok():
|
|
16
|
+
return LoginOk(
|
|
17
|
+
returnCode=0,
|
|
18
|
+
reasonCode=0,
|
|
19
|
+
message="Success.",
|
|
20
|
+
ltpa_token=ANY,
|
|
21
|
+
jwt_token=ANY,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def make_loginok_ltpa():
|
|
26
|
+
return LoginOk(
|
|
27
|
+
returnCode=0,
|
|
28
|
+
reasonCode=0,
|
|
29
|
+
message="Success.",
|
|
30
|
+
ltpa_token=ltpa_token,
|
|
31
|
+
jwt_token=ANY,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def make_loginok_jwt():
|
|
36
|
+
return LoginOk(
|
|
37
|
+
returnCode=0, reasonCode=0, message="Success.", ltpa_token=None, jwt_token=jwt_token
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_loginok_ltpa_jwt():
|
|
42
|
+
return LoginOk(
|
|
43
|
+
returnCode=0,
|
|
44
|
+
reasonCode=0,
|
|
45
|
+
message="Success.",
|
|
46
|
+
ltpa_token=ltpa_token,
|
|
47
|
+
jwt_token=jwt_token,
|
|
48
|
+
)
|