iris-pex-embedded-python 3.7.2b2__py3-none-any.whl → 4.0.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.
- iop/__init__.py +276 -18
- iop/__main__.py +3 -3
- iop/cli/__init__.py +1 -0
- iop/cli/formatting.py +45 -0
- iop/{_cli.py → cli/main.py} +238 -216
- iop/cli/parser.py +165 -0
- iop/cli/types.py +73 -0
- iop/cls/IOP/Common.cls +11 -2
- iop/cls/IOP/PrivateSession/Duplex.cls +1 -1
- iop/cls/IOP/Service/Remote/Rest/v1.cls +152 -5
- iop/cls/IOP/Utils.cls +814 -7
- iop/cls/IOP/Wrapper.cls +1 -1
- iop/components/__init__.py +16 -0
- iop/{_async_request.py → components/async_request.py} +7 -7
- iop/components/business_host.py +363 -0
- iop/components/business_operation.py +84 -0
- iop/{_business_process.py → components/business_process.py} +145 -125
- iop/components/business_service.py +106 -0
- iop/{_common.py → components/common.py} +160 -164
- iop/{_debugpy.py → components/debugpy.py} +11 -11
- iop/{_generator_request.py → components/generator_request.py} +4 -4
- iop/{_inbound_adapter.py → components/inbound_adapter.py} +10 -18
- iop/{_log_manager.py → components/log_manager.py} +1 -1
- iop/{_outbound_adapter.py → components/outbound_adapter.py} +1 -1
- iop/components/polling_business_service.py +48 -0
- iop/{_private_session_duplex.py → components/private_session_duplex.py} +7 -9
- iop/{_private_session_process.py → components/private_session_process.py} +2 -2
- iop/{_settings.py → components/settings.py} +23 -3
- iop/messages/__init__.py +7 -0
- iop/messages/base.py +61 -0
- iop/{_decorators.py → messages/decorators.py} +8 -2
- iop/messages/dispatch.py +351 -0
- iop/{_persistent_message.py → messages/persistent.py} +24 -19
- iop/{_serialization.py → messages/serialization.py} +34 -21
- iop/{_message_validator.py → messages/validation.py} +15 -13
- iop/migration/__init__.py +1 -0
- iop/migration/io.py +61 -0
- iop/migration/manifest.py +534 -0
- iop/migration/plans.py +80 -0
- iop/migration/utils.py +1126 -0
- iop/production/__init__.py +55 -0
- iop/production/actions.py +525 -0
- iop/production/common.py +103 -0
- iop/production/component.py +234 -0
- iop/production/declarations.py +190 -0
- iop/production/declarative.py +214 -0
- iop/production/diff.py +327 -0
- iop/production/import_.py +296 -0
- iop/production/inspection.py +92 -0
- iop/production/model.py +1143 -0
- iop/production/planning.py +678 -0
- iop/production/reconstruction.py +341 -0
- iop/production/rendering.py +610 -0
- iop/production/runtime.py +91 -0
- iop/production/source_inference.py +729 -0
- iop/production/types.py +546 -0
- iop/production/validation.py +452 -0
- iop/runtime/__init__.py +1 -0
- iop/runtime/director.py +415 -0
- iop/runtime/environment.py +59 -0
- iop/{_iris.py → runtime/iris.py} +2 -2
- iop/runtime/local.py +145 -0
- iop/{_director_protocol.py → runtime/protocol.py} +25 -8
- iop/runtime/remote/__init__.py +12 -0
- iop/runtime/remote/client.py +90 -0
- iop/runtime/remote/director.py +283 -0
- iop/runtime/remote/migration.py +57 -0
- iop/runtime/remote/settings.py +69 -0
- iop/runtime/remote/setup.py +72 -0
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/METADATA +11 -2
- iris_pex_embedded_python-4.0.0.dist-info/RECORD +99 -0
- iris_pex_embedded_python-4.0.0.dist-info/entry_points.txt +2 -0
- iris_pex_embedded_python-4.0.0.dist-info/top_level.txt +1 -0
- grongier/__init__.py +0 -0
- grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -8
- grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -13
- grongier/cls/Grongier/PEX/BusinessService.cls +0 -8
- grongier/cls/Grongier/PEX/Common.cls +0 -10
- grongier/cls/Grongier/PEX/Director.cls +0 -10
- grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -4
- grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -13
- grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -4
- grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -8
- grongier/cls/Grongier/PEX/Message.cls +0 -13
- grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -8
- grongier/cls/Grongier/PEX/PickleMessage.cls +0 -13
- grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -8
- grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -14
- grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -14
- grongier/cls/Grongier/PEX/Test.cls +0 -10
- grongier/cls/Grongier/PEX/Utils.cls +0 -10
- grongier/cls/Grongier/Service/WSGI.cls +0 -4
- grongier/pex/__init__.py +0 -75
- grongier/pex/__main__.py +0 -4
- grongier/pex/_business_host.py +0 -1
- grongier/pex/_cli.py +0 -4
- grongier/pex/_common.py +0 -1
- grongier/pex/_director.py +0 -1
- grongier/pex/_utils.py +0 -1
- grongier/pex/wsgi/handlers.py +0 -104
- iop/_business_host.py +0 -322
- iop/_business_operation.py +0 -77
- iop/_business_service.py +0 -67
- iop/_director.py +0 -333
- iop/_dispatch.py +0 -194
- iop/_local.py +0 -106
- iop/_message.py +0 -45
- iop/_polling_business_service.py +0 -6
- iop/_remote.py +0 -473
- iop/_utils.py +0 -912
- iop/cls/IOP/Service/WSGI.cls +0 -310
- iop/wsgi/handlers.py +0 -112
- iris_pex_embedded_python-3.7.2b2.dist-info/RECORD +0 -96
- iris_pex_embedded_python-3.7.2b2.dist-info/entry_points.txt +0 -2
- iris_pex_embedded_python-3.7.2b2.dist-info/top_level.txt +0 -2
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/WHEEL +0 -0
- {iris_pex_embedded_python-3.7.2b2.dist-info → iris_pex_embedded_python-4.0.0.dist-info}/licenses/LICENSE +0 -0
iop/production/diff.py
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from .common import PRODUCTION_SETTING_FIELDS, _bool_text, _text_value
|
|
7
|
+
from .types import (
|
|
8
|
+
GraphEdge,
|
|
9
|
+
ProductionDiff,
|
|
10
|
+
ProductionDiffEntry,
|
|
11
|
+
_canonical_value,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .component import ComponentRef
|
|
16
|
+
from .model import Production
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _diff_productions(
|
|
20
|
+
desired: Production,
|
|
21
|
+
current: Production,
|
|
22
|
+
*,
|
|
23
|
+
include_graph_metadata: bool = False,
|
|
24
|
+
) -> ProductionDiff:
|
|
25
|
+
changes: list[ProductionDiffEntry] = []
|
|
26
|
+
warnings = _diff_warnings(desired, current)
|
|
27
|
+
|
|
28
|
+
if current.name != desired.name:
|
|
29
|
+
changes.append(
|
|
30
|
+
ProductionDiffEntry(
|
|
31
|
+
action="change",
|
|
32
|
+
kind="production",
|
|
33
|
+
path="production.name",
|
|
34
|
+
before=current.name,
|
|
35
|
+
after=desired.name,
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
_diff_mapping_values(
|
|
40
|
+
changes,
|
|
41
|
+
kind="production",
|
|
42
|
+
base_path="production",
|
|
43
|
+
before=_production_signature(current),
|
|
44
|
+
after=_production_signature(desired),
|
|
45
|
+
)
|
|
46
|
+
_diff_items(changes, desired=desired, current=current)
|
|
47
|
+
_diff_connections(
|
|
48
|
+
changes,
|
|
49
|
+
desired=desired,
|
|
50
|
+
current=current,
|
|
51
|
+
include_graph_metadata=include_graph_metadata,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return ProductionDiff(
|
|
55
|
+
production_name=desired.name,
|
|
56
|
+
changes=tuple(changes),
|
|
57
|
+
warnings=tuple(warnings),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _diff_warnings(desired: Production, current: Production) -> list[str]:
|
|
62
|
+
warnings = []
|
|
63
|
+
warnings.extend(f"desired: {warning}" for warning in desired.graph().warnings)
|
|
64
|
+
warnings.extend(f"current: {warning}" for warning in current.graph().warnings)
|
|
65
|
+
return warnings
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _production_signature(production: Production) -> dict[str, Any]:
|
|
69
|
+
signature = {
|
|
70
|
+
"testing_enabled": _bool_text(production.testing_enabled),
|
|
71
|
+
"log_general_trace_events": _bool_text(production.log_general_trace_events),
|
|
72
|
+
"actor_pool_size": _text_value(production.actor_pool_size),
|
|
73
|
+
"description": production.description,
|
|
74
|
+
}
|
|
75
|
+
signature.update(
|
|
76
|
+
{
|
|
77
|
+
field_name: _text_value(getattr(production, field_name))
|
|
78
|
+
for field_name in PRODUCTION_SETTING_FIELDS
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
return signature
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _item_signatures(production: Production) -> dict[str, dict[str, Any]]:
|
|
85
|
+
return {item.name: _item_signature(item) for item in production.items}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _item_signature(item: ComponentRef) -> dict[str, Any]:
|
|
89
|
+
return {
|
|
90
|
+
"class_name": item.class_name or "",
|
|
91
|
+
"category": item.category,
|
|
92
|
+
"pool_size": _text_value(item.pool_size),
|
|
93
|
+
"enabled": _bool_text(item.enabled),
|
|
94
|
+
"foreground": _bool_text(item.foreground),
|
|
95
|
+
"comment": item.comment,
|
|
96
|
+
"log_trace_events": _bool_text(item.log_trace_events),
|
|
97
|
+
"schedule": item.schedule,
|
|
98
|
+
"host_settings": _settings_signature(item.host_settings),
|
|
99
|
+
"adapter_settings": _settings_signature(item.adapter_settings),
|
|
100
|
+
"other_settings": _canonical_value(item.other_settings),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _settings_signature(settings: dict[str, Any]) -> dict[str, str]:
|
|
105
|
+
return {key: _text_value(settings[key]) for key in sorted(settings)}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _diff_items(
|
|
109
|
+
changes: list[ProductionDiffEntry],
|
|
110
|
+
*,
|
|
111
|
+
desired: Production,
|
|
112
|
+
current: Production,
|
|
113
|
+
) -> None:
|
|
114
|
+
desired_items = _item_signatures(desired)
|
|
115
|
+
current_items = _item_signatures(current)
|
|
116
|
+
|
|
117
|
+
for item_name in sorted(current_items.keys() - desired_items.keys()):
|
|
118
|
+
changes.append(
|
|
119
|
+
ProductionDiffEntry(
|
|
120
|
+
action="remove",
|
|
121
|
+
kind="item",
|
|
122
|
+
path=f"items.{item_name}",
|
|
123
|
+
before=current_items[item_name],
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
for item_name in sorted(desired_items.keys() - current_items.keys()):
|
|
128
|
+
changes.append(
|
|
129
|
+
ProductionDiffEntry(
|
|
130
|
+
action="add",
|
|
131
|
+
kind="item",
|
|
132
|
+
path=f"items.{item_name}",
|
|
133
|
+
after=desired_items[item_name],
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
for item_name in sorted(desired_items.keys() & current_items.keys()):
|
|
138
|
+
_diff_item_fields(
|
|
139
|
+
changes,
|
|
140
|
+
item_name=item_name,
|
|
141
|
+
before=current_items[item_name],
|
|
142
|
+
after=desired_items[item_name],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _diff_item_fields(
|
|
147
|
+
changes: list[ProductionDiffEntry],
|
|
148
|
+
*,
|
|
149
|
+
item_name: str,
|
|
150
|
+
before: dict[str, Any],
|
|
151
|
+
after: dict[str, Any],
|
|
152
|
+
) -> None:
|
|
153
|
+
scalar_fields = [
|
|
154
|
+
"class_name",
|
|
155
|
+
"category",
|
|
156
|
+
"pool_size",
|
|
157
|
+
"enabled",
|
|
158
|
+
"foreground",
|
|
159
|
+
"comment",
|
|
160
|
+
"log_trace_events",
|
|
161
|
+
"schedule",
|
|
162
|
+
]
|
|
163
|
+
_diff_mapping_values(
|
|
164
|
+
changes,
|
|
165
|
+
kind="item",
|
|
166
|
+
base_path=f"items.{item_name}",
|
|
167
|
+
before={field: before[field] for field in scalar_fields},
|
|
168
|
+
after={field: after[field] for field in scalar_fields},
|
|
169
|
+
)
|
|
170
|
+
_diff_mapping_values(
|
|
171
|
+
changes,
|
|
172
|
+
kind="setting",
|
|
173
|
+
base_path=f"items.{item_name}.settings.Host",
|
|
174
|
+
before=before["host_settings"],
|
|
175
|
+
after=after["host_settings"],
|
|
176
|
+
)
|
|
177
|
+
_diff_mapping_values(
|
|
178
|
+
changes,
|
|
179
|
+
kind="setting",
|
|
180
|
+
base_path=f"items.{item_name}.settings.Adapter",
|
|
181
|
+
before=before["adapter_settings"],
|
|
182
|
+
after=after["adapter_settings"],
|
|
183
|
+
)
|
|
184
|
+
if before["other_settings"] != after["other_settings"]:
|
|
185
|
+
changes.append(
|
|
186
|
+
ProductionDiffEntry(
|
|
187
|
+
action="change",
|
|
188
|
+
kind="setting",
|
|
189
|
+
path=f"items.{item_name}.settings.Other",
|
|
190
|
+
before=before["other_settings"],
|
|
191
|
+
after=after["other_settings"],
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _diff_mapping_values(
|
|
197
|
+
changes: list[ProductionDiffEntry],
|
|
198
|
+
*,
|
|
199
|
+
kind: str,
|
|
200
|
+
base_path: str,
|
|
201
|
+
before: dict[str, Any],
|
|
202
|
+
after: dict[str, Any],
|
|
203
|
+
) -> None:
|
|
204
|
+
for key in sorted(before.keys() - after.keys()):
|
|
205
|
+
changes.append(
|
|
206
|
+
ProductionDiffEntry(
|
|
207
|
+
action="remove",
|
|
208
|
+
kind=kind,
|
|
209
|
+
path=f"{base_path}.{key}",
|
|
210
|
+
before=before[key],
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
for key in sorted(after.keys() - before.keys()):
|
|
214
|
+
changes.append(
|
|
215
|
+
ProductionDiffEntry(
|
|
216
|
+
action="add",
|
|
217
|
+
kind=kind,
|
|
218
|
+
path=f"{base_path}.{key}",
|
|
219
|
+
after=after[key],
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
for key in sorted(before.keys() & after.keys()):
|
|
223
|
+
if before[key] != after[key]:
|
|
224
|
+
changes.append(
|
|
225
|
+
ProductionDiffEntry(
|
|
226
|
+
action="change",
|
|
227
|
+
kind=kind,
|
|
228
|
+
path=f"{base_path}.{key}",
|
|
229
|
+
before=before[key],
|
|
230
|
+
after=after[key],
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _diff_connections(
|
|
236
|
+
changes: list[ProductionDiffEntry],
|
|
237
|
+
*,
|
|
238
|
+
desired: Production,
|
|
239
|
+
current: Production,
|
|
240
|
+
include_graph_metadata: bool = False,
|
|
241
|
+
) -> None:
|
|
242
|
+
desired_connections = _connection_signature(
|
|
243
|
+
desired,
|
|
244
|
+
include_graph_metadata=include_graph_metadata,
|
|
245
|
+
)
|
|
246
|
+
current_connections = _connection_signature(
|
|
247
|
+
current,
|
|
248
|
+
include_graph_metadata=include_graph_metadata,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
for source in sorted(current_connections.keys() - desired_connections.keys()):
|
|
252
|
+
changes.append(
|
|
253
|
+
ProductionDiffEntry(
|
|
254
|
+
action="remove",
|
|
255
|
+
kind="connection",
|
|
256
|
+
path=f"connections.{_connection_source_path(source)}",
|
|
257
|
+
before=current_connections[source],
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
for source in sorted(desired_connections.keys() - current_connections.keys()):
|
|
261
|
+
changes.append(
|
|
262
|
+
ProductionDiffEntry(
|
|
263
|
+
action="add",
|
|
264
|
+
kind="connection",
|
|
265
|
+
path=f"connections.{_connection_source_path(source)}",
|
|
266
|
+
after=desired_connections[source],
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
for source in sorted(desired_connections.keys() & current_connections.keys()):
|
|
270
|
+
before = current_connections[source]
|
|
271
|
+
after = desired_connections[source]
|
|
272
|
+
if before != after:
|
|
273
|
+
changes.append(
|
|
274
|
+
ProductionDiffEntry(
|
|
275
|
+
action="change",
|
|
276
|
+
kind="connection",
|
|
277
|
+
path=f"connections.{_connection_source_path(source)}",
|
|
278
|
+
before=before,
|
|
279
|
+
after=after,
|
|
280
|
+
)
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _connection_signature(
|
|
285
|
+
production: Production,
|
|
286
|
+
*,
|
|
287
|
+
include_graph_metadata: bool = False,
|
|
288
|
+
) -> dict[tuple[str, str], list[Any]]:
|
|
289
|
+
if include_graph_metadata:
|
|
290
|
+
detailed_connections: dict[tuple[str, str], list[dict[str, Any]]] = {}
|
|
291
|
+
for edge in production.graph().edges:
|
|
292
|
+
source = (edge.source_item, edge.source_target_setting)
|
|
293
|
+
detailed_connections.setdefault(source, []).append(_edge_diff_value(edge))
|
|
294
|
+
return {
|
|
295
|
+
source: sorted(
|
|
296
|
+
values,
|
|
297
|
+
key=lambda value: json.dumps(value, sort_keys=True),
|
|
298
|
+
)
|
|
299
|
+
for source, values in sorted(detailed_connections.items())
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
connections: dict[tuple[str, str], set[str]] = {}
|
|
303
|
+
for edge in production.graph().edges:
|
|
304
|
+
source = (edge.source_item, edge.source_target_setting)
|
|
305
|
+
connections.setdefault(source, set()).add(edge.target)
|
|
306
|
+
return {
|
|
307
|
+
source: list(sorted(targets))
|
|
308
|
+
for source, targets in sorted(connections.items())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def _edge_diff_value(edge: GraphEdge) -> dict[str, Any]:
|
|
313
|
+
data: dict[str, Any] = {
|
|
314
|
+
"target": edge.target,
|
|
315
|
+
"origin": edge.origin,
|
|
316
|
+
"interaction": edge.interaction,
|
|
317
|
+
}
|
|
318
|
+
if edge.metadata:
|
|
319
|
+
data["metadata"] = _canonical_value(edge.metadata)
|
|
320
|
+
return data
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _connection_source_path(source: tuple[str, str]) -> str:
|
|
324
|
+
source_item, source_target_setting = source
|
|
325
|
+
if source_target_setting:
|
|
326
|
+
return f"{source_item}.{source_target_setting}"
|
|
327
|
+
return source_item
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .common import PRODUCTION_SETTING_FIELDS_BY_IRIS, _text_value
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _as_list(value: Any) -> list[Any]:
|
|
9
|
+
if value is None:
|
|
10
|
+
return []
|
|
11
|
+
if isinstance(value, list):
|
|
12
|
+
return value
|
|
13
|
+
return [value]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _production_payload(data: dict[str, Any]) -> tuple[str, dict[str, Any]]:
|
|
17
|
+
if not isinstance(data, dict) or not data:
|
|
18
|
+
raise ValueError("Production data must be a non-empty dictionary.")
|
|
19
|
+
|
|
20
|
+
if "Production" in data:
|
|
21
|
+
production_data = data["Production"]
|
|
22
|
+
if not isinstance(production_data, dict):
|
|
23
|
+
raise ValueError("Production data must be a dictionary.")
|
|
24
|
+
production_name = production_data.get("@Name") or "Production"
|
|
25
|
+
return production_name, production_data
|
|
26
|
+
|
|
27
|
+
production_name = next(iter(data.keys()))
|
|
28
|
+
production_data = data[production_name]
|
|
29
|
+
if not isinstance(production_data, dict):
|
|
30
|
+
raise ValueError("Production data must be a dictionary.")
|
|
31
|
+
production_name = production_data.get("@Name") or production_name
|
|
32
|
+
return production_name, production_data
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _split_settings(
|
|
36
|
+
settings: Any,
|
|
37
|
+
) -> tuple[dict[str, Any], dict[str, Any], list[dict[str, Any]]]:
|
|
38
|
+
host_settings: dict[str, Any] = {}
|
|
39
|
+
adapter_settings: dict[str, Any] = {}
|
|
40
|
+
other_settings: list[dict[str, Any]] = []
|
|
41
|
+
|
|
42
|
+
for setting in _as_list(settings):
|
|
43
|
+
if not isinstance(setting, dict):
|
|
44
|
+
continue
|
|
45
|
+
target = setting.get("@Target", "Host")
|
|
46
|
+
name = setting.get("@Name", "")
|
|
47
|
+
value = setting.get("#text", "")
|
|
48
|
+
if target in ("", "Host"):
|
|
49
|
+
host_settings[name] = value
|
|
50
|
+
elif target == "Adapter":
|
|
51
|
+
adapter_settings[name] = value
|
|
52
|
+
else:
|
|
53
|
+
other_settings.append(dict(setting))
|
|
54
|
+
return host_settings, adapter_settings, other_settings
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _split_production_settings(settings: Any) -> dict[str, Any]:
|
|
58
|
+
values: dict[str, Any] = {}
|
|
59
|
+
for setting in _as_list(settings):
|
|
60
|
+
if not isinstance(setting, dict):
|
|
61
|
+
continue
|
|
62
|
+
target = setting.get("@Target", "")
|
|
63
|
+
if target not in ("", "Production"):
|
|
64
|
+
continue
|
|
65
|
+
iris_name = str(setting.get("@Name", ""))
|
|
66
|
+
field_name = PRODUCTION_SETTING_FIELDS_BY_IRIS.get(iris_name)
|
|
67
|
+
if field_name:
|
|
68
|
+
values[field_name] = setting.get("#text", "")
|
|
69
|
+
return values
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _normalize_connections(
|
|
73
|
+
connections: Any,
|
|
74
|
+
) -> tuple[dict[str, list[dict[str, Any]]], set[str], list[str]]:
|
|
75
|
+
connection_map: dict[str, list[dict[str, Any]]] = {}
|
|
76
|
+
runtime_sources: set[str] = set()
|
|
77
|
+
warnings: list[str] = []
|
|
78
|
+
|
|
79
|
+
if connections is None:
|
|
80
|
+
return connection_map, runtime_sources, warnings
|
|
81
|
+
|
|
82
|
+
if isinstance(connections, dict):
|
|
83
|
+
warnings.extend(str(value) for value in _as_list(connections.get("warnings")))
|
|
84
|
+
if "items" in connections:
|
|
85
|
+
for item in _as_list(connections.get("items")):
|
|
86
|
+
if not isinstance(item, dict):
|
|
87
|
+
continue
|
|
88
|
+
source = (
|
|
89
|
+
item.get("item")
|
|
90
|
+
or item.get("name")
|
|
91
|
+
or item.get("source_item")
|
|
92
|
+
or ""
|
|
93
|
+
)
|
|
94
|
+
if not source:
|
|
95
|
+
continue
|
|
96
|
+
item_warnings = [value for value in _as_list(item.get("warnings")) if value]
|
|
97
|
+
warnings.extend(
|
|
98
|
+
f"{source}: {value}"
|
|
99
|
+
for value in item_warnings
|
|
100
|
+
)
|
|
101
|
+
discovered = False
|
|
102
|
+
for target in _as_list(item.get("connections")):
|
|
103
|
+
normalized = _normalize_connection_target(target)
|
|
104
|
+
if normalized is not None:
|
|
105
|
+
discovered = True
|
|
106
|
+
connection_map.setdefault(source, []).append(normalized)
|
|
107
|
+
for edge in _as_list(item.get("edges")):
|
|
108
|
+
normalized = _normalize_connection_target(edge)
|
|
109
|
+
if normalized is not None:
|
|
110
|
+
discovered = True
|
|
111
|
+
connection_map.setdefault(source, []).append(normalized)
|
|
112
|
+
if not item_warnings or discovered:
|
|
113
|
+
runtime_sources.add(source)
|
|
114
|
+
return connection_map, runtime_sources, warnings
|
|
115
|
+
|
|
116
|
+
for source, targets in connections.items():
|
|
117
|
+
if source in ("production", "warnings"):
|
|
118
|
+
continue
|
|
119
|
+
runtime_sources.add(str(source))
|
|
120
|
+
for target in _as_list(targets):
|
|
121
|
+
normalized = _normalize_connection_target(target)
|
|
122
|
+
if normalized is not None:
|
|
123
|
+
connection_map.setdefault(str(source), []).append(normalized)
|
|
124
|
+
return connection_map, runtime_sources, warnings
|
|
125
|
+
|
|
126
|
+
if isinstance(connections, list):
|
|
127
|
+
for edge in connections:
|
|
128
|
+
if not isinstance(edge, dict):
|
|
129
|
+
continue
|
|
130
|
+
source = edge.get("source_item") or edge.get("item") or ""
|
|
131
|
+
if not source and "." in str(edge.get("source", "")):
|
|
132
|
+
source, _, source_target_setting = str(edge["source"]).rpartition(".")
|
|
133
|
+
edge = {**edge, "source_target_setting": source_target_setting}
|
|
134
|
+
if not source:
|
|
135
|
+
continue
|
|
136
|
+
runtime_sources.add(str(source))
|
|
137
|
+
normalized = _normalize_connection_target(edge)
|
|
138
|
+
if normalized is not None:
|
|
139
|
+
connection_map.setdefault(str(source), []).append(normalized)
|
|
140
|
+
return connection_map, runtime_sources, warnings
|
|
141
|
+
|
|
142
|
+
warnings.append(f"Ignoring invalid runtime connections: {connections!r}")
|
|
143
|
+
return connection_map, runtime_sources, warnings
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _normalize_runtime_item_metadata(connections: Any) -> dict[str, dict[str, str]]:
|
|
147
|
+
metadata: dict[str, dict[str, str]] = {}
|
|
148
|
+
if not isinstance(connections, dict):
|
|
149
|
+
return metadata
|
|
150
|
+
for item in _as_list(connections.get("items")):
|
|
151
|
+
if not isinstance(item, dict):
|
|
152
|
+
continue
|
|
153
|
+
item_name = (
|
|
154
|
+
item.get("item")
|
|
155
|
+
or item.get("name")
|
|
156
|
+
or item.get("source_item")
|
|
157
|
+
or ""
|
|
158
|
+
)
|
|
159
|
+
if not item_name:
|
|
160
|
+
continue
|
|
161
|
+
adapter_class_name = (
|
|
162
|
+
item.get("adapter_class_name")
|
|
163
|
+
or item.get("adapter")
|
|
164
|
+
or item.get("AdapterClassName")
|
|
165
|
+
or item.get("Adapter")
|
|
166
|
+
or ""
|
|
167
|
+
)
|
|
168
|
+
item_metadata: dict[str, str] = {}
|
|
169
|
+
if adapter_class_name:
|
|
170
|
+
item_metadata["adapter_class_name"] = str(adapter_class_name)
|
|
171
|
+
kind = item.get("kind") or item.get("type") or item.get("role") or ""
|
|
172
|
+
if kind:
|
|
173
|
+
item_metadata["kind"] = str(kind)
|
|
174
|
+
if item.get("iop") is not None:
|
|
175
|
+
item_metadata["iop"] = str(item.get("iop"))
|
|
176
|
+
module = item.get("module") or item.get("%module") or ""
|
|
177
|
+
if module:
|
|
178
|
+
item_metadata["%module"] = str(module)
|
|
179
|
+
classname = item.get("classname") or item.get("%classname") or ""
|
|
180
|
+
if classname:
|
|
181
|
+
item_metadata["%classname"] = str(classname)
|
|
182
|
+
classpaths = item.get("classpaths") or item.get("%classpaths") or ""
|
|
183
|
+
if classpaths:
|
|
184
|
+
item_metadata["%classpaths"] = str(classpaths)
|
|
185
|
+
if item_metadata:
|
|
186
|
+
metadata[str(item_name)] = item_metadata
|
|
187
|
+
return metadata
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _normalize_connection_target(value: Any) -> dict[str, Any] | None:
|
|
191
|
+
if isinstance(value, str):
|
|
192
|
+
if not value:
|
|
193
|
+
return None
|
|
194
|
+
return {"target": value, "source_target_setting": ""}
|
|
195
|
+
if not isinstance(value, dict):
|
|
196
|
+
return None
|
|
197
|
+
target = value.get("target") or value.get("name") or value.get("to") or ""
|
|
198
|
+
if not target:
|
|
199
|
+
return None
|
|
200
|
+
source_target_setting = (
|
|
201
|
+
value.get("source_target_setting")
|
|
202
|
+
or value.get("source_port")
|
|
203
|
+
or value.get("port")
|
|
204
|
+
or ""
|
|
205
|
+
)
|
|
206
|
+
metadata = dict(value.get("metadata") or {})
|
|
207
|
+
known_keys = {
|
|
208
|
+
"target",
|
|
209
|
+
"name",
|
|
210
|
+
"to",
|
|
211
|
+
"source_target_setting",
|
|
212
|
+
"source_port",
|
|
213
|
+
"port",
|
|
214
|
+
"interaction",
|
|
215
|
+
"metadata",
|
|
216
|
+
"logical_name",
|
|
217
|
+
}
|
|
218
|
+
for key, item in value.items():
|
|
219
|
+
if key not in known_keys and item is not None:
|
|
220
|
+
metadata.setdefault(str(key), item)
|
|
221
|
+
normalized: dict[str, Any] = {
|
|
222
|
+
"target": str(target),
|
|
223
|
+
"source_target_setting": str(source_target_setting),
|
|
224
|
+
}
|
|
225
|
+
if value.get("interaction"):
|
|
226
|
+
normalized["interaction"] = str(value["interaction"])
|
|
227
|
+
if metadata:
|
|
228
|
+
normalized["metadata"] = metadata
|
|
229
|
+
return normalized
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _normalize_queue_info(value: Any) -> tuple[dict[str, dict[str, Any]], list[str]]:
|
|
233
|
+
queue_map: dict[str, dict[str, Any]] = {}
|
|
234
|
+
warnings: list[str] = []
|
|
235
|
+
if value is None:
|
|
236
|
+
return queue_map, warnings
|
|
237
|
+
|
|
238
|
+
if isinstance(value, dict):
|
|
239
|
+
warnings.extend(str(item) for item in _as_list(value.get("warnings")) if item)
|
|
240
|
+
if "items" in value:
|
|
241
|
+
for item in _as_list(value.get("items")):
|
|
242
|
+
normalized = _normalize_queue_item(item)
|
|
243
|
+
if normalized is None:
|
|
244
|
+
continue
|
|
245
|
+
item_name, info = normalized
|
|
246
|
+
queue_map[item_name] = info
|
|
247
|
+
return queue_map, warnings
|
|
248
|
+
|
|
249
|
+
for item_name, info in value.items():
|
|
250
|
+
if item_name in ("production", "warnings"):
|
|
251
|
+
continue
|
|
252
|
+
if isinstance(info, dict):
|
|
253
|
+
queue_map[str(item_name)] = dict(info)
|
|
254
|
+
return queue_map, warnings
|
|
255
|
+
|
|
256
|
+
if isinstance(value, list):
|
|
257
|
+
for item in value:
|
|
258
|
+
normalized = _normalize_queue_item(item)
|
|
259
|
+
if normalized is None:
|
|
260
|
+
continue
|
|
261
|
+
item_name, info = normalized
|
|
262
|
+
queue_map[item_name] = info
|
|
263
|
+
return queue_map, warnings
|
|
264
|
+
|
|
265
|
+
warnings.append(f"Ignoring invalid queue info: {value!r}")
|
|
266
|
+
return queue_map, warnings
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _normalize_queue_item(value: Any) -> tuple[str, dict[str, Any]] | None:
|
|
270
|
+
if not isinstance(value, dict):
|
|
271
|
+
return None
|
|
272
|
+
item_name = value.get("item") or value.get("name") or value.get("queue_name") or ""
|
|
273
|
+
if not item_name:
|
|
274
|
+
return None
|
|
275
|
+
info = dict(value)
|
|
276
|
+
info.pop("item", None)
|
|
277
|
+
info.setdefault("queue_name", info.pop("name", item_name))
|
|
278
|
+
return str(item_name), info
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _setting_targets(value: Any) -> list[str]:
|
|
282
|
+
text = _text_value(value).strip()
|
|
283
|
+
if not text:
|
|
284
|
+
return []
|
|
285
|
+
return [part.strip() for part in text.split(",") if part.strip()]
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _matching_host_setting(component: Any, target_name: str) -> str:
|
|
289
|
+
for name, value in component.host_settings.items():
|
|
290
|
+
if target_name in _setting_targets(value):
|
|
291
|
+
return name
|
|
292
|
+
return ""
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _is_internal_runtime_target(target_name: str) -> bool:
|
|
296
|
+
return target_name in {"Ens.Alert", "Ens.ScheduleHandler"}
|