zosmfrestapi 0.2.0__py3-none-any.whl
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/__init__.py +93 -0
- zosmfrestapi/client.py +47 -0
- zosmfrestapi/exceptions/__init__.py +18 -0
- zosmfrestapi/exceptions/authentication.py +17 -0
- zosmfrestapi/exceptions/dataset.py +17 -0
- zosmfrestapi/exceptions/software.py +34 -0
- zosmfrestapi/exceptions/workflow.py +30 -0
- zosmfrestapi/models/__init__.py +0 -0
- zosmfrestapi/models/authentication.py +16 -0
- zosmfrestapi/models/software.py +231 -0
- zosmfrestapi/models/software_updates.py +99 -0
- zosmfrestapi/models/topology.py +28 -0
- zosmfrestapi/models/workflow.py +114 -0
- zosmfrestapi/py.typed +0 -0
- zosmfrestapi/services/__init__.py +176 -0
- zosmfrestapi/services/authentication.py +52 -0
- zosmfrestapi/services/dataset.py +42 -0
- zosmfrestapi/services/software.py +477 -0
- zosmfrestapi/services/software_updates.py +949 -0
- zosmfrestapi/services/topology.py +49 -0
- zosmfrestapi/services/workflow.py +350 -0
- zosmfrestapi/testing/__init__.py +11 -0
- zosmfrestapi/testing/authentication.py +48 -0
- zosmfrestapi/testing/helpers.py +9 -0
- zosmfrestapi/testing/software.py +392 -0
- zosmfrestapi/testing/software_updates.py +281 -0
- zosmfrestapi/testing/topology.py +19 -0
- zosmfrestapi/testing/workflows.py +320 -0
- zosmfrestapi/utils.py +76 -0
- zosmfrestapi/zcrypto.py +55 -0
- zosmfrestapi/zcrypto_client.py +68 -0
- zosmfrestapi-0.2.0.dist-info/METADATA +172 -0
- zosmfrestapi-0.2.0.dist-info/RECORD +35 -0
- zosmfrestapi-0.2.0.dist-info/WHEEL +4 -0
- zosmfrestapi-0.2.0.dist-info/licenses/LICENSE +10 -0
zosmfrestapi/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Synchronous Python client for the z/OSMF REST API.
|
|
2
|
+
|
|
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
|
+
"""
|
|
7
|
+
|
|
8
|
+
from zosmfrestapi.client import ZosmfClient
|
|
9
|
+
from zosmfrestapi.exceptions import ZosmfAuthError, ZosmfError
|
|
10
|
+
from zosmfrestapi.exceptions.authentication import ZosmfLoginError
|
|
11
|
+
from zosmfrestapi.exceptions.dataset import ZosmfDatasetNotFoundError
|
|
12
|
+
from zosmfrestapi.exceptions.software import (
|
|
13
|
+
ZosmfSoftwareInstanceNotFoundError,
|
|
14
|
+
ZosmfSoftwareManagementError,
|
|
15
|
+
ZosmfSoftwareManagementRequestError,
|
|
16
|
+
)
|
|
17
|
+
from zosmfrestapi.exceptions.workflow import ZosmfWorkflowBaseError, ZosmfWorkflowError
|
|
18
|
+
from zosmfrestapi.models.authentication import LoginOk
|
|
19
|
+
from zosmfrestapi.models.software import (
|
|
20
|
+
CsiEntry,
|
|
21
|
+
DataSet,
|
|
22
|
+
Feature,
|
|
23
|
+
Fmid,
|
|
24
|
+
NonSmpeManagedDataset,
|
|
25
|
+
Product,
|
|
26
|
+
SmpeManagedDataset,
|
|
27
|
+
SmpeManagedDatasetZoneDddef,
|
|
28
|
+
SmpeManagedUnixDataset,
|
|
29
|
+
SoftwareInstance,
|
|
30
|
+
SoftwareInstanceWorkflow,
|
|
31
|
+
WorkflowLocation,
|
|
32
|
+
)
|
|
33
|
+
from zosmfrestapi.models.software_updates import (
|
|
34
|
+
MissingFixcat,
|
|
35
|
+
MissingUpdate,
|
|
36
|
+
MissingUpdateResolver,
|
|
37
|
+
SoftwareUpdate,
|
|
38
|
+
SoftwareUpdateZone,
|
|
39
|
+
UpdateErrorResults,
|
|
40
|
+
UpdateProcess,
|
|
41
|
+
UpdateResult,
|
|
42
|
+
UpdateSelection,
|
|
43
|
+
)
|
|
44
|
+
from zosmfrestapi.models.topology import Group, System
|
|
45
|
+
from zosmfrestapi.models.workflow import (
|
|
46
|
+
WorkflowArchivedListItem,
|
|
47
|
+
WorkflowInstance,
|
|
48
|
+
WorkflowItem,
|
|
49
|
+
WorkflowListItem,
|
|
50
|
+
)
|
|
51
|
+
from zosmfrestapi.zcrypto_client import ZosmfZcryptoClient
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"ZosmfClient",
|
|
55
|
+
"ZosmfZcryptoClient",
|
|
56
|
+
"LoginOk",
|
|
57
|
+
"ZosmfDatasetNotFoundError",
|
|
58
|
+
"ZosmfError",
|
|
59
|
+
"ZosmfAuthError",
|
|
60
|
+
"ZosmfLoginError",
|
|
61
|
+
"System",
|
|
62
|
+
"Group",
|
|
63
|
+
"CsiEntry",
|
|
64
|
+
"DataSet",
|
|
65
|
+
"Feature",
|
|
66
|
+
"Fmid",
|
|
67
|
+
"NonSmpeManagedDataset",
|
|
68
|
+
"Product",
|
|
69
|
+
"SmpeManagedDataset",
|
|
70
|
+
"SmpeManagedDatasetZoneDddef",
|
|
71
|
+
"SmpeManagedUnixDataset",
|
|
72
|
+
"SoftwareInstance",
|
|
73
|
+
"SoftwareInstanceWorkflow",
|
|
74
|
+
"WorkflowLocation",
|
|
75
|
+
"ZosmfSoftwareManagementError",
|
|
76
|
+
"ZosmfSoftwareInstanceNotFoundError",
|
|
77
|
+
"ZosmfSoftwareManagementRequestError",
|
|
78
|
+
"MissingFixcat",
|
|
79
|
+
"MissingUpdate",
|
|
80
|
+
"MissingUpdateResolver",
|
|
81
|
+
"SoftwareUpdate",
|
|
82
|
+
"SoftwareUpdateZone",
|
|
83
|
+
"UpdateErrorResults",
|
|
84
|
+
"UpdateProcess",
|
|
85
|
+
"UpdateResult",
|
|
86
|
+
"UpdateSelection",
|
|
87
|
+
"WorkflowArchivedListItem",
|
|
88
|
+
"WorkflowInstance",
|
|
89
|
+
"WorkflowItem",
|
|
90
|
+
"WorkflowListItem",
|
|
91
|
+
"ZosmfWorkflowBaseError",
|
|
92
|
+
"ZosmfWorkflowError",
|
|
93
|
+
]
|
zosmfrestapi/client.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from zosmfrestapi.services.authentication import AuthenticationServices
|
|
2
|
+
from zosmfrestapi.services.dataset import DatasetServices
|
|
3
|
+
from zosmfrestapi.services.software import SoftwareServices
|
|
4
|
+
from zosmfrestapi.services.software_updates import SoftwareUpdatesServices
|
|
5
|
+
from zosmfrestapi.services.topology import TopologyServices
|
|
6
|
+
from zosmfrestapi.services.workflow import WorkflowServices
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ZosmfClient(
|
|
10
|
+
AuthenticationServices,
|
|
11
|
+
DatasetServices,
|
|
12
|
+
TopologyServices,
|
|
13
|
+
WorkflowServices,
|
|
14
|
+
SoftwareServices,
|
|
15
|
+
SoftwareUpdatesServices,
|
|
16
|
+
):
|
|
17
|
+
"""Synchronous client for the z/OSMF REST APIs.
|
|
18
|
+
|
|
19
|
+
The client combines authentication, datasets, topology, workflows,
|
|
20
|
+
software-instance management, and software-update operations around one
|
|
21
|
+
reusable HTTP session. Update-start and resume methods can change the
|
|
22
|
+
selected target zone; use them only when those changes are intended.
|
|
23
|
+
|
|
24
|
+
Use it as a context manager to close the underlying HTTP session reliably.
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
Use a context manager when making requests:
|
|
28
|
+
|
|
29
|
+
with ZosmfClient("https://zosmf.example",
|
|
30
|
+
username="demouser", password="DEMOPASS") as client:
|
|
31
|
+
systems = client.get_systems()
|
|
32
|
+
|
|
33
|
+
Use a client without using a context manager:
|
|
34
|
+
|
|
35
|
+
client = ZosmfClient("https://zosmf.example",
|
|
36
|
+
username="demouser", password="DEMOPASS")
|
|
37
|
+
systems = client.get_systems()
|
|
38
|
+
client.close()
|
|
39
|
+
|
|
40
|
+
Use a client with mTLS authentication:
|
|
41
|
+
|
|
42
|
+
with ZosmfClient("https://zosmf.example",
|
|
43
|
+
username="demouser", password="DEMOPASS",
|
|
44
|
+
cert=("/path/to/cert", "/path/to/key"),
|
|
45
|
+
key_password="secret") as client:
|
|
46
|
+
systems = client.get_systems()
|
|
47
|
+
"""
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import httpx2
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ZosmfError(Exception):
|
|
5
|
+
"""Base exception for errors raised by the z/OSMF REST API client."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ZosmfAuthError(ZosmfError):
|
|
11
|
+
"""Raised when z/OSMF rejects a request with HTTP 401.
|
|
12
|
+
|
|
13
|
+
The triggering ``httpx2.HTTPStatusError`` is available as
|
|
14
|
+
:attr:`http_exception`.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, exc: httpx2.HTTPStatusError) -> None:
|
|
18
|
+
self.http_exception = exc
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import httpx2
|
|
2
|
+
|
|
3
|
+
from . import ZosmfError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ZosmfLoginError(ZosmfError):
|
|
7
|
+
"""Raised when the authentication endpoint rejects a login request.
|
|
8
|
+
|
|
9
|
+
The response's return code, reason code, and message are exposed as
|
|
10
|
+
``return_code``, ``reason_code``, and ``message``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, response: httpx2.Response) -> None:
|
|
14
|
+
self.http_exception = None
|
|
15
|
+
self.return_code = response.json()["returnCode"]
|
|
16
|
+
self.reason_code = response.json()["reasonCode"]
|
|
17
|
+
self.message = response.json()["message"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import httpx2
|
|
4
|
+
from zosmfrestapi.exceptions import ZosmfError
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ZosmfDatasetError(ZosmfError):
|
|
10
|
+
"""Raised when a data set REST request fails with an HTTP status error."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ZosmfDatasetNotFoundError(ZosmfDatasetError):
|
|
14
|
+
"""Raised when z/OSMF reports that the requested data set is not found."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, response: httpx2.Response) -> None:
|
|
17
|
+
pass
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from . import ZosmfError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ZosmfSoftwareManagementError(ZosmfError):
|
|
5
|
+
"""Base exception for z/OSMF Software Management operations."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ZosmfSoftwareManagementRequestError(ZosmfSoftwareManagementError):
|
|
9
|
+
"""Raised when a Software Management request fails with an HTTP status.
|
|
10
|
+
|
|
11
|
+
When the response includes the corresponding fields, ``status_code``,
|
|
12
|
+
``reason_code``, ``messages``, and ``body`` provide response details.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, error=None) -> None:
|
|
16
|
+
self.status_code = error.response.status_code if error else None
|
|
17
|
+
self.reason_code = error.response.json()["reason"] if error else None
|
|
18
|
+
self.messages = error.response.json()["messages"] if error else None
|
|
19
|
+
self.body = error.response.text if error else None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ZosmfSoftwareInstanceNotFoundError(ZosmfSoftwareManagementError):
|
|
23
|
+
"""Raised when a requested software instance does not exist."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, response) -> None:
|
|
26
|
+
super().__init__(response)
|
|
27
|
+
self.messages = (
|
|
28
|
+
response.json().get("messages", [])
|
|
29
|
+
if response.headers.get("Content-Type").startswith("application/json")
|
|
30
|
+
else [response.text]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def __str__(self) -> str:
|
|
34
|
+
return ", ".join(self.messages) if self.messages else ""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import httpx2
|
|
2
|
+
|
|
3
|
+
from . import ZosmfError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ZosmfWorkflowBaseError(ZosmfError):
|
|
7
|
+
"""Base exception for z/OSMF workflow operations."""
|
|
8
|
+
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ZosmfWorkflowError(ZosmfWorkflowBaseError):
|
|
13
|
+
"""Raised when a workflow REST request fails with an HTTP status.
|
|
14
|
+
|
|
15
|
+
When present in the response, ``status_code``, ``message_id``, ``message``,
|
|
16
|
+
and ``body`` expose the z/OSMF error details.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, error: httpx2.HTTPStatusError | None = None) -> None:
|
|
20
|
+
self.exc = error
|
|
21
|
+
self.status_code = error.response.status_code if error else None
|
|
22
|
+
self.message_id = error.response.json()["messageID"] if error else None
|
|
23
|
+
self.message = error.response.json()["messageText"] if error else None
|
|
24
|
+
self.body = error.response.text if error else None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ZosmfWorkflowCanceledError(ZosmfWorkflowBaseError):
|
|
28
|
+
"""Reserved for workflow-specific cancellation failures."""
|
|
29
|
+
|
|
30
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class LoginOk:
|
|
6
|
+
"""Successful response from the z/OSMF authentication endpoint.
|
|
7
|
+
|
|
8
|
+
The endpoint return fields are preserved alongside any LTPA or JWT token
|
|
9
|
+
cookies returned by the server.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
returnCode: int
|
|
13
|
+
reasonCode: int
|
|
14
|
+
message: str
|
|
15
|
+
ltpa_token: str | None = None
|
|
16
|
+
jwt_token: str | None = None
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import pendulum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class DataSet:
|
|
9
|
+
"""Reference to a data set and, optionally, the volume that contains it."""
|
|
10
|
+
|
|
11
|
+
dsname: str | None = None
|
|
12
|
+
volume: str | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class SmpeManagedDatasetZoneDddef:
|
|
17
|
+
"""DDDEF names associated with an SMP/E zone."""
|
|
18
|
+
|
|
19
|
+
zone: str | None = None
|
|
20
|
+
dddefs: list[str] = field(default_factory=list)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class SmpeManagedDataset:
|
|
25
|
+
"""Inventory details for an SMP/E-managed MVS data set."""
|
|
26
|
+
|
|
27
|
+
dsname: str | None = None
|
|
28
|
+
volumes: list[str] | None = field(default_factory=list)
|
|
29
|
+
dstype: str | None = None
|
|
30
|
+
recfm: str | None = None
|
|
31
|
+
lrecl: str | None = None
|
|
32
|
+
blksize: str | None = None
|
|
33
|
+
tracks: str | None = None
|
|
34
|
+
used: str | None = None
|
|
35
|
+
extents: str | None = None
|
|
36
|
+
dscategory: list[str] = field(default_factory=list)
|
|
37
|
+
zones: list[str] = field(default_factory=list)
|
|
38
|
+
zonedddefs: list[SmpeManagedDatasetZoneDddef] | None = field(default_factory=list)
|
|
39
|
+
msgs: Any = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SmpeManagedUnixDataset:
|
|
44
|
+
"""Inventory details for an SMP/E-managed UNIX file-system data set."""
|
|
45
|
+
|
|
46
|
+
dsname: str | None = None
|
|
47
|
+
unixdirs: list[str] | None = field(default_factory=list)
|
|
48
|
+
volumes: list[str] | None = field(default_factory=list)
|
|
49
|
+
dstype: str | None = None
|
|
50
|
+
recfm: str | None = None
|
|
51
|
+
lrecl: str | None = None
|
|
52
|
+
blksize: str | None = None
|
|
53
|
+
tracks: str | None = None
|
|
54
|
+
used: str | None = None
|
|
55
|
+
extents: str | None = None
|
|
56
|
+
dscategory: list[str] | None = field(default_factory=list)
|
|
57
|
+
zones: list[str] | None = field(default_factory=list)
|
|
58
|
+
zonedddefs: list[SmpeManagedDatasetZoneDddef] | None = field(default_factory=list)
|
|
59
|
+
msgs: list[str] | None = field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class NonSmpeManagedDataset:
|
|
64
|
+
"""Inventory details for a data set not managed by SMP/E."""
|
|
65
|
+
|
|
66
|
+
dsname: str | None = None
|
|
67
|
+
volumes: list[str] | None = field(default_factory=list)
|
|
68
|
+
dstype: str | None = None
|
|
69
|
+
recfm: str | None = None
|
|
70
|
+
lrecl: str | None = None
|
|
71
|
+
blksize: str | None = None
|
|
72
|
+
tracks: str | None = None
|
|
73
|
+
used: str | None = None
|
|
74
|
+
extents: str | None = None
|
|
75
|
+
dscategory: list[str] | None = field(default_factory=list)
|
|
76
|
+
unixdirs: list[str] | None = field(default_factory=list)
|
|
77
|
+
msgs: list[str] | None = field(default_factory=list)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class CsiSubEntry:
|
|
82
|
+
"""Arbitrary values returned for one CSI subentry."""
|
|
83
|
+
|
|
84
|
+
values: dict[str, Any]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class CsiEntry:
|
|
89
|
+
"""CSI entry identity and its returned subentry values."""
|
|
90
|
+
|
|
91
|
+
entryname: str | None = None
|
|
92
|
+
entrytype: str | None = None
|
|
93
|
+
zonename: str | None = None
|
|
94
|
+
subentries: list[dict[str, Any]] = field(default_factory=list)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class Fmid:
|
|
99
|
+
"""Function modification identifier and its description/target zones."""
|
|
100
|
+
|
|
101
|
+
fmid: str | None = None
|
|
102
|
+
description: str | None = None
|
|
103
|
+
targetzones: list[str] | None = field(default_factory=list)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class Feature:
|
|
108
|
+
"""Software product feature and the FMIDs that implement it."""
|
|
109
|
+
|
|
110
|
+
feature: str | None = None
|
|
111
|
+
fmids: list[Fmid] = field(default_factory=list)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class Product:
|
|
116
|
+
"""SMP/E-managed product metadata and service lifecycle dates."""
|
|
117
|
+
|
|
118
|
+
prodname: str | None = None
|
|
119
|
+
productid: str | None = None
|
|
120
|
+
release: str | None = None
|
|
121
|
+
vendor: str | None = None
|
|
122
|
+
generalavailability: pendulum.DateTime | None = None
|
|
123
|
+
endofservice: pendulum.DateTime | None = None
|
|
124
|
+
url: str | None = None
|
|
125
|
+
productinfofileversion: str | None = None
|
|
126
|
+
features: list[Feature] = field(default_factory=list)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass
|
|
130
|
+
class NonSmpeProduct:
|
|
131
|
+
"""Product metadata for a product not managed by SMP/E."""
|
|
132
|
+
|
|
133
|
+
prodname: str | None = None
|
|
134
|
+
productid: str | None = None
|
|
135
|
+
release: str | None = None
|
|
136
|
+
vendor: str | None = None
|
|
137
|
+
url: str | None = None
|
|
138
|
+
features: list[str] = field(default_factory=list)
|
|
139
|
+
generalavailability: pendulum.DateTime | None = None
|
|
140
|
+
endofservice: pendulum.DateTime | None = None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class WorkflowLocation:
|
|
145
|
+
"""Location details for a workflow associated with a software instance."""
|
|
146
|
+
|
|
147
|
+
smptype: str | None = None
|
|
148
|
+
smpname: str | None = None
|
|
149
|
+
dsname: str | None = None
|
|
150
|
+
path: str | None = None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass
|
|
154
|
+
class SoftwareInstanceWorkflow:
|
|
155
|
+
"""Workflow definition associated with a software instance."""
|
|
156
|
+
|
|
157
|
+
name: str | None = None
|
|
158
|
+
description: str | None = None
|
|
159
|
+
location: WorkflowLocation = field(default_factory=WorkflowLocation)
|
|
160
|
+
performonhostsystem: bool | None = None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass
|
|
164
|
+
class DataSetProperty:
|
|
165
|
+
"""Property values for a data set DDDEF in a software instance."""
|
|
166
|
+
|
|
167
|
+
dddefname: str | None = None
|
|
168
|
+
zone: str | None = None
|
|
169
|
+
dsname: str | None = None
|
|
170
|
+
volume: str | None = None
|
|
171
|
+
dstype: str | None = None
|
|
172
|
+
properties: list[dict[str, str]] = field(default_factory=list)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class DataSetPropertyLabel:
|
|
177
|
+
"""Display label associated with a data set property name."""
|
|
178
|
+
|
|
179
|
+
propertyname: str | None = None
|
|
180
|
+
label: str | None = None
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass
|
|
184
|
+
class ProductProperty:
|
|
185
|
+
"""Property values associated with a product and release."""
|
|
186
|
+
|
|
187
|
+
prodid: str | None = None
|
|
188
|
+
release: str | None = None
|
|
189
|
+
prodname: str | None = None
|
|
190
|
+
properties: list[dict[str, str]] = field(default_factory=list)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@dataclass
|
|
194
|
+
class SoftwareInstance:
|
|
195
|
+
"""A z/OSMF software instance and its associated inventory.
|
|
196
|
+
|
|
197
|
+
Construction requires either a global zone with at least one target zone,
|
|
198
|
+
or data-set references when no global zone is configured.
|
|
199
|
+
|
|
200
|
+
Raises:
|
|
201
|
+
ValueError: If neither a global zone nor data sets are provided, or if
|
|
202
|
+
a global zone is provided without target zones.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
name: str
|
|
206
|
+
system: str
|
|
207
|
+
description: str | None = ""
|
|
208
|
+
uuid: str | None = field(default="", metadata={"json_name": "swiuuid"})
|
|
209
|
+
globalzone: str | None = None
|
|
210
|
+
targetzones: list[str] = field(default_factory=list)
|
|
211
|
+
categories: list[str] = field(default_factory=list)
|
|
212
|
+
productinforetrieved: pendulum.DateTime | None = None
|
|
213
|
+
lastmodified: pendulum.DateTime | None = None
|
|
214
|
+
modifiedby: str | None = None
|
|
215
|
+
created: pendulum.DateTime | None = None
|
|
216
|
+
createdby: str | None = None
|
|
217
|
+
locked: pendulum.DateTime | None = None
|
|
218
|
+
lockedby: str | None = None
|
|
219
|
+
datasets: list[DataSet] = field(default_factory=list)
|
|
220
|
+
products: list[Product] = field(default_factory=list)
|
|
221
|
+
nonsmpeproducts: list[NonSmpeProduct] = field(default_factory=list)
|
|
222
|
+
workflows: list[SoftwareInstanceWorkflow] = field(default_factory=list)
|
|
223
|
+
datasetproperties: list[DataSetProperty] = field(default_factory=list)
|
|
224
|
+
datasetpropertylabels: list[DataSetPropertyLabel] = field(default_factory=list)
|
|
225
|
+
productproperties: list[ProductProperty] = field(default_factory=list)
|
|
226
|
+
|
|
227
|
+
def __post_init__(self) -> None:
|
|
228
|
+
if not self.globalzone and not self.datasets:
|
|
229
|
+
raise ValueError("Either globalzone or datasets must be specified")
|
|
230
|
+
elif self.globalzone and not self.targetzones:
|
|
231
|
+
raise ValueError("Target zones must be specified when globalzone is set")
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
import pendulum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class UpdateSelection:
|
|
8
|
+
"""SYSMOD updates selected for a software-update process."""
|
|
9
|
+
|
|
10
|
+
updates: list[str] = field(default_factory=list)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class UpdateResult:
|
|
15
|
+
"""Selection and application result for one update in a process."""
|
|
16
|
+
|
|
17
|
+
fmid: str | None = None
|
|
18
|
+
selectStatus: str | None = field(default=None)
|
|
19
|
+
name: str | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class UpdateErrorResults:
|
|
24
|
+
"""SMP/E return code and messages associated with a failed update."""
|
|
25
|
+
|
|
26
|
+
smpe_rc: int
|
|
27
|
+
message: str
|
|
28
|
+
detailedMessage: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class UpdateProcess:
|
|
33
|
+
"""Status, selection, timing, and results for a software-update process."""
|
|
34
|
+
|
|
35
|
+
targetzone: str
|
|
36
|
+
selection: UpdateSelection
|
|
37
|
+
notes: str
|
|
38
|
+
processid: int
|
|
39
|
+
started: pendulum.DateTime
|
|
40
|
+
status: str
|
|
41
|
+
step: str
|
|
42
|
+
lastmodified: pendulum.DateTime | None = None
|
|
43
|
+
completed: pendulum.DateTime | None = None
|
|
44
|
+
error_results: UpdateErrorResults | None = None
|
|
45
|
+
updates: list[UpdateResult] = field(default_factory=list)
|
|
46
|
+
holds: list[str] = field(default_factory=list)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class SoftwareUpdateZone:
|
|
51
|
+
"""Installation status of a software update in one zone."""
|
|
52
|
+
|
|
53
|
+
zone: str
|
|
54
|
+
status: str
|
|
55
|
+
installed: pendulum.DateTime | None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class SoftwareUpdate:
|
|
60
|
+
"""Software-update metadata and its status across zones."""
|
|
61
|
+
|
|
62
|
+
name: str | None = None
|
|
63
|
+
type: str | None = None
|
|
64
|
+
fmid: str | None = None
|
|
65
|
+
zones: list[SoftwareUpdateZone] = field(default_factory=list)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class MissingUpdateResolver:
|
|
70
|
+
"""Update that can resolve a hold on a missing update."""
|
|
71
|
+
|
|
72
|
+
name: str | None = None
|
|
73
|
+
received: bool | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class MissingUpdate:
|
|
78
|
+
"""Critical missing update, its hold details, and resolving updates."""
|
|
79
|
+
|
|
80
|
+
name: str | None = None
|
|
81
|
+
holdclass: str | None = None
|
|
82
|
+
holdsymptom: str | None = None
|
|
83
|
+
heldsysmod: str | None = None
|
|
84
|
+
fmid: str | None = None
|
|
85
|
+
fmiddesc: str | None = None
|
|
86
|
+
resolvers: list[MissingUpdateResolver] | None = field(default_factory=list)
|
|
87
|
+
tgtzones: list[str] = field(default_factory=list)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class MissingFixcat:
|
|
92
|
+
"""Fix category missing from an instance and its resolving updates."""
|
|
93
|
+
|
|
94
|
+
name: str | None = None
|
|
95
|
+
fixcats: list[str] = field(default_factory=list)
|
|
96
|
+
fmid: str | None = None
|
|
97
|
+
fmiddesc: str | None = None
|
|
98
|
+
resolvers: list[MissingUpdateResolver] | None = field(default_factory=list)
|
|
99
|
+
tgtzones: list[str] = field(default_factory=list)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class System:
|
|
6
|
+
"""System record returned by the z/OSMF topology API."""
|
|
7
|
+
|
|
8
|
+
name: str = field(metadata={"json_name": "systemName"})
|
|
9
|
+
nickName: str = field(metadata={"json_name": "systemNickName"})
|
|
10
|
+
sysplex: str = field(metadata={"json_name": "sysplexName"})
|
|
11
|
+
groups: str = field(default="", metadata={"json_name": "groupNames"})
|
|
12
|
+
cpcSerial: str = ""
|
|
13
|
+
zosVR: str = ""
|
|
14
|
+
jesType: str = ""
|
|
15
|
+
jesMemberName: str = ""
|
|
16
|
+
httpProxyName: str = ""
|
|
17
|
+
ftpDestinationName: str = ""
|
|
18
|
+
url: str = ""
|
|
19
|
+
cpcName: str = ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Group:
|
|
24
|
+
"""Group record and the system nicknames that belong to it."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
systems: str = field(metadata={"json_name": "systemNickNames"})
|
|
28
|
+
description: str = ""
|