python-rac 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.
- pyrac/__init__.py +4 -0
- pyrac/__main__.py +4 -0
- pyrac/cli.py +921 -0
- pyrac/client.py +2382 -0
- pyrac/desktop.py +5003 -0
- pyrac/legacy.py +8 -0
- pyrac/mcp_server.py +3845 -0
- pyrac/protocol/__init__.py +0 -0
- pyrac/protocol/v851.py +32 -0
- pyrac/protocol/v851_client.py +3453 -0
- pyrac/read_service.py +831 -0
- pyrac/v8311.py +1400 -0
- pyrac/web/__init__.py +0 -0
- pyrac/web/__main__.py +355 -0
- pyrac/web/app.py +2835 -0
- pyrac/web/workspace.py +1905 -0
- python_rac-0.2.0.dist-info/METADATA +843 -0
- python_rac-0.2.0.dist-info/RECORD +22 -0
- python_rac-0.2.0.dist-info/WHEEL +5 -0
- python_rac-0.2.0.dist-info/entry_points.txt +5 -0
- python_rac-0.2.0.dist-info/licenses/LICENSE +21 -0
- python_rac-0.2.0.dist-info/top_level.txt +1 -0
pyrac/mcp_server.py
ADDED
|
@@ -0,0 +1,3845 @@
|
|
|
1
|
+
"""Local MCP stdio interface; read-only unless mutations are explicitly enabled."""
|
|
2
|
+
import argparse
|
|
3
|
+
import asyncio
|
|
4
|
+
from functools import partial
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
from pydantic import StrictBool, StrictInt, StrictStr
|
|
7
|
+
from .client import RacClient, RacError
|
|
8
|
+
from .read_service import CLUSTER_LISTS, SERVER_LISTS, PROFILE_ACL_LISTS, ReadService
|
|
9
|
+
from .web.workspace import startup_endpoints
|
|
10
|
+
|
|
11
|
+
READ_TIMEOUT = 30.0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_server(service, *, allow_process_turn_off=False, allow_cluster_insert=False,
|
|
15
|
+
allow_agent_admin_register=False, allow_agent_admin_remove=False,
|
|
16
|
+
allow_cluster_admin_register=False, allow_cluster_admin_remove=False,
|
|
17
|
+
allow_rule_insert=False, allow_rule_remove=False, allow_rule_apply=False,
|
|
18
|
+
allow_service_settings_apply=False, allow_service_setting_remove=False,
|
|
19
|
+
allow_service_setting_update=False, allow_service_setting_insert=False,
|
|
20
|
+
allow_profile_remove=False, allow_profile_description_update=False,
|
|
21
|
+
allow_profile_config_update=False, allow_profile_access_flags_update=False,
|
|
22
|
+
allow_profile_role_fields_update=False, allow_profile_directory_remove=False,
|
|
23
|
+
allow_profile_directory_update=False, allow_profile_com_remove=False,
|
|
24
|
+
allow_profile_com_update=False, allow_profile_addin_remove=False,
|
|
25
|
+
allow_profile_addin_update=False, allow_profile_module_remove=False,
|
|
26
|
+
allow_profile_module_update=False, allow_profile_app_remove=False,
|
|
27
|
+
allow_profile_app_update=False, allow_profile_inet_remove=False,
|
|
28
|
+
allow_profile_inet_update=False,
|
|
29
|
+
allow_rule_priority_update=False, allow_rule_fields_update=False,
|
|
30
|
+
allow_rule_position_update=False,
|
|
31
|
+
allow_infobase_security_profile_update=False,
|
|
32
|
+
allow_infobase_safe_mode_security_profile_update=False,
|
|
33
|
+
allow_infobase_maximum_scheduled_jobs_shift_update=False,
|
|
34
|
+
allow_infobase_minimum_scheduled_jobs_period_update=False,
|
|
35
|
+
allow_infobase_configuration_unload_delay_update=False,
|
|
36
|
+
allow_infobase_local_speech_update=False,
|
|
37
|
+
allow_infobase_external_session_manager_update=False,
|
|
38
|
+
allow_infobase_reserve_working_processes_update=False,
|
|
39
|
+
allow_infobase_denied_message_update=False,
|
|
40
|
+
allow_infobase_license_distribution_update=False,
|
|
41
|
+
allow_infobase_sessions_deny_update=False,
|
|
42
|
+
allow_infobase_scheduled_jobs_deny_update=False,
|
|
43
|
+
allow_infobase_description_update=False,
|
|
44
|
+
allow_infobase_summary_description_update=False,
|
|
45
|
+
allow_infobase_registration_drop=False,
|
|
46
|
+
allow_infobase_existing_registration=False,
|
|
47
|
+
allow_infobase_database_create=False,
|
|
48
|
+
allow_limit_remove=False,
|
|
49
|
+
allow_limit_update=False, allow_counter_remove=False,
|
|
50
|
+
allow_counter_description_update=False,
|
|
51
|
+
allow_counter_collection_time_update=False,
|
|
52
|
+
allow_counter_duration_update=False, allow_counter_group_update=False,
|
|
53
|
+
allow_counter_filter_type_update=False, allow_counter_filter_update=False,
|
|
54
|
+
allow_counter_analysis_flags_update=False, allow_counter_clear=False,
|
|
55
|
+
allow_cluster_remove=False,
|
|
56
|
+
allow_cluster_name_update=False,
|
|
57
|
+
allow_cluster_expiration_timeout_update=False,
|
|
58
|
+
allow_cluster_security_level_update=False,
|
|
59
|
+
allow_cluster_session_fault_tolerance_level_update=False,
|
|
60
|
+
allow_cluster_load_balancing_mode_update=False,
|
|
61
|
+
allow_cluster_kill_problem_processes_update=False,
|
|
62
|
+
allow_cluster_kill_by_memory_with_dump_update=False,
|
|
63
|
+
allow_cluster_audit_update=False,
|
|
64
|
+
allow_cluster_ping_update=False,
|
|
65
|
+
allow_cluster_restart_schedule_update=False,
|
|
66
|
+
allow_server_restart_schedule_update=False,
|
|
67
|
+
allow_server_infobases_limit_update=False,
|
|
68
|
+
allow_server_connections_limit_update=False,
|
|
69
|
+
allow_server_safe_call_memory_limit_update=False,
|
|
70
|
+
allow_server_critical_total_memory_update=False,
|
|
71
|
+
allow_server_temporary_total_memory_update=False,
|
|
72
|
+
allow_server_temporary_memory_time_limit_update=False,
|
|
73
|
+
allow_server_spn_update=False,
|
|
74
|
+
allow_server_dedicate_managers_update=False,
|
|
75
|
+
allow_server_port_ranges_update=False, workspace_endpoints=None):
|
|
76
|
+
from mcp.server.mcpserver import MCPServer
|
|
77
|
+
|
|
78
|
+
server = MCPServer('pyrac', description=('1C RAC views' if
|
|
79
|
+
(allow_process_turn_off or allow_cluster_insert or allow_cluster_remove or
|
|
80
|
+
allow_agent_admin_register or allow_agent_admin_remove or
|
|
81
|
+
allow_cluster_admin_register or allow_cluster_admin_remove or
|
|
82
|
+
allow_rule_insert or allow_rule_remove or allow_rule_priority_update or
|
|
83
|
+
allow_rule_fields_update or allow_rule_position_update or
|
|
84
|
+
allow_infobase_security_profile_update or
|
|
85
|
+
allow_infobase_safe_mode_security_profile_update or
|
|
86
|
+
allow_infobase_maximum_scheduled_jobs_shift_update or
|
|
87
|
+
allow_infobase_minimum_scheduled_jobs_period_update or
|
|
88
|
+
allow_infobase_configuration_unload_delay_update or
|
|
89
|
+
allow_infobase_local_speech_update or
|
|
90
|
+
allow_infobase_external_session_manager_update or
|
|
91
|
+
allow_infobase_reserve_working_processes_update or
|
|
92
|
+
allow_infobase_denied_message_update or
|
|
93
|
+
allow_infobase_license_distribution_update or
|
|
94
|
+
allow_infobase_sessions_deny_update or
|
|
95
|
+
allow_infobase_scheduled_jobs_deny_update or
|
|
96
|
+
allow_infobase_description_update or
|
|
97
|
+
allow_infobase_summary_description_update or
|
|
98
|
+
allow_infobase_registration_drop or
|
|
99
|
+
allow_infobase_existing_registration or
|
|
100
|
+
allow_infobase_database_create or
|
|
101
|
+
allow_rule_apply or allow_service_settings_apply or
|
|
102
|
+
allow_service_setting_remove or allow_service_setting_update or
|
|
103
|
+
allow_service_setting_insert or allow_profile_remove or
|
|
104
|
+
allow_profile_description_update or allow_profile_config_update or
|
|
105
|
+
allow_profile_access_flags_update or allow_profile_role_fields_update or
|
|
106
|
+
allow_profile_directory_remove or allow_profile_directory_update or
|
|
107
|
+
allow_profile_com_remove or allow_profile_com_update or
|
|
108
|
+
allow_profile_addin_remove or allow_profile_addin_update or
|
|
109
|
+
allow_profile_module_remove or allow_profile_module_update or
|
|
110
|
+
allow_profile_app_remove or allow_profile_app_update or
|
|
111
|
+
allow_profile_inet_remove or allow_profile_inet_update or
|
|
112
|
+
allow_limit_remove or allow_limit_update or allow_counter_remove or
|
|
113
|
+
allow_counter_description_update or allow_counter_collection_time_update or
|
|
114
|
+
allow_counter_duration_update or allow_counter_group_update or
|
|
115
|
+
allow_counter_filter_type_update or allow_counter_filter_update or
|
|
116
|
+
allow_counter_analysis_flags_update or allow_counter_clear or
|
|
117
|
+
allow_cluster_name_update or
|
|
118
|
+
allow_cluster_expiration_timeout_update or
|
|
119
|
+
allow_cluster_security_level_update or
|
|
120
|
+
allow_cluster_session_fault_tolerance_level_update or
|
|
121
|
+
allow_cluster_load_balancing_mode_update or
|
|
122
|
+
allow_cluster_kill_problem_processes_update or
|
|
123
|
+
allow_cluster_kill_by_memory_with_dump_update or
|
|
124
|
+
allow_cluster_audit_update or
|
|
125
|
+
allow_cluster_ping_update or
|
|
126
|
+
allow_cluster_restart_schedule_update or
|
|
127
|
+
allow_server_restart_schedule_update or
|
|
128
|
+
allow_server_infobases_limit_update or
|
|
129
|
+
allow_server_connections_limit_update or
|
|
130
|
+
allow_server_safe_call_memory_limit_update or
|
|
131
|
+
allow_server_critical_total_memory_update or
|
|
132
|
+
allow_server_temporary_total_memory_update or
|
|
133
|
+
allow_server_temporary_memory_time_limit_update or
|
|
134
|
+
allow_server_spn_update or
|
|
135
|
+
allow_server_dedicate_managers_update or
|
|
136
|
+
allow_server_port_ranges_update) else 'Read-only 1C RAC views'))
|
|
137
|
+
|
|
138
|
+
async def safe_read(operation, *args):
|
|
139
|
+
try:
|
|
140
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
141
|
+
return ReadService.public_data(await operation(*args))
|
|
142
|
+
except Exception as exc:
|
|
143
|
+
# Do not let SDK logging include credentials or raw server frames,
|
|
144
|
+
# including errors raised by an unexpected decoder or backend.
|
|
145
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
146
|
+
|
|
147
|
+
if workspace_endpoints is not None:
|
|
148
|
+
@server.tool(description='List cluster navigation entries on every configured RAS '
|
|
149
|
+
'without requiring a search term; failed endpoints are isolated')
|
|
150
|
+
async def rac_workspace_list_clusters() -> list[dict]:
|
|
151
|
+
semaphore = asyncio.Semaphore(4)
|
|
152
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
153
|
+
|
|
154
|
+
async def list_one(index, endpoint):
|
|
155
|
+
host, port, protocol = endpoint
|
|
156
|
+
try:
|
|
157
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
158
|
+
rows = await ReadService(partial(RacClient, host, port, protocol)).clusters()
|
|
159
|
+
result = []
|
|
160
|
+
for row in rows:
|
|
161
|
+
if not isinstance(row, dict):
|
|
162
|
+
continue
|
|
163
|
+
try:
|
|
164
|
+
cluster = str(UUID(str(row['cluster'])))
|
|
165
|
+
except (KeyError, TypeError, ValueError):
|
|
166
|
+
continue
|
|
167
|
+
name = row.get('name')
|
|
168
|
+
object_host = row.get('host')
|
|
169
|
+
valid_host = (isinstance(object_host, str) and object_host.strip()
|
|
170
|
+
and ReadService._valid_search_text(object_host))
|
|
171
|
+
valid_name = (isinstance(name, str) and name.strip()
|
|
172
|
+
and ReadService._valid_search_text(name))
|
|
173
|
+
entry = {'ras': index, 'host': host, 'port': port,
|
|
174
|
+
'protocol': protocol, 'cluster': cluster,
|
|
175
|
+
'name': name if valid_name else
|
|
176
|
+
object_host if valid_host else 'Unnamed cluster'}
|
|
177
|
+
if valid_host:
|
|
178
|
+
entry['object_host'] = object_host
|
|
179
|
+
result.append(entry)
|
|
180
|
+
return result
|
|
181
|
+
except Exception:
|
|
182
|
+
return []
|
|
183
|
+
|
|
184
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
185
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
186
|
+
return [row for batch in batches for row in batch]
|
|
187
|
+
|
|
188
|
+
@server.tool(description='List infobase navigation entries across all startup RAS '
|
|
189
|
+
'without requiring a search term; isolate failed clusters')
|
|
190
|
+
async def rac_workspace_list_infobases() -> list[dict]:
|
|
191
|
+
semaphore = asyncio.Semaphore(4)
|
|
192
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
193
|
+
|
|
194
|
+
async def list_one(index, endpoint):
|
|
195
|
+
host, port, protocol = endpoint
|
|
196
|
+
result = []
|
|
197
|
+
try:
|
|
198
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
199
|
+
async with RacClient(host, port, protocol) as client:
|
|
200
|
+
for cluster_row in await client.list_clusters():
|
|
201
|
+
if not isinstance(cluster_row, dict):
|
|
202
|
+
continue
|
|
203
|
+
try:
|
|
204
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
205
|
+
except (KeyError, TypeError, ValueError):
|
|
206
|
+
continue
|
|
207
|
+
try:
|
|
208
|
+
bases = await client.list_infobases(UUID(cluster))
|
|
209
|
+
for row in bases:
|
|
210
|
+
if not isinstance(row, dict):
|
|
211
|
+
continue
|
|
212
|
+
try:
|
|
213
|
+
infobase = str(UUID(str(row['infobase'])))
|
|
214
|
+
except (KeyError, TypeError, ValueError):
|
|
215
|
+
continue
|
|
216
|
+
name = row.get('name')
|
|
217
|
+
valid_name = (isinstance(name, str) and name.strip()
|
|
218
|
+
and ReadService._valid_search_text(name))
|
|
219
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
220
|
+
'protocol': protocol, 'cluster': cluster,
|
|
221
|
+
'infobase': infobase,
|
|
222
|
+
'name': name if valid_name else 'Unnamed infobase'})
|
|
223
|
+
except Exception:
|
|
224
|
+
continue
|
|
225
|
+
return result
|
|
226
|
+
except Exception:
|
|
227
|
+
return result
|
|
228
|
+
|
|
229
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
230
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
231
|
+
return [row for batch in batches for row in batch]
|
|
232
|
+
|
|
233
|
+
@server.tool(description='List working-server navigation entries across all startup RAS '
|
|
234
|
+
'without a search term; isolate failed clusters')
|
|
235
|
+
async def rac_workspace_list_servers() -> list[dict]:
|
|
236
|
+
semaphore = asyncio.Semaphore(4)
|
|
237
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
238
|
+
|
|
239
|
+
async def list_one(index, endpoint):
|
|
240
|
+
host, port, protocol = endpoint
|
|
241
|
+
result = []
|
|
242
|
+
try:
|
|
243
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
244
|
+
async with RacClient(host, port, protocol) as client:
|
|
245
|
+
for cluster_row in await client.list_clusters():
|
|
246
|
+
if not isinstance(cluster_row, dict):
|
|
247
|
+
continue
|
|
248
|
+
try:
|
|
249
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
250
|
+
except (KeyError, TypeError, ValueError):
|
|
251
|
+
continue
|
|
252
|
+
try:
|
|
253
|
+
for row in await client.list_servers(UUID(cluster)):
|
|
254
|
+
if not isinstance(row, dict):
|
|
255
|
+
continue
|
|
256
|
+
try:
|
|
257
|
+
server_id = str(UUID(str(row['server'])))
|
|
258
|
+
except (KeyError, TypeError, ValueError):
|
|
259
|
+
continue
|
|
260
|
+
name, object_host = row.get('name'), row.get('host_name')
|
|
261
|
+
valid_name = (isinstance(name, str) and name.strip()
|
|
262
|
+
and ReadService._valid_search_text(name))
|
|
263
|
+
valid_host = (isinstance(object_host, str) and object_host.strip()
|
|
264
|
+
and ReadService._valid_search_text(object_host))
|
|
265
|
+
entry = {'ras': index, 'host': host, 'port': port,
|
|
266
|
+
'protocol': protocol, 'cluster': cluster,
|
|
267
|
+
'server': server_id, 'name': name if valid_name else
|
|
268
|
+
object_host if valid_host else 'Unnamed server'}
|
|
269
|
+
if valid_host:
|
|
270
|
+
entry['object_host'] = object_host
|
|
271
|
+
result.append(entry)
|
|
272
|
+
except Exception:
|
|
273
|
+
continue
|
|
274
|
+
except Exception:
|
|
275
|
+
pass # Keep completed clusters; never return server exception text.
|
|
276
|
+
return result
|
|
277
|
+
|
|
278
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
279
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
280
|
+
return [row for batch in batches for row in batch]
|
|
281
|
+
|
|
282
|
+
@server.tool(description='List working-process navigation entries across all startup RAS '
|
|
283
|
+
'without a search term; isolate failed clusters')
|
|
284
|
+
async def rac_workspace_list_processes() -> list[dict]:
|
|
285
|
+
semaphore = asyncio.Semaphore(4)
|
|
286
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
287
|
+
|
|
288
|
+
async def list_one(index, endpoint):
|
|
289
|
+
host, port, protocol = endpoint
|
|
290
|
+
result = []
|
|
291
|
+
try:
|
|
292
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
293
|
+
async with RacClient(host, port, protocol) as client:
|
|
294
|
+
for cluster_row in await client.list_clusters():
|
|
295
|
+
if not isinstance(cluster_row, dict):
|
|
296
|
+
continue
|
|
297
|
+
try:
|
|
298
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
299
|
+
except (KeyError, TypeError, ValueError):
|
|
300
|
+
continue
|
|
301
|
+
try:
|
|
302
|
+
for row in await client.list_processes(UUID(cluster)):
|
|
303
|
+
if not isinstance(row, dict):
|
|
304
|
+
continue
|
|
305
|
+
try:
|
|
306
|
+
process = str(UUID(str(row['process'])))
|
|
307
|
+
except (KeyError, TypeError, ValueError):
|
|
308
|
+
continue
|
|
309
|
+
pid, object_host = row.get('pid'), row.get('host')
|
|
310
|
+
valid_pid = (type(pid) is str and pid.isascii()
|
|
311
|
+
and pid.isdecimal() and len(pid) <= 20)
|
|
312
|
+
valid_host = (isinstance(object_host, str) and object_host.strip()
|
|
313
|
+
and ReadService._valid_search_text(object_host))
|
|
314
|
+
entry = {'ras': index, 'host': host, 'port': port,
|
|
315
|
+
'protocol': protocol, 'cluster': cluster,
|
|
316
|
+
'process': process,
|
|
317
|
+
'name': f'PID {pid}' if valid_pid else 'Working process'}
|
|
318
|
+
if valid_pid:
|
|
319
|
+
entry['pid'] = pid
|
|
320
|
+
if valid_host:
|
|
321
|
+
entry['object_host'] = object_host
|
|
322
|
+
result.append(entry)
|
|
323
|
+
except Exception:
|
|
324
|
+
continue
|
|
325
|
+
except Exception:
|
|
326
|
+
pass # Keep completed clusters; never return server exception text.
|
|
327
|
+
return result
|
|
328
|
+
|
|
329
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
330
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
331
|
+
return [row for batch in batches for row in batch]
|
|
332
|
+
|
|
333
|
+
@server.tool(description='List session navigation entries across all startup RAS '
|
|
334
|
+
'without a search term; isolate failed clusters')
|
|
335
|
+
async def rac_workspace_list_sessions() -> list[dict]:
|
|
336
|
+
semaphore = asyncio.Semaphore(4)
|
|
337
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
338
|
+
|
|
339
|
+
async def list_one(index, endpoint):
|
|
340
|
+
host, port, protocol = endpoint
|
|
341
|
+
result = []
|
|
342
|
+
try:
|
|
343
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
344
|
+
async with RacClient(host, port, protocol) as client:
|
|
345
|
+
for cluster_row in await client.list_clusters():
|
|
346
|
+
if not isinstance(cluster_row, dict):
|
|
347
|
+
continue
|
|
348
|
+
try:
|
|
349
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
350
|
+
except (KeyError, TypeError, ValueError):
|
|
351
|
+
continue
|
|
352
|
+
try:
|
|
353
|
+
for row in await client.list_sessions(UUID(cluster)):
|
|
354
|
+
if not isinstance(row, dict):
|
|
355
|
+
continue
|
|
356
|
+
try:
|
|
357
|
+
session = str(UUID(str(row['session_id'])))
|
|
358
|
+
except (KeyError, TypeError, ValueError):
|
|
359
|
+
continue
|
|
360
|
+
name = row.get('user_name')
|
|
361
|
+
if (not isinstance(name, str) or not name.strip()
|
|
362
|
+
or not ReadService._valid_search_text(name)):
|
|
363
|
+
name = 'Unnamed session'
|
|
364
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
365
|
+
'protocol': protocol, 'cluster': cluster,
|
|
366
|
+
'session': session, 'name': name})
|
|
367
|
+
except Exception:
|
|
368
|
+
continue
|
|
369
|
+
except Exception:
|
|
370
|
+
pass # Keep completed clusters; never return server exception text.
|
|
371
|
+
return result
|
|
372
|
+
|
|
373
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
374
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
375
|
+
return [row for batch in batches for row in batch]
|
|
376
|
+
|
|
377
|
+
@server.tool(description='List connection navigation entries across all startup RAS '
|
|
378
|
+
'without a search term; isolate failed clusters')
|
|
379
|
+
async def rac_workspace_list_connections() -> list[dict]:
|
|
380
|
+
semaphore = asyncio.Semaphore(4)
|
|
381
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
382
|
+
|
|
383
|
+
async def list_one(index, endpoint):
|
|
384
|
+
host, port, protocol = endpoint
|
|
385
|
+
result = []
|
|
386
|
+
try:
|
|
387
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
388
|
+
async with RacClient(host, port, protocol) as client:
|
|
389
|
+
for cluster_row in await client.list_clusters():
|
|
390
|
+
if not isinstance(cluster_row, dict):
|
|
391
|
+
continue
|
|
392
|
+
try:
|
|
393
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
394
|
+
except (KeyError, TypeError, ValueError):
|
|
395
|
+
continue
|
|
396
|
+
try:
|
|
397
|
+
for row in await client.list_connections(UUID(cluster)):
|
|
398
|
+
if not isinstance(row, dict):
|
|
399
|
+
continue
|
|
400
|
+
try:
|
|
401
|
+
connection = str(UUID(str(row['connection'])))
|
|
402
|
+
except (KeyError, TypeError, ValueError):
|
|
403
|
+
continue
|
|
404
|
+
object_host = row.get('host')
|
|
405
|
+
valid_host = (isinstance(object_host, str) and object_host.strip()
|
|
406
|
+
and ReadService._valid_search_text(object_host))
|
|
407
|
+
entry = {'ras': index, 'host': host, 'port': port,
|
|
408
|
+
'protocol': protocol, 'cluster': cluster,
|
|
409
|
+
'connection': connection,
|
|
410
|
+
'name': object_host if valid_host else 'Unnamed connection'}
|
|
411
|
+
if valid_host:
|
|
412
|
+
entry['object_host'] = object_host
|
|
413
|
+
result.append(entry)
|
|
414
|
+
except Exception:
|
|
415
|
+
continue
|
|
416
|
+
except Exception:
|
|
417
|
+
pass # Keep completed clusters; never return server exception text.
|
|
418
|
+
return result
|
|
419
|
+
|
|
420
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
421
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
422
|
+
return [row for batch in batches for row in batch]
|
|
423
|
+
|
|
424
|
+
@server.tool(description='List safe lock navigation entries across startup RAS '
|
|
425
|
+
'without a search term; isolate failed clusters')
|
|
426
|
+
async def rac_workspace_list_locks() -> list[dict]:
|
|
427
|
+
semaphore = asyncio.Semaphore(4)
|
|
428
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
429
|
+
|
|
430
|
+
async def list_one(index, endpoint):
|
|
431
|
+
host, port, protocol = endpoint
|
|
432
|
+
result = []
|
|
433
|
+
try:
|
|
434
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
435
|
+
async with RacClient(host, port, protocol) as client:
|
|
436
|
+
for cluster_row in await client.list_clusters():
|
|
437
|
+
if not isinstance(cluster_row, dict):
|
|
438
|
+
continue
|
|
439
|
+
try:
|
|
440
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
441
|
+
except (KeyError, TypeError, ValueError):
|
|
442
|
+
continue
|
|
443
|
+
try:
|
|
444
|
+
for row in await client.list_locks(UUID(cluster)):
|
|
445
|
+
if not isinstance(row, dict):
|
|
446
|
+
continue
|
|
447
|
+
try:
|
|
448
|
+
connection = str(UUID(str(row['connection'])))
|
|
449
|
+
obj = str(UUID(str(row['object'])))
|
|
450
|
+
except (KeyError, TypeError, ValueError):
|
|
451
|
+
continue
|
|
452
|
+
label = row.get('lock-descr')
|
|
453
|
+
name = (label if isinstance(label, str) and label.strip()
|
|
454
|
+
and ReadService._valid_search_text(label)
|
|
455
|
+
else 'Lock')
|
|
456
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
457
|
+
'protocol': protocol, 'cluster': cluster,
|
|
458
|
+
'connection': connection, 'object': obj,
|
|
459
|
+
'name': name})
|
|
460
|
+
except Exception:
|
|
461
|
+
continue
|
|
462
|
+
except Exception:
|
|
463
|
+
pass # Keep completed clusters without private exception text.
|
|
464
|
+
return result
|
|
465
|
+
|
|
466
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
467
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
468
|
+
return [row for batch in batches for row in batch]
|
|
469
|
+
|
|
470
|
+
@server.tool(description='List cluster-service navigation entries across all startup RAS '
|
|
471
|
+
'without a search term; isolate failed clusters')
|
|
472
|
+
async def rac_workspace_list_services() -> list[dict]:
|
|
473
|
+
semaphore = asyncio.Semaphore(4)
|
|
474
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
475
|
+
|
|
476
|
+
async def list_one(index, endpoint):
|
|
477
|
+
host, port, protocol = endpoint
|
|
478
|
+
result = []
|
|
479
|
+
try:
|
|
480
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
481
|
+
async with RacClient(host, port, protocol) as client:
|
|
482
|
+
for cluster_row in await client.list_clusters():
|
|
483
|
+
if not isinstance(cluster_row, dict):
|
|
484
|
+
continue
|
|
485
|
+
try:
|
|
486
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
487
|
+
except (KeyError, TypeError, ValueError):
|
|
488
|
+
continue
|
|
489
|
+
try:
|
|
490
|
+
for row in await client.list_services(UUID(cluster)):
|
|
491
|
+
if not isinstance(row, dict):
|
|
492
|
+
continue
|
|
493
|
+
name = row.get('name')
|
|
494
|
+
if (not isinstance(name, str) or not name.strip()
|
|
495
|
+
or not ReadService._valid_search_text(name)):
|
|
496
|
+
continue
|
|
497
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
498
|
+
'protocol': protocol, 'cluster': cluster,
|
|
499
|
+
'name': name})
|
|
500
|
+
except Exception:
|
|
501
|
+
continue
|
|
502
|
+
except Exception:
|
|
503
|
+
pass # Keep completed clusters; never return server exception text.
|
|
504
|
+
return result
|
|
505
|
+
|
|
506
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
507
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
508
|
+
return [row for batch in batches for row in batch]
|
|
509
|
+
|
|
510
|
+
@server.tool(description='List security-profile navigation entries across all startup RAS '
|
|
511
|
+
'without a search term; isolate failed clusters')
|
|
512
|
+
async def rac_workspace_list_profiles() -> list[dict]:
|
|
513
|
+
semaphore = asyncio.Semaphore(4)
|
|
514
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
515
|
+
|
|
516
|
+
async def list_one(index, endpoint):
|
|
517
|
+
host, port, protocol = endpoint
|
|
518
|
+
result = []
|
|
519
|
+
try:
|
|
520
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
521
|
+
async with RacClient(host, port, protocol) as client:
|
|
522
|
+
for cluster_row in await client.list_clusters():
|
|
523
|
+
if not isinstance(cluster_row, dict):
|
|
524
|
+
continue
|
|
525
|
+
try:
|
|
526
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
527
|
+
except (KeyError, TypeError, ValueError):
|
|
528
|
+
continue
|
|
529
|
+
try:
|
|
530
|
+
for row in await client.list_profiles(UUID(cluster)):
|
|
531
|
+
if not isinstance(row, dict):
|
|
532
|
+
continue
|
|
533
|
+
name = row.get('name')
|
|
534
|
+
if (not isinstance(name, str) or not name.strip()
|
|
535
|
+
or not ReadService._valid_search_text(name)):
|
|
536
|
+
continue
|
|
537
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
538
|
+
'protocol': protocol, 'cluster': cluster,
|
|
539
|
+
'name': name})
|
|
540
|
+
except Exception:
|
|
541
|
+
continue
|
|
542
|
+
except Exception:
|
|
543
|
+
pass # Keep completed clusters; never return server exception text.
|
|
544
|
+
return result
|
|
545
|
+
|
|
546
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
547
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
548
|
+
return [row for batch in batches for row in batch]
|
|
549
|
+
|
|
550
|
+
@server.tool(description='List managers, counters or limits across startup RAS without '
|
|
551
|
+
'a search term; return navigation fields only')
|
|
552
|
+
async def rac_workspace_list_cluster_inventory(kind: StrictStr) -> list[dict]:
|
|
553
|
+
options = {'managers': ('list_managers', 'manager', 'manager'),
|
|
554
|
+
'counters': ('list_counters', 'counter', 'name'),
|
|
555
|
+
'limits': ('list_limits', 'limit', 'name')}
|
|
556
|
+
if kind not in options:
|
|
557
|
+
raise ValueError('Unsupported cluster inventory')
|
|
558
|
+
method, row_kind, selector_key = options[kind]
|
|
559
|
+
semaphore = asyncio.Semaphore(4)
|
|
560
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
561
|
+
|
|
562
|
+
async def list_one(index, endpoint):
|
|
563
|
+
host, port, protocol = endpoint
|
|
564
|
+
result = []
|
|
565
|
+
try:
|
|
566
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
567
|
+
async with RacClient(host, port, protocol) as client:
|
|
568
|
+
for cluster_row in await client.list_clusters():
|
|
569
|
+
if not isinstance(cluster_row, dict):
|
|
570
|
+
continue
|
|
571
|
+
try:
|
|
572
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
573
|
+
except (KeyError, TypeError, ValueError):
|
|
574
|
+
continue
|
|
575
|
+
try:
|
|
576
|
+
for row in await getattr(client, method)(UUID(cluster)):
|
|
577
|
+
if not isinstance(row, dict):
|
|
578
|
+
continue
|
|
579
|
+
if row_kind == 'manager':
|
|
580
|
+
try:
|
|
581
|
+
selector = str(UUID(str(row[selector_key])))
|
|
582
|
+
except (KeyError, TypeError, ValueError):
|
|
583
|
+
continue
|
|
584
|
+
label = row.get('host')
|
|
585
|
+
name = (label if isinstance(label, str) and label.strip()
|
|
586
|
+
and ReadService._valid_search_text(label)
|
|
587
|
+
else 'Cluster manager')
|
|
588
|
+
else:
|
|
589
|
+
selector = row.get(selector_key)
|
|
590
|
+
if (not isinstance(selector, str) or not selector.strip()
|
|
591
|
+
or not ReadService._valid_search_text(selector)):
|
|
592
|
+
continue
|
|
593
|
+
name = selector
|
|
594
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
595
|
+
'protocol': protocol, 'cluster': cluster,
|
|
596
|
+
'kind': row_kind, 'selector': selector,
|
|
597
|
+
'name': name})
|
|
598
|
+
except Exception:
|
|
599
|
+
continue
|
|
600
|
+
except Exception:
|
|
601
|
+
pass # Keep completed clusters; never return server exception text.
|
|
602
|
+
return result
|
|
603
|
+
|
|
604
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
605
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
606
|
+
return [row for batch in batches for row in batch]
|
|
607
|
+
|
|
608
|
+
@server.tool(description='List rule, service-setting or transfer-directory navigation '
|
|
609
|
+
'across startup RAS without a search term; return safe fields only')
|
|
610
|
+
async def rac_workspace_list_server_inventory(kind: StrictStr) -> list[dict]:
|
|
611
|
+
options = {'rules': ('list_rules', 'rule', 'object-type', 'Assignment rule'),
|
|
612
|
+
'service-settings': ('list_service_settings', 'setting',
|
|
613
|
+
'service-name', 'Service setting'),
|
|
614
|
+
'transfer-directories': ('list_service_data_dirs_for_transfer',
|
|
615
|
+
'service-name', 'service-name', '')}
|
|
616
|
+
if kind not in options:
|
|
617
|
+
raise ValueError('Unsupported server inventory')
|
|
618
|
+
method, selector_key, label_key, fallback = options[kind]
|
|
619
|
+
semaphore = asyncio.Semaphore(4)
|
|
620
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
621
|
+
|
|
622
|
+
async def list_one(index, endpoint):
|
|
623
|
+
host, port, protocol = endpoint
|
|
624
|
+
result = []
|
|
625
|
+
try:
|
|
626
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
627
|
+
async with RacClient(host, port, protocol) as client:
|
|
628
|
+
for cluster_row in await client.list_clusters():
|
|
629
|
+
if not isinstance(cluster_row, dict):
|
|
630
|
+
continue
|
|
631
|
+
try:
|
|
632
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
633
|
+
except (KeyError, TypeError, ValueError):
|
|
634
|
+
continue
|
|
635
|
+
try:
|
|
636
|
+
servers = await client.list_servers(UUID(cluster))
|
|
637
|
+
except Exception:
|
|
638
|
+
continue
|
|
639
|
+
for server_row in servers:
|
|
640
|
+
if not isinstance(server_row, dict):
|
|
641
|
+
continue
|
|
642
|
+
try:
|
|
643
|
+
server_id = str(UUID(str(server_row['server'])))
|
|
644
|
+
except (KeyError, TypeError, ValueError):
|
|
645
|
+
continue
|
|
646
|
+
try:
|
|
647
|
+
for row in await getattr(client, method)(
|
|
648
|
+
UUID(cluster), UUID(server_id)):
|
|
649
|
+
if not isinstance(row, dict):
|
|
650
|
+
continue
|
|
651
|
+
entry = {'ras': index, 'host': host, 'port': port,
|
|
652
|
+
'protocol': protocol, 'cluster': cluster,
|
|
653
|
+
'server': server_id, 'kind': kind}
|
|
654
|
+
if kind == 'transfer-directories':
|
|
655
|
+
service_name = row.get(selector_key)
|
|
656
|
+
if (not isinstance(service_name, str)
|
|
657
|
+
or not service_name.strip()
|
|
658
|
+
or not ReadService._valid_search_text(service_name)):
|
|
659
|
+
continue
|
|
660
|
+
entry.update({'service-name': service_name,
|
|
661
|
+
'name': service_name})
|
|
662
|
+
infobase = row.get('infobase-name')
|
|
663
|
+
if (isinstance(infobase, str)
|
|
664
|
+
and ReadService._valid_search_text(infobase)):
|
|
665
|
+
entry['infobase-name'] = infobase
|
|
666
|
+
else:
|
|
667
|
+
try:
|
|
668
|
+
selector = str(UUID(str(row[selector_key])))
|
|
669
|
+
except (KeyError, TypeError, ValueError):
|
|
670
|
+
continue
|
|
671
|
+
label = row.get(label_key)
|
|
672
|
+
name = (label if isinstance(label, str) and label.strip()
|
|
673
|
+
and ReadService._valid_search_text(label)
|
|
674
|
+
else fallback)
|
|
675
|
+
entry.update({selector_key: selector, 'name': name})
|
|
676
|
+
result.append(entry)
|
|
677
|
+
except Exception:
|
|
678
|
+
continue
|
|
679
|
+
except Exception:
|
|
680
|
+
pass # Keep completed servers without exposing connection errors.
|
|
681
|
+
return result
|
|
682
|
+
|
|
683
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
684
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
685
|
+
return [row for batch in batches for row in batch]
|
|
686
|
+
|
|
687
|
+
@server.tool(description='List safe agent or cluster administrator names across '
|
|
688
|
+
'startup RAS without a search term')
|
|
689
|
+
async def rac_workspace_list_administrators(kind: StrictStr) -> list[dict]:
|
|
690
|
+
if kind not in ('agent-admins', 'cluster-admins'):
|
|
691
|
+
raise ValueError('Unsupported administrator inventory')
|
|
692
|
+
semaphore = asyncio.Semaphore(4)
|
|
693
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
694
|
+
|
|
695
|
+
async def list_one(index, endpoint):
|
|
696
|
+
host, port, protocol = endpoint
|
|
697
|
+
result = []
|
|
698
|
+
try:
|
|
699
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
700
|
+
async with RacClient(host, port, protocol) as client:
|
|
701
|
+
if kind == 'agent-admins':
|
|
702
|
+
for row in await client.list_agent_admins():
|
|
703
|
+
if not isinstance(row, dict):
|
|
704
|
+
continue
|
|
705
|
+
name = row.get('name')
|
|
706
|
+
if (isinstance(name, str) and name.strip()
|
|
707
|
+
and ReadService._valid_search_text(name)):
|
|
708
|
+
result.append({'ras': index, 'host': host, 'port': port,
|
|
709
|
+
'protocol': protocol, 'kind': kind, 'name': name})
|
|
710
|
+
else:
|
|
711
|
+
for cluster_row in await client.list_clusters():
|
|
712
|
+
if not isinstance(cluster_row, dict):
|
|
713
|
+
continue
|
|
714
|
+
try:
|
|
715
|
+
cluster = str(UUID(str(cluster_row['cluster'])))
|
|
716
|
+
except (KeyError, TypeError, ValueError):
|
|
717
|
+
continue
|
|
718
|
+
try:
|
|
719
|
+
for row in await client.list_cluster_admins(UUID(cluster)):
|
|
720
|
+
if not isinstance(row, dict):
|
|
721
|
+
continue
|
|
722
|
+
name = row.get('name')
|
|
723
|
+
if (isinstance(name, str) and name.strip()
|
|
724
|
+
and ReadService._valid_search_text(name)):
|
|
725
|
+
result.append({'ras': index, 'host': host,
|
|
726
|
+
'port': port, 'protocol': protocol,
|
|
727
|
+
'kind': kind, 'cluster': cluster,
|
|
728
|
+
'name': name})
|
|
729
|
+
except Exception:
|
|
730
|
+
continue
|
|
731
|
+
except Exception:
|
|
732
|
+
pass # Keep completed clusters, not server exception text.
|
|
733
|
+
return result
|
|
734
|
+
|
|
735
|
+
batches = await asyncio.gather(*(list_one(index, endpoint)
|
|
736
|
+
for index, endpoint in enumerate(workspace_endpoints)))
|
|
737
|
+
return [row for batch in batches for row in batch]
|
|
738
|
+
|
|
739
|
+
@server.tool(description='Read-only cross-RAS search of verified inventories; '
|
|
740
|
+
'returns endpoint and object navigation fields only')
|
|
741
|
+
async def rac_workspace_search(query: StrictStr, scope: StrictStr) -> list[dict]:
|
|
742
|
+
methods = {'clusters': 'search_clusters', 'infobases': 'search_infobases',
|
|
743
|
+
'profiles': 'search_profiles', 'servers': 'search_working_servers',
|
|
744
|
+
'processes': 'search_working_processes', 'sessions': 'search_sessions',
|
|
745
|
+
'connections': 'search_connections', 'managers': 'search_managers',
|
|
746
|
+
'counters': 'search_counters', 'limits': 'search_limits',
|
|
747
|
+
'services': 'search_services', 'locks': 'search_locks',
|
|
748
|
+
'agent-admins': 'search_agent_admins',
|
|
749
|
+
'cluster-admins': 'search_cluster_admins',
|
|
750
|
+
'rules': 'search_rules',
|
|
751
|
+
'service-settings': 'search_service_settings',
|
|
752
|
+
'transfer-directories': 'search_transfer_directories',
|
|
753
|
+
**{f'profile-{kind if kind != "directory" else "directories"}':
|
|
754
|
+
kind for kind in PROFILE_ACL_LISTS}}
|
|
755
|
+
if scope not in methods:
|
|
756
|
+
raise ValueError('Invalid workspace search scope')
|
|
757
|
+
needle = ReadService._search_needle(query)
|
|
758
|
+
semaphore = asyncio.Semaphore(4)
|
|
759
|
+
deadline = asyncio.get_running_loop().time() + READ_TIMEOUT
|
|
760
|
+
|
|
761
|
+
async def search_one(index, endpoint):
|
|
762
|
+
host, port, protocol = endpoint
|
|
763
|
+
try:
|
|
764
|
+
async with asyncio.timeout_at(deadline), semaphore:
|
|
765
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
766
|
+
if scope.startswith('profile-'):
|
|
767
|
+
rows = await reader.search_profile_acl_all(methods[scope], needle)
|
|
768
|
+
else:
|
|
769
|
+
rows = await getattr(reader, methods[scope])(needle)
|
|
770
|
+
results = []
|
|
771
|
+
for row in rows:
|
|
772
|
+
address = ReadService.public_data(row)
|
|
773
|
+
if 'host' in address:
|
|
774
|
+
address['object_host'] = address.pop('host')
|
|
775
|
+
results.append({**address, 'ras': index, 'host': host,
|
|
776
|
+
'port': port, 'protocol': protocol})
|
|
777
|
+
return results
|
|
778
|
+
except Exception:
|
|
779
|
+
# A failed RAS must not hide the healthy endpoints or leak its error.
|
|
780
|
+
return []
|
|
781
|
+
|
|
782
|
+
batches = await asyncio.gather(*(search_one(i, endpoint)
|
|
783
|
+
for i, endpoint in enumerate(workspace_endpoints)))
|
|
784
|
+
return [row for batch in batches for row in batch]
|
|
785
|
+
|
|
786
|
+
@server.tool(description='Read-only cluster overview from one configured RAS; '
|
|
787
|
+
'ras is the index returned by rac_workspace_search')
|
|
788
|
+
async def rac_workspace_cluster_overview(ras: StrictInt, cluster_id: StrictStr) -> dict:
|
|
789
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
790
|
+
raise ValueError('Invalid RAS index')
|
|
791
|
+
cluster = str(UUID(cluster_id))
|
|
792
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
793
|
+
try:
|
|
794
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
795
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
796
|
+
record = ReadService.public_data(await reader.cluster(cluster))
|
|
797
|
+
return {'ras': ras, 'host': host, 'port': port,
|
|
798
|
+
'protocol': protocol, **record}
|
|
799
|
+
except Exception as exc:
|
|
800
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
801
|
+
|
|
802
|
+
@server.tool(description='List a verified cluster inventory from a selected startup RAS')
|
|
803
|
+
async def rac_workspace_list_cluster_group(ras: StrictInt, cluster_id: StrictStr,
|
|
804
|
+
group: StrictStr) -> dict:
|
|
805
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
806
|
+
raise ValueError('Invalid RAS index')
|
|
807
|
+
if group not in CLUSTER_LISTS:
|
|
808
|
+
raise ValueError('Invalid cluster group')
|
|
809
|
+
cluster = str(UUID(cluster_id))
|
|
810
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
811
|
+
try:
|
|
812
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
813
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
814
|
+
rows = ReadService.public_data(await reader.list_group(cluster, group))
|
|
815
|
+
for row in rows:
|
|
816
|
+
if 'host' in row:
|
|
817
|
+
row['object_host'] = row.pop('host')
|
|
818
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
819
|
+
'cluster': cluster, 'group': group, 'rows': rows}
|
|
820
|
+
except Exception as exc:
|
|
821
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
822
|
+
|
|
823
|
+
@server.tool(description='Read one server, process, session, infobase or connection '
|
|
824
|
+
'from a selected startup RAS')
|
|
825
|
+
async def rac_workspace_object_detail(ras: StrictInt, cluster_id: StrictStr,
|
|
826
|
+
kind: StrictStr, object_id: StrictStr) -> dict:
|
|
827
|
+
methods = {'server': 'server', 'process': 'process', 'session': 'session',
|
|
828
|
+
'infobase': 'infobase_info', 'connection': 'connection'}
|
|
829
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
830
|
+
raise ValueError('Invalid RAS index')
|
|
831
|
+
if kind not in methods:
|
|
832
|
+
raise ValueError('Invalid object kind')
|
|
833
|
+
cluster, object_id = str(UUID(cluster_id)), str(UUID(object_id))
|
|
834
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
835
|
+
try:
|
|
836
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
837
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
838
|
+
record = ReadService.public_data(
|
|
839
|
+
await getattr(reader, methods[kind])(cluster, object_id))
|
|
840
|
+
if 'host' in record:
|
|
841
|
+
record['object_host'] = record.pop('host')
|
|
842
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
843
|
+
'cluster': cluster, 'kind': kind, 'object': record}
|
|
844
|
+
except Exception as exc:
|
|
845
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
846
|
+
|
|
847
|
+
@server.tool(description='List rules, service settings or transfer directories '
|
|
848
|
+
'of a working server on the selected startup RAS')
|
|
849
|
+
async def rac_workspace_list_server_group(ras: StrictInt, cluster_id: StrictStr,
|
|
850
|
+
server_id: StrictStr, group: StrictStr) -> dict:
|
|
851
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
852
|
+
raise ValueError('Invalid RAS index')
|
|
853
|
+
if group not in SERVER_LISTS:
|
|
854
|
+
raise ValueError('Invalid server group')
|
|
855
|
+
cluster, server_id = str(UUID(cluster_id)), str(UUID(server_id))
|
|
856
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
857
|
+
try:
|
|
858
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
859
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
860
|
+
rows = ReadService.public_data(
|
|
861
|
+
await reader.list_server_group(cluster, server_id, group))
|
|
862
|
+
for row in rows:
|
|
863
|
+
if 'host' in row:
|
|
864
|
+
row['object_host'] = row.pop('host')
|
|
865
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
866
|
+
'cluster': cluster, 'server': server_id, 'group': group,
|
|
867
|
+
'rows': rows}
|
|
868
|
+
except Exception as exc:
|
|
869
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
870
|
+
|
|
871
|
+
@server.tool(description='List one verified security-profile ACL subtype on '
|
|
872
|
+
'the selected startup RAS')
|
|
873
|
+
async def rac_workspace_list_profile_acl(ras: StrictInt, cluster_id: StrictStr,
|
|
874
|
+
profile_name: StrictStr,
|
|
875
|
+
acl_type: StrictStr) -> dict:
|
|
876
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
877
|
+
raise ValueError('Invalid RAS index')
|
|
878
|
+
if acl_type not in PROFILE_ACL_LISTS:
|
|
879
|
+
raise ValueError('Invalid profile ACL type')
|
|
880
|
+
cluster = str(UUID(cluster_id))
|
|
881
|
+
ReadService._validated_profile_name(profile_name)
|
|
882
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
883
|
+
try:
|
|
884
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
885
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
886
|
+
rows = ReadService.public_data(
|
|
887
|
+
await reader.list_profile_acl(cluster, profile_name, acl_type))
|
|
888
|
+
for row in rows:
|
|
889
|
+
if 'host' in row:
|
|
890
|
+
row['object_host'] = row.pop('host')
|
|
891
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
892
|
+
'cluster': cluster, 'profile': profile_name,
|
|
893
|
+
'acl_type': acl_type, 'rows': rows}
|
|
894
|
+
except Exception as exc:
|
|
895
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
896
|
+
|
|
897
|
+
@server.tool(description='List sessions, connections or binary storages of an '
|
|
898
|
+
'infobase on the selected startup RAS')
|
|
899
|
+
async def rac_workspace_list_infobase_group(ras: StrictInt, cluster_id: StrictStr,
|
|
900
|
+
infobase_id: StrictStr,
|
|
901
|
+
group: StrictStr) -> dict:
|
|
902
|
+
methods = {'sessions': 'sessions_in_infobase',
|
|
903
|
+
'connections': 'connections_in_infobase',
|
|
904
|
+
'binary-storages': 'binary_storages'}
|
|
905
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
906
|
+
raise ValueError('Invalid RAS index')
|
|
907
|
+
if group not in methods:
|
|
908
|
+
raise ValueError('Invalid infobase group')
|
|
909
|
+
cluster, infobase = str(UUID(cluster_id)), str(UUID(infobase_id))
|
|
910
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
911
|
+
try:
|
|
912
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
913
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
914
|
+
rows = ReadService.public_data(
|
|
915
|
+
await getattr(reader, methods[group])(cluster, infobase))
|
|
916
|
+
for row in rows:
|
|
917
|
+
if 'host' in row:
|
|
918
|
+
row['object_host'] = row.pop('host')
|
|
919
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
920
|
+
'cluster': cluster, 'infobase': infobase, 'group': group,
|
|
921
|
+
'rows': rows}
|
|
922
|
+
except Exception as exc:
|
|
923
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
924
|
+
|
|
925
|
+
@server.tool(description='Read a manager, rule, service setting, counter or limit '
|
|
926
|
+
'from the selected startup RAS')
|
|
927
|
+
async def rac_workspace_record_detail(ras: StrictInt, cluster_id: StrictStr,
|
|
928
|
+
kind: StrictStr, selector: StrictStr,
|
|
929
|
+
server_id: StrictStr = '') -> dict:
|
|
930
|
+
methods = {'manager': 'manager', 'rule': 'rule',
|
|
931
|
+
'service-setting': 'service_setting',
|
|
932
|
+
'counter': 'counter', 'limit': 'limit'}
|
|
933
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
934
|
+
raise ValueError('Invalid RAS index')
|
|
935
|
+
if kind not in methods:
|
|
936
|
+
raise ValueError('Invalid record kind')
|
|
937
|
+
cluster = str(UUID(cluster_id))
|
|
938
|
+
if kind in ('rule', 'service-setting'):
|
|
939
|
+
server_id = str(UUID(server_id))
|
|
940
|
+
elif server_id:
|
|
941
|
+
raise ValueError('Unexpected working server')
|
|
942
|
+
if kind in ('counter', 'limit'):
|
|
943
|
+
if (not selector or '\x00' in selector
|
|
944
|
+
or len(selector.encode('utf-8')) > 1024):
|
|
945
|
+
raise ValueError('Invalid record name')
|
|
946
|
+
else:
|
|
947
|
+
selector = str(UUID(selector))
|
|
948
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
949
|
+
try:
|
|
950
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
951
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
952
|
+
arguments = ((cluster, server_id, selector) if kind in ('rule', 'service-setting')
|
|
953
|
+
else (cluster, selector))
|
|
954
|
+
record = ReadService.public_data(
|
|
955
|
+
await getattr(reader, methods[kind])(*arguments))
|
|
956
|
+
if 'host' in record:
|
|
957
|
+
record['object_host'] = record.pop('host')
|
|
958
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
959
|
+
'cluster': cluster, 'server': server_id, 'kind': kind,
|
|
960
|
+
'selector': selector, 'object': record}
|
|
961
|
+
except Exception as exc:
|
|
962
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
963
|
+
|
|
964
|
+
@server.tool(description='Read related processes, connections or locks for a '
|
|
965
|
+
'selected server, process, infobase or connection on a startup RAS')
|
|
966
|
+
async def rac_workspace_related_inventory(ras: StrictInt, cluster_id: StrictStr,
|
|
967
|
+
scope: StrictStr, record_id: StrictStr) -> dict:
|
|
968
|
+
methods = {'server-processes': 'processes_on_server',
|
|
969
|
+
'process-connections': 'connections_in_process',
|
|
970
|
+
'infobase-locks': 'filtered_locks',
|
|
971
|
+
'connection-locks': 'filtered_locks'}
|
|
972
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
973
|
+
raise ValueError('Invalid RAS index')
|
|
974
|
+
if scope not in methods:
|
|
975
|
+
raise ValueError('Invalid related inventory scope')
|
|
976
|
+
cluster, record = str(UUID(cluster_id)), str(UUID(record_id))
|
|
977
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
978
|
+
try:
|
|
979
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
980
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
981
|
+
if scope.endswith('-locks'):
|
|
982
|
+
rows = await reader.filtered_locks(cluster, scope.split('-')[0], record)
|
|
983
|
+
else:
|
|
984
|
+
rows = await getattr(reader, methods[scope])(cluster, record)
|
|
985
|
+
rows = ReadService.public_data(rows)
|
|
986
|
+
for row in rows:
|
|
987
|
+
if 'host' in row:
|
|
988
|
+
row['object_host'] = row.pop('host')
|
|
989
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
990
|
+
'cluster': cluster, 'scope': scope, 'record': record,
|
|
991
|
+
'rows': rows}
|
|
992
|
+
except Exception as exc:
|
|
993
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
994
|
+
|
|
995
|
+
@server.tool(description='Read regular or accumulated values of a named counter '
|
|
996
|
+
'on the selected startup RAS')
|
|
997
|
+
async def rac_workspace_counter_values(ras: StrictInt, cluster_id: StrictStr,
|
|
998
|
+
name: StrictStr, object_filter: StrictStr = '',
|
|
999
|
+
accumulated: StrictBool = False) -> dict:
|
|
1000
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
1001
|
+
raise ValueError('Invalid RAS index')
|
|
1002
|
+
if (not name.strip() or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
1003
|
+
or '\x00' in object_filter
|
|
1004
|
+
or len(object_filter.encode('utf-8')) > 4096):
|
|
1005
|
+
raise ValueError('Invalid counter selector')
|
|
1006
|
+
cluster = str(UUID(cluster_id))
|
|
1007
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
1008
|
+
try:
|
|
1009
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
1010
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
1011
|
+
rows = ReadService.public_data(await reader.counter_values(
|
|
1012
|
+
cluster, name, object_filter, accumulated=accumulated))
|
|
1013
|
+
for row in rows:
|
|
1014
|
+
if 'host' in row:
|
|
1015
|
+
row['object_host'] = row.pop('host')
|
|
1016
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
1017
|
+
'cluster': cluster, 'name': name, 'object_filter': object_filter,
|
|
1018
|
+
'accumulated': accumulated, 'rows': rows}
|
|
1019
|
+
except Exception as exc:
|
|
1020
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
1021
|
+
|
|
1022
|
+
@server.tool(description='Read an infobase summary or a security-profile overview '
|
|
1023
|
+
'with its verified ACL lists from the selected startup RAS')
|
|
1024
|
+
async def rac_workspace_named_overview(ras: StrictInt, cluster_id: StrictStr,
|
|
1025
|
+
kind: StrictStr, selector: StrictStr) -> dict:
|
|
1026
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
1027
|
+
raise ValueError('Invalid RAS index')
|
|
1028
|
+
if kind not in ('infobase-summary', 'profile-overview'):
|
|
1029
|
+
raise ValueError('Invalid overview kind')
|
|
1030
|
+
cluster = str(UUID(cluster_id))
|
|
1031
|
+
if kind == 'infobase-summary':
|
|
1032
|
+
selector = str(UUID(selector))
|
|
1033
|
+
else:
|
|
1034
|
+
ReadService._validated_profile_name(selector)
|
|
1035
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
1036
|
+
try:
|
|
1037
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
1038
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
1039
|
+
if kind == 'infobase-summary':
|
|
1040
|
+
record = await reader.infobase_summary(cluster, selector)
|
|
1041
|
+
else:
|
|
1042
|
+
record = await reader.profile_overview(cluster, selector)
|
|
1043
|
+
record = ReadService.public_data(record)
|
|
1044
|
+
if 'host' in record:
|
|
1045
|
+
record['object_host'] = record.pop('host')
|
|
1046
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
1047
|
+
'cluster': cluster, 'kind': kind, 'selector': selector,
|
|
1048
|
+
'object': record}
|
|
1049
|
+
except Exception as exc:
|
|
1050
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
1051
|
+
|
|
1052
|
+
@server.tool(description='Read an infobase info or summary by name on the '
|
|
1053
|
+
'selected startup RAS, without requiring an infobase UUID')
|
|
1054
|
+
async def rac_workspace_infobase_by_name(ras: StrictInt, cluster_id: StrictStr,
|
|
1055
|
+
kind: StrictStr, name: StrictStr) -> dict:
|
|
1056
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
1057
|
+
raise ValueError('Invalid RAS index')
|
|
1058
|
+
if kind not in ('info', 'summary'):
|
|
1059
|
+
raise ValueError('Invalid infobase view')
|
|
1060
|
+
cluster = str(UUID(cluster_id))
|
|
1061
|
+
ReadService._validated_infobase_name(name)
|
|
1062
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
1063
|
+
if protocol == '10.0':
|
|
1064
|
+
raise ValueError('Infobase info and summary by name require protocol 16.0')
|
|
1065
|
+
try:
|
|
1066
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
1067
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
1068
|
+
method = (reader.infobase_info_by_name if kind == 'info'
|
|
1069
|
+
else reader.infobase_summary_by_name)
|
|
1070
|
+
record = ReadService.public_data(await method(cluster, name))
|
|
1071
|
+
if 'host' in record:
|
|
1072
|
+
record['object_host'] = record.pop('host')
|
|
1073
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
1074
|
+
'cluster': cluster, 'kind': kind, 'name': name, 'object': record}
|
|
1075
|
+
except Exception as exc:
|
|
1076
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
1077
|
+
|
|
1078
|
+
@server.tool(description='Read agent version, agent administrators or cluster '
|
|
1079
|
+
'administrators from the selected startup RAS')
|
|
1080
|
+
async def rac_workspace_agent_view(ras: StrictInt, scope: StrictStr,
|
|
1081
|
+
cluster_id: StrictStr = '') -> dict:
|
|
1082
|
+
methods = {'agent-version': 'agent_version', 'agent-admins': 'agent_admins',
|
|
1083
|
+
'cluster-admins': 'cluster_admins'}
|
|
1084
|
+
if type(ras) is not int or not 0 <= ras < len(workspace_endpoints):
|
|
1085
|
+
raise ValueError('Invalid RAS index')
|
|
1086
|
+
if scope not in methods:
|
|
1087
|
+
raise ValueError('Invalid agent view')
|
|
1088
|
+
if scope == 'cluster-admins':
|
|
1089
|
+
cluster_id = str(UUID(cluster_id))
|
|
1090
|
+
elif cluster_id:
|
|
1091
|
+
raise ValueError('Unexpected cluster')
|
|
1092
|
+
host, port, protocol = workspace_endpoints[ras]
|
|
1093
|
+
try:
|
|
1094
|
+
async with asyncio.timeout(READ_TIMEOUT):
|
|
1095
|
+
reader = ReadService(partial(RacClient, host, port, protocol))
|
|
1096
|
+
args = (cluster_id,) if cluster_id else ()
|
|
1097
|
+
data = ReadService.public_data(await getattr(reader, methods[scope])(*args))
|
|
1098
|
+
return {'ras': ras, 'host': host, 'port': port, 'protocol': protocol,
|
|
1099
|
+
'scope': scope, 'cluster': cluster_id, 'data': data}
|
|
1100
|
+
except Exception as exc:
|
|
1101
|
+
raise ValueError('RAC read failed (' + type(exc).__name__ + ')') from None
|
|
1102
|
+
|
|
1103
|
+
@server.tool(description='List clusters accessible on the configured RAS')
|
|
1104
|
+
async def rac_list_clusters() -> list[dict]:
|
|
1105
|
+
return await safe_read(service.clusters)
|
|
1106
|
+
|
|
1107
|
+
@server.tool(description='Search clusters by name or UUID on the configured RAS; '
|
|
1108
|
+
'returns only cluster UUID and name')
|
|
1109
|
+
async def rac_search_clusters(query: StrictStr) -> list[dict]:
|
|
1110
|
+
return await safe_read(service.search_clusters, query)
|
|
1111
|
+
|
|
1112
|
+
@server.tool(description='Search infobases by name or UUID across clusters on the configured RAS; '
|
|
1113
|
+
'returns only cluster UUID, infobase UUID and name')
|
|
1114
|
+
async def rac_search_infobases(query: StrictStr) -> list[dict]:
|
|
1115
|
+
return await safe_read(service.search_infobases, query)
|
|
1116
|
+
|
|
1117
|
+
@server.tool(description='Search security profiles by name across clusters on the configured RAS; '
|
|
1118
|
+
'returns only cluster UUID and profile name')
|
|
1119
|
+
async def rac_search_profiles(query: StrictStr) -> list[dict]:
|
|
1120
|
+
return await safe_read(service.search_profiles, query)
|
|
1121
|
+
|
|
1122
|
+
@server.tool(description='Search working servers by name or UUID across clusters on the '
|
|
1123
|
+
'configured RAS; returns only cluster UUID, server UUID and name')
|
|
1124
|
+
async def rac_search_working_servers(query: StrictStr) -> list[dict]:
|
|
1125
|
+
return await safe_read(service.search_working_servers, query)
|
|
1126
|
+
|
|
1127
|
+
@server.tool(description='Search working processes by PID, host or UUID across clusters '
|
|
1128
|
+
'on the configured RAS; returns only navigation fields')
|
|
1129
|
+
async def rac_search_working_processes(query: StrictStr) -> list[dict]:
|
|
1130
|
+
return await safe_read(service.search_working_processes, query)
|
|
1131
|
+
|
|
1132
|
+
@server.tool(description='Search sessions by user, host, application, UUID or numeric ID '
|
|
1133
|
+
'on the configured RAS; returns only cluster, session UUID and user')
|
|
1134
|
+
async def rac_search_sessions(query: StrictStr) -> list[dict]:
|
|
1135
|
+
return await safe_read(service.search_sessions, query)
|
|
1136
|
+
|
|
1137
|
+
@server.tool(description='Search connections by host, application, UUID or numeric ID '
|
|
1138
|
+
'on the configured RAS; returns only cluster, connection UUID and host')
|
|
1139
|
+
async def rac_search_connections(query: StrictStr) -> list[dict]:
|
|
1140
|
+
return await safe_read(service.search_connections, query)
|
|
1141
|
+
|
|
1142
|
+
@server.tool(description='Search agent administrators on the configured RAS by name or '
|
|
1143
|
+
'OS user; returns only administrator names')
|
|
1144
|
+
async def rac_search_agent_admins(query: StrictStr) -> list[dict]:
|
|
1145
|
+
return await safe_read(service.search_agent_admins, query)
|
|
1146
|
+
|
|
1147
|
+
@server.tool(description='Search cluster administrators on the configured RAS by name or '
|
|
1148
|
+
'OS user; returns only cluster UUID and administrator name')
|
|
1149
|
+
async def rac_search_cluster_admins(query: StrictStr) -> list[dict]:
|
|
1150
|
+
return await safe_read(service.search_cluster_admins, query)
|
|
1151
|
+
|
|
1152
|
+
@server.tool(description='Search cluster managers by UUID, host, description or PID '
|
|
1153
|
+
'on the configured RAS; returns only cluster UUID, manager UUID and host')
|
|
1154
|
+
async def rac_search_managers(query: StrictStr) -> list[dict]:
|
|
1155
|
+
return await safe_read(service.search_managers, query)
|
|
1156
|
+
|
|
1157
|
+
@server.tool(description='Search cluster services by name, description or manager UUID '
|
|
1158
|
+
'on the configured RAS; returns only cluster UUID and service name')
|
|
1159
|
+
async def rac_search_services(query: StrictStr) -> list[dict]:
|
|
1160
|
+
return await safe_read(service.search_services, query)
|
|
1161
|
+
|
|
1162
|
+
@server.tool(description='Search counters by name or description across clusters on '
|
|
1163
|
+
'the configured RAS; returns only navigation fields')
|
|
1164
|
+
async def rac_search_counters(query: StrictStr) -> list[dict]:
|
|
1165
|
+
return await safe_read(service.search_counters, query)
|
|
1166
|
+
|
|
1167
|
+
@server.tool(description='Search limits by name or description across clusters on '
|
|
1168
|
+
'the configured RAS; returns only navigation fields')
|
|
1169
|
+
async def rac_search_limits(query: StrictStr) -> list[dict]:
|
|
1170
|
+
return await safe_read(service.search_limits, query)
|
|
1171
|
+
|
|
1172
|
+
@server.tool(description='Search locks by description, connection UUID or object UUID '
|
|
1173
|
+
'across clusters on the configured RAS; returns only navigation fields')
|
|
1174
|
+
async def rac_search_locks(query: StrictStr) -> list[dict]:
|
|
1175
|
+
return await safe_read(service.search_locks, query)
|
|
1176
|
+
|
|
1177
|
+
@server.tool(description='Search assignment rules across working servers on the '
|
|
1178
|
+
'configured RAS; returns exact cluster, server and rule UUIDs')
|
|
1179
|
+
async def rac_search_rules(query: StrictStr) -> list[dict]:
|
|
1180
|
+
return await safe_read(service.search_rules, query)
|
|
1181
|
+
|
|
1182
|
+
@server.tool(description='Search service settings across working servers on the '
|
|
1183
|
+
'configured RAS; returns exact cluster, server and setting UUIDs')
|
|
1184
|
+
async def rac_search_service_settings(query: StrictStr) -> list[dict]:
|
|
1185
|
+
return await safe_read(service.search_service_settings, query)
|
|
1186
|
+
|
|
1187
|
+
@server.tool(description='Search transfer directories by service or infobase name '
|
|
1188
|
+
'on the configured RAS; excludes filesystem paths')
|
|
1189
|
+
async def rac_search_transfer_directories(query: StrictStr) -> list[dict]:
|
|
1190
|
+
return await safe_read(service.search_transfer_directories, query)
|
|
1191
|
+
|
|
1192
|
+
@server.tool(description='Get the RAS agent version (protocol 16.0)')
|
|
1193
|
+
async def rac_agent_version() -> str:
|
|
1194
|
+
return await safe_read(service.agent_version)
|
|
1195
|
+
|
|
1196
|
+
@server.tool(description='List RAS agent administrators')
|
|
1197
|
+
async def rac_list_agent_admins() -> list[dict]:
|
|
1198
|
+
return await safe_read(service.agent_admins)
|
|
1199
|
+
|
|
1200
|
+
@server.tool(description='List cluster administrators by cluster UUID')
|
|
1201
|
+
async def rac_list_cluster_admins(cluster_id: StrictStr) -> list[dict]:
|
|
1202
|
+
return await safe_read(service.cluster_admins, cluster_id)
|
|
1203
|
+
|
|
1204
|
+
@server.tool(description='Get a cluster record by UUID')
|
|
1205
|
+
async def rac_get_cluster(cluster_id: StrictStr) -> dict:
|
|
1206
|
+
return await safe_read(service.one, cluster_id, 'get_cluster')
|
|
1207
|
+
|
|
1208
|
+
@server.tool(description='Read cluster info, working servers, processes and infobase summaries')
|
|
1209
|
+
async def rac_cluster_overview(cluster_id: StrictStr) -> dict:
|
|
1210
|
+
return await safe_read(service.cluster, cluster_id)
|
|
1211
|
+
|
|
1212
|
+
@server.tool(description='List working servers of a cluster')
|
|
1213
|
+
async def rac_list_working_servers(cluster_id: StrictStr) -> list[dict]:
|
|
1214
|
+
return await safe_read(service.one, cluster_id, 'list_servers')
|
|
1215
|
+
|
|
1216
|
+
@server.tool(description='List working processes of a cluster')
|
|
1217
|
+
async def rac_list_working_processes(cluster_id: StrictStr) -> list[dict]:
|
|
1218
|
+
return await safe_read(service.one, cluster_id, 'list_processes')
|
|
1219
|
+
|
|
1220
|
+
@server.tool(description='List working processes filtered by working server UUID')
|
|
1221
|
+
async def rac_list_processes_on_server(cluster_id: StrictStr, server_id: StrictStr) -> list[dict]:
|
|
1222
|
+
return await safe_read(service.processes_on_server, cluster_id, server_id)
|
|
1223
|
+
|
|
1224
|
+
@server.tool(description='List infobase summaries of a cluster')
|
|
1225
|
+
async def rac_list_infobase_summaries(cluster_id: StrictStr) -> list[dict]:
|
|
1226
|
+
return await safe_read(service.one, cluster_id, 'list_infobases')
|
|
1227
|
+
|
|
1228
|
+
@server.tool(description='List sessions filtered by infobase UUID (populated 16.0 decoder unverified)')
|
|
1229
|
+
async def rac_list_sessions_in_infobase(cluster_id: StrictStr, infobase_id: StrictStr) -> list[dict]:
|
|
1230
|
+
return await safe_read(service.sessions_in_infobase, cluster_id, infobase_id)
|
|
1231
|
+
|
|
1232
|
+
@server.tool(description='List connections filtered by infobase UUID')
|
|
1233
|
+
async def rac_list_connections_in_infobase(cluster_id: StrictStr, infobase_id: StrictStr) -> list[dict]:
|
|
1234
|
+
return await safe_read(service.connections_in_infobase, cluster_id, infobase_id)
|
|
1235
|
+
|
|
1236
|
+
@server.tool(description='List connections filtered locally by process UUID after decoding the cluster list')
|
|
1237
|
+
async def rac_list_connections_in_process(cluster_id: StrictStr, process_id: StrictStr) -> list[dict]:
|
|
1238
|
+
return await safe_read(service.connections_in_process, cluster_id, process_id)
|
|
1239
|
+
|
|
1240
|
+
@server.tool(description='List locks filtered by infobase or connection UUID (not session)')
|
|
1241
|
+
async def rac_list_filtered_locks(cluster_id: StrictStr, scope: StrictStr, record_id: StrictStr) -> list[dict]:
|
|
1242
|
+
return await safe_read(service.filtered_locks, cluster_id, scope, record_id)
|
|
1243
|
+
|
|
1244
|
+
@server.tool(description='List a verified read-only cluster group: ' + ', '.join(CLUSTER_LISTS))
|
|
1245
|
+
async def rac_list_cluster_group(cluster_id: StrictStr, group: StrictStr) -> list[dict]:
|
|
1246
|
+
return await safe_read(service.list_group, cluster_id, group)
|
|
1247
|
+
|
|
1248
|
+
@server.tool(description='Get a working server record by cluster and server UUID')
|
|
1249
|
+
async def rac_get_server(cluster_id: StrictStr, server_id: StrictStr) -> dict:
|
|
1250
|
+
return await safe_read(service.server, cluster_id, server_id)
|
|
1251
|
+
|
|
1252
|
+
@server.tool(description='Get a working process record by cluster and process UUID')
|
|
1253
|
+
async def rac_get_process(cluster_id: StrictStr, process_id: StrictStr) -> dict:
|
|
1254
|
+
return await safe_read(service.process, cluster_id, process_id)
|
|
1255
|
+
|
|
1256
|
+
@server.tool(description='Get a session record by cluster and session UUID')
|
|
1257
|
+
async def rac_get_session(cluster_id: StrictStr, session_id: StrictStr) -> dict:
|
|
1258
|
+
return await safe_read(service.session, cluster_id, session_id)
|
|
1259
|
+
|
|
1260
|
+
@server.tool(description='Get an infobase summary by cluster and infobase UUID')
|
|
1261
|
+
async def rac_get_infobase_summary(cluster_id: StrictStr, infobase_id: StrictStr) -> dict:
|
|
1262
|
+
return await safe_read(service.infobase_summary, cluster_id, infobase_id)
|
|
1263
|
+
|
|
1264
|
+
@server.tool(description='Get an infobase summary by its unique name within a cluster')
|
|
1265
|
+
async def rac_get_infobase_summary_by_name(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1266
|
+
return await safe_read(service.infobase_summary_by_name, cluster_id, name)
|
|
1267
|
+
|
|
1268
|
+
@server.tool(description='Get infobase full info by cluster and infobase UUID (protocol 16.0)')
|
|
1269
|
+
async def rac_get_infobase_info(cluster_id: StrictStr, infobase_id: StrictStr) -> dict:
|
|
1270
|
+
return await safe_read(service.infobase_info, cluster_id, infobase_id)
|
|
1271
|
+
|
|
1272
|
+
@server.tool(description='Get filtered infobase full info by its unique name (protocol 16.0)')
|
|
1273
|
+
async def rac_get_infobase_info_by_name(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1274
|
+
return await safe_read(service.infobase_info_by_name, cluster_id, name)
|
|
1275
|
+
|
|
1276
|
+
@server.tool(description='List binary data storages of an infobase (populated results unverified)')
|
|
1277
|
+
async def rac_list_binary_storages(cluster_id: StrictStr, infobase_id: StrictStr) -> list[dict]:
|
|
1278
|
+
return await safe_read(service.binary_storages, cluster_id, infobase_id)
|
|
1279
|
+
|
|
1280
|
+
@server.tool(description='Get a connection record by cluster and connection UUID')
|
|
1281
|
+
async def rac_get_connection(cluster_id: StrictStr, connection_id: StrictStr) -> dict:
|
|
1282
|
+
return await safe_read(service.connection, cluster_id, connection_id)
|
|
1283
|
+
|
|
1284
|
+
@server.tool(description='Get a service setting by cluster, server and setting UUID')
|
|
1285
|
+
async def rac_get_service_setting(cluster_id: StrictStr, server_id: StrictStr, setting_id: StrictStr) -> dict:
|
|
1286
|
+
return await safe_read(service.service_setting, cluster_id, server_id, setting_id)
|
|
1287
|
+
|
|
1288
|
+
@server.tool(description='Get an assignment rule by cluster, server and rule UUID')
|
|
1289
|
+
async def rac_get_rule(cluster_id: StrictStr, server_id: StrictStr, rule_id: StrictStr) -> dict:
|
|
1290
|
+
return await safe_read(service.rule, cluster_id, server_id, rule_id)
|
|
1291
|
+
|
|
1292
|
+
@server.tool(description='Get a cluster manager by cluster and manager UUID')
|
|
1293
|
+
async def rac_get_manager(cluster_id: StrictStr, manager_id: StrictStr) -> dict:
|
|
1294
|
+
return await safe_read(service.manager, cluster_id, manager_id)
|
|
1295
|
+
|
|
1296
|
+
@server.tool(description='Get a cluster limit by cluster UUID and limit name')
|
|
1297
|
+
async def rac_get_limit(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1298
|
+
return await safe_read(service.limit, cluster_id, name)
|
|
1299
|
+
|
|
1300
|
+
@server.tool(description='Get a cluster counter by cluster UUID and counter name')
|
|
1301
|
+
async def rac_get_counter(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1302
|
+
return await safe_read(service.counter, cluster_id, name)
|
|
1303
|
+
|
|
1304
|
+
@server.tool(description='List counter readings (populated 16.0 decoder not live-verified)')
|
|
1305
|
+
async def rac_list_counter_values(cluster_id: StrictStr, name: StrictStr, object_filter: StrictStr = '') -> list[dict]:
|
|
1306
|
+
return await safe_read(service.counter_values, cluster_id, name, object_filter)
|
|
1307
|
+
|
|
1308
|
+
@server.tool(description='List accumulated counter readings (populated 16.0 decoder not live-verified)')
|
|
1309
|
+
async def rac_list_accumulated_counter_values(cluster_id: StrictStr, name: StrictStr,
|
|
1310
|
+
object_filter: StrictStr = '') -> list[dict]:
|
|
1311
|
+
return await safe_read(partial(service.counter_values, accumulated=True),
|
|
1312
|
+
cluster_id, name, object_filter)
|
|
1313
|
+
|
|
1314
|
+
@server.tool(description='List a verified read-only server group: ' + ', '.join(SERVER_LISTS))
|
|
1315
|
+
async def rac_list_server_group(cluster_id: StrictStr, server_id: StrictStr, group: StrictStr) -> list[dict]:
|
|
1316
|
+
return await safe_read(service.list_server_group, cluster_id, server_id, group)
|
|
1317
|
+
|
|
1318
|
+
@server.tool(description='List service transfer directories filtered by service name on a working server')
|
|
1319
|
+
async def rac_list_transfer_directories(cluster_id: StrictStr, server_id: StrictStr,
|
|
1320
|
+
service_name: StrictStr = '') -> list[dict]:
|
|
1321
|
+
return await safe_read(service.transfer_directories, cluster_id, server_id, service_name)
|
|
1322
|
+
|
|
1323
|
+
@server.tool(description='Get a security profile by its unique name within a cluster')
|
|
1324
|
+
async def rac_get_profile_by_name(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1325
|
+
return await safe_read(service.profile_by_name, cluster_id, name)
|
|
1326
|
+
|
|
1327
|
+
@server.tool(description='Read a security profile and all six verified ACL lists by exact name')
|
|
1328
|
+
async def rac_profile_overview(cluster_id: StrictStr, name: StrictStr) -> dict:
|
|
1329
|
+
return await safe_read(service.profile_overview, cluster_id, name)
|
|
1330
|
+
|
|
1331
|
+
@server.tool(description='List profile ACL entries: ' + ', '.join(PROFILE_ACL_LISTS) +
|
|
1332
|
+
'; optionally filter by exact alias (directory) or name (other ACLs)')
|
|
1333
|
+
async def rac_list_profile_acl(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1334
|
+
acl_type: StrictStr, name: StrictStr = '') -> list[dict]:
|
|
1335
|
+
return await safe_read(service.list_profile_acl, cluster_id, profile_name, acl_type, name)
|
|
1336
|
+
|
|
1337
|
+
@server.tool(description='Search one of six verified profile ACL categories by alias or name '
|
|
1338
|
+
'on the configured RAS; returns only navigation fields')
|
|
1339
|
+
async def rac_search_profile_acl(cluster_id: StrictStr, acl_type: StrictStr,
|
|
1340
|
+
query: StrictStr) -> list[dict]:
|
|
1341
|
+
return await safe_read(service.search_profile_acl, cluster_id, acl_type, query)
|
|
1342
|
+
|
|
1343
|
+
if allow_agent_admin_register:
|
|
1344
|
+
@server.tool(description='MUTATION: register a password-free OS-capable agent administrator. '
|
|
1345
|
+
'Confirm the exact name:os_user; guarded client verifies the complete admin list.')
|
|
1346
|
+
async def rac_register_agent_admin(name: StrictStr, os_user: StrictStr, descr: StrictStr,
|
|
1347
|
+
confirm_name_os_user: StrictStr) -> dict:
|
|
1348
|
+
if (type(name) is not str or not name or '\x00' in name
|
|
1349
|
+
or len(name.encode('utf-8')) > 1024
|
|
1350
|
+
or type(os_user) is not str or not os_user or '\x00' in os_user
|
|
1351
|
+
or len(os_user.encode('utf-8')) > 1024
|
|
1352
|
+
or type(descr) is not str or '\x00' in descr
|
|
1353
|
+
or len(descr.encode('utf-8')) > 1024):
|
|
1354
|
+
raise ValueError('Invalid agent administrator parameters')
|
|
1355
|
+
if confirm_name_os_user != f'{name}:{os_user}':
|
|
1356
|
+
raise ValueError('Confirmation must match name:os_user exactly')
|
|
1357
|
+
try:
|
|
1358
|
+
async with service.factory() as client:
|
|
1359
|
+
return await client.register_agent_admin(name, os_user, descr=descr)
|
|
1360
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1361
|
+
raise ValueError('RAC agent admin register failed (' + type(exc).__name__ + ')') from None
|
|
1362
|
+
|
|
1363
|
+
if allow_agent_admin_remove:
|
|
1364
|
+
@server.tool(description='DESTRUCTIVE: remove an agent administrator; requires an authenticated '
|
|
1365
|
+
'agent-user and exact name confirmation. Guarded client verifies full list.')
|
|
1366
|
+
async def rac_remove_agent_admin(name: StrictStr, confirm_name: StrictStr) -> list[dict]:
|
|
1367
|
+
if (type(name) is not str or not name or '\x00' in name
|
|
1368
|
+
or len(name.encode('utf-8')) > 1024):
|
|
1369
|
+
raise ValueError('Invalid agent administrator name')
|
|
1370
|
+
if confirm_name != name:
|
|
1371
|
+
raise ValueError('Confirmation must match agent administrator name exactly')
|
|
1372
|
+
try:
|
|
1373
|
+
async with service.factory() as client:
|
|
1374
|
+
return await client.remove_agent_admin(name)
|
|
1375
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1376
|
+
raise ValueError('RAC agent admin remove failed (' + type(exc).__name__ + ')') from None
|
|
1377
|
+
|
|
1378
|
+
if allow_cluster_admin_register:
|
|
1379
|
+
@server.tool(description='MUTATION: register a password-free OS-capable cluster administrator. '
|
|
1380
|
+
'Confirm exact cluster UUID:name:os_user; guarded client checks full admin list.')
|
|
1381
|
+
async def rac_register_cluster_admin(cluster_id: StrictStr, name: StrictStr,
|
|
1382
|
+
os_user: StrictStr, descr: StrictStr,
|
|
1383
|
+
confirm_cluster_name_os_user: StrictStr) -> dict:
|
|
1384
|
+
try:
|
|
1385
|
+
cluster = UUID(cluster_id)
|
|
1386
|
+
except (TypeError, ValueError, AttributeError):
|
|
1387
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1388
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name
|
|
1389
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
1390
|
+
or type(os_user) is not str or not os_user or '\x00' in os_user
|
|
1391
|
+
or len(os_user.encode('utf-8')) > 1024
|
|
1392
|
+
or type(descr) is not str or '\x00' in descr
|
|
1393
|
+
or len(descr.encode('utf-8')) > 1024):
|
|
1394
|
+
raise ValueError('Invalid cluster administrator parameters')
|
|
1395
|
+
if confirm_cluster_name_os_user != f'{cluster}:{name}:{os_user}':
|
|
1396
|
+
raise ValueError('Confirmation must match cluster UUID:name:os_user exactly')
|
|
1397
|
+
try:
|
|
1398
|
+
async with service.factory() as client:
|
|
1399
|
+
return await client.register_cluster_admin(cluster, name, os_user, descr=descr)
|
|
1400
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1401
|
+
raise ValueError('RAC cluster admin register failed (' + type(exc).__name__ + ')') from None
|
|
1402
|
+
|
|
1403
|
+
if allow_cluster_admin_remove:
|
|
1404
|
+
@server.tool(description='DESTRUCTIVE: remove a cluster administrator. Requires an authenticated '
|
|
1405
|
+
'cluster-user and exact cluster UUID:name confirmation; guarded client checks '
|
|
1406
|
+
'the complete administrator list after removal.')
|
|
1407
|
+
async def rac_remove_cluster_admin(cluster_id: StrictStr, name: StrictStr,
|
|
1408
|
+
confirm_cluster_name: StrictStr) -> list[dict]:
|
|
1409
|
+
try:
|
|
1410
|
+
cluster = UUID(cluster_id)
|
|
1411
|
+
except (TypeError, ValueError, AttributeError):
|
|
1412
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1413
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name
|
|
1414
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024):
|
|
1415
|
+
raise ValueError('Invalid cluster administrator parameters')
|
|
1416
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
1417
|
+
raise ValueError('Confirmation must match cluster UUID:name exactly')
|
|
1418
|
+
try:
|
|
1419
|
+
async with service.factory() as client:
|
|
1420
|
+
return await client.remove_cluster_admin(cluster, name)
|
|
1421
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1422
|
+
raise ValueError('RAC cluster admin remove failed (' + type(exc).__name__ + ')') from None
|
|
1423
|
+
|
|
1424
|
+
if allow_infobase_database_create:
|
|
1425
|
+
@server.tool(description='DESTRUCTIVE: create and register a new PostgreSQL database. '
|
|
1426
|
+
'Confirm cluster UUID:name:database name exactly; guarded client '
|
|
1427
|
+
'requires full list and info read-back. No password input.')
|
|
1428
|
+
async def rac_create_infobase_database(
|
|
1429
|
+
cluster_id: StrictStr, name: StrictStr, db_name: StrictStr, db_server: StrictStr,
|
|
1430
|
+
db_user: StrictStr, locale: StrictStr, confirm_cluster_name_db: StrictStr) -> dict:
|
|
1431
|
+
try:
|
|
1432
|
+
cluster = UUID(cluster_id)
|
|
1433
|
+
except (TypeError, ValueError, AttributeError):
|
|
1434
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1435
|
+
if str(cluster) != cluster_id:
|
|
1436
|
+
raise ValueError('Invalid cluster UUID')
|
|
1437
|
+
for field in (name, db_name, db_server, db_user, locale):
|
|
1438
|
+
if (type(field) is not str or not field or '\x00' in field
|
|
1439
|
+
or len(field.encode('utf-8')) > 255):
|
|
1440
|
+
raise ValueError('Invalid infobase creation parameters')
|
|
1441
|
+
if confirm_cluster_name_db != f'{cluster}:{name}:{db_name}':
|
|
1442
|
+
raise ValueError('Confirmation must match cluster UUID:name:database name exactly')
|
|
1443
|
+
try:
|
|
1444
|
+
async with service.factory() as client:
|
|
1445
|
+
return ReadService._public_infobase_info(await client.create_infobase(
|
|
1446
|
+
cluster, name=name, db_name=db_name, db_server=db_server,
|
|
1447
|
+
db_user=db_user, locale=locale, create_database=True))
|
|
1448
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1449
|
+
raise ValueError('RAC infobase database creation failed ('
|
|
1450
|
+
+ type(exc).__name__ + ')') from None
|
|
1451
|
+
|
|
1452
|
+
if allow_infobase_existing_registration:
|
|
1453
|
+
@server.tool(description='MUTATION: register an already existing PostgreSQL infobase; '
|
|
1454
|
+
'never create the database. Confirm cluster UUID:name:database name '
|
|
1455
|
+
'exactly; guarded client verifies full list and info read-back.')
|
|
1456
|
+
async def rac_register_existing_infobase(
|
|
1457
|
+
cluster_id: StrictStr, name: StrictStr, db_name: StrictStr, db_server: StrictStr,
|
|
1458
|
+
db_user: StrictStr, locale: StrictStr, confirm_cluster_name_db: StrictStr) -> dict:
|
|
1459
|
+
try:
|
|
1460
|
+
cluster = UUID(cluster_id)
|
|
1461
|
+
except (TypeError, ValueError, AttributeError):
|
|
1462
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1463
|
+
if str(cluster) != cluster_id:
|
|
1464
|
+
raise ValueError('Invalid cluster UUID')
|
|
1465
|
+
for field in (name, db_name, db_server, db_user, locale):
|
|
1466
|
+
if (type(field) is not str or not field or '\x00' in field
|
|
1467
|
+
or len(field.encode('utf-8')) > 255):
|
|
1468
|
+
raise ValueError('Invalid infobase registration parameters')
|
|
1469
|
+
if confirm_cluster_name_db != f'{cluster}:{name}:{db_name}':
|
|
1470
|
+
raise ValueError('Confirmation must match cluster UUID:name:database name exactly')
|
|
1471
|
+
try:
|
|
1472
|
+
async with service.factory() as client:
|
|
1473
|
+
return ReadService._public_infobase_info(await client.create_infobase(
|
|
1474
|
+
cluster, name=name, db_name=db_name, db_server=db_server,
|
|
1475
|
+
db_user=db_user, locale=locale, create_database=False))
|
|
1476
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1477
|
+
raise ValueError('RAC existing infobase registration failed ('
|
|
1478
|
+
+ type(exc).__name__ + ')') from None
|
|
1479
|
+
|
|
1480
|
+
if allow_infobase_registration_drop:
|
|
1481
|
+
@server.tool(description='DESTRUCTIVE: remove an infobase registration only; never drop or '
|
|
1482
|
+
'clear the underlying database. Confirm cluster UUID:infobase UUID '
|
|
1483
|
+
'exactly; guarded client verifies the complete registration list.')
|
|
1484
|
+
async def rac_drop_infobase_registration(
|
|
1485
|
+
cluster_id: StrictStr, infobase_id: StrictStr,
|
|
1486
|
+
confirm_cluster_infobase: StrictStr) -> list[dict]:
|
|
1487
|
+
try:
|
|
1488
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1489
|
+
except (TypeError, ValueError, AttributeError):
|
|
1490
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1491
|
+
if str(cluster) != cluster_id or str(infobase) != infobase_id:
|
|
1492
|
+
raise ValueError('Invalid cluster or infobase UUID')
|
|
1493
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1494
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1495
|
+
try:
|
|
1496
|
+
async with service.factory() as client:
|
|
1497
|
+
return await client.drop_infobase(cluster, infobase, drop_database=False,
|
|
1498
|
+
clear_database=False)
|
|
1499
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1500
|
+
raise ValueError('RAC infobase registration drop failed ('
|
|
1501
|
+
+ type(exc).__name__ + ')') from None
|
|
1502
|
+
|
|
1503
|
+
if allow_infobase_summary_description_update:
|
|
1504
|
+
@server.tool(description='MUTATION: set infobase summary description (empty string '
|
|
1505
|
+
'clears). Confirm cluster UUID:infobase UUID exactly; guarded '
|
|
1506
|
+
'client checks the complete infobase list.')
|
|
1507
|
+
async def rac_update_infobase_summary_description(
|
|
1508
|
+
cluster_id: StrictStr, infobase_id: StrictStr, description: StrictStr,
|
|
1509
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1510
|
+
try:
|
|
1511
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1512
|
+
except (TypeError, ValueError, AttributeError):
|
|
1513
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1514
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1515
|
+
or type(description) is not str or '\x00' in description
|
|
1516
|
+
or len(description.encode('utf-8')) > 1024):
|
|
1517
|
+
raise ValueError('Invalid infobase summary description')
|
|
1518
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1519
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1520
|
+
try:
|
|
1521
|
+
async with service.factory() as client:
|
|
1522
|
+
return ReadService._public_infobase_info(
|
|
1523
|
+
await client.update_infobase_summary_description(cluster, infobase, description))
|
|
1524
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1525
|
+
raise ValueError('RAC infobase summary description update failed ('
|
|
1526
|
+
+ type(exc).__name__ + ')') from None
|
|
1527
|
+
|
|
1528
|
+
if allow_infobase_description_update:
|
|
1529
|
+
@server.tool(description='MUTATION: set infobase description (empty string clears). '
|
|
1530
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1531
|
+
'checks complete info and infobase list.')
|
|
1532
|
+
async def rac_update_infobase_description(
|
|
1533
|
+
cluster_id: StrictStr, infobase_id: StrictStr, description: StrictStr,
|
|
1534
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1535
|
+
try:
|
|
1536
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1537
|
+
except (TypeError, ValueError, AttributeError):
|
|
1538
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1539
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1540
|
+
or type(description) is not str or '\x00' in description
|
|
1541
|
+
or len(description.encode('utf-8')) > 1024):
|
|
1542
|
+
raise ValueError('Invalid infobase description')
|
|
1543
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1544
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1545
|
+
try:
|
|
1546
|
+
async with service.factory() as client:
|
|
1547
|
+
return ReadService._public_infobase_info(
|
|
1548
|
+
await client.update_infobase_description(cluster, infobase, description))
|
|
1549
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1550
|
+
raise ValueError('RAC infobase description update failed ('
|
|
1551
|
+
+ type(exc).__name__ + ')') from None
|
|
1552
|
+
|
|
1553
|
+
if allow_infobase_scheduled_jobs_deny_update:
|
|
1554
|
+
@server.tool(description='MUTATION: deny or allow scheduled jobs for an infobase. '
|
|
1555
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1556
|
+
'checks complete info and registration list.')
|
|
1557
|
+
async def rac_update_infobase_scheduled_jobs_deny(
|
|
1558
|
+
cluster_id: StrictStr, infobase_id: StrictStr, denied: StrictBool,
|
|
1559
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1560
|
+
try:
|
|
1561
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1562
|
+
except (TypeError, ValueError, AttributeError):
|
|
1563
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1564
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1565
|
+
or type(denied) is not bool):
|
|
1566
|
+
raise ValueError('Invalid infobase scheduled jobs deny flag')
|
|
1567
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1568
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1569
|
+
try:
|
|
1570
|
+
async with service.factory() as client:
|
|
1571
|
+
return ReadService._public_infobase_info(
|
|
1572
|
+
await client.update_infobase_scheduled_jobs_deny(cluster, infobase, denied))
|
|
1573
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1574
|
+
raise ValueError('RAC infobase scheduled jobs deny update failed ('
|
|
1575
|
+
+ type(exc).__name__ + ')') from None
|
|
1576
|
+
|
|
1577
|
+
if allow_infobase_sessions_deny_update:
|
|
1578
|
+
@server.tool(description='MUTATION: deny or allow sessions for an infobase. '
|
|
1579
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1580
|
+
'checks complete info and registration list.')
|
|
1581
|
+
async def rac_update_infobase_sessions_deny(
|
|
1582
|
+
cluster_id: StrictStr, infobase_id: StrictStr, denied: StrictBool,
|
|
1583
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1584
|
+
try:
|
|
1585
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1586
|
+
except (TypeError, ValueError, AttributeError):
|
|
1587
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1588
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1589
|
+
or type(denied) is not bool):
|
|
1590
|
+
raise ValueError('Invalid infobase sessions deny flag')
|
|
1591
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1592
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1593
|
+
try:
|
|
1594
|
+
async with service.factory() as client:
|
|
1595
|
+
return ReadService._public_infobase_info(
|
|
1596
|
+
await client.update_infobase_sessions_deny(cluster, infobase, denied))
|
|
1597
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1598
|
+
raise ValueError('RAC infobase sessions deny update failed ('
|
|
1599
|
+
+ type(exc).__name__ + ')') from None
|
|
1600
|
+
|
|
1601
|
+
if allow_infobase_license_distribution_update:
|
|
1602
|
+
@server.tool(description='MUTATION: allow or deny infobase license distribution. '
|
|
1603
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1604
|
+
'checks complete info and registration list.')
|
|
1605
|
+
async def rac_update_infobase_license_distribution(
|
|
1606
|
+
cluster_id: StrictStr, infobase_id: StrictStr, distribution: StrictStr,
|
|
1607
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1608
|
+
try:
|
|
1609
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1610
|
+
except (TypeError, ValueError, AttributeError):
|
|
1611
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1612
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1613
|
+
or type(distribution) is not str or distribution not in ('allow', 'deny')):
|
|
1614
|
+
raise ValueError('Invalid infobase license distribution')
|
|
1615
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1616
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1617
|
+
try:
|
|
1618
|
+
async with service.factory() as client:
|
|
1619
|
+
return ReadService._public_infobase_info(
|
|
1620
|
+
await client.update_infobase_license_distribution(cluster, infobase, distribution))
|
|
1621
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1622
|
+
raise ValueError('RAC infobase license distribution update failed ('
|
|
1623
|
+
+ type(exc).__name__ + ')') from None
|
|
1624
|
+
|
|
1625
|
+
if allow_infobase_denied_message_update:
|
|
1626
|
+
@server.tool(description='MUTATION: set infobase denied message (empty string clears). '
|
|
1627
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1628
|
+
'checks complete info and registration list.')
|
|
1629
|
+
async def rac_update_infobase_denied_message(
|
|
1630
|
+
cluster_id: StrictStr, infobase_id: StrictStr, message: StrictStr,
|
|
1631
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1632
|
+
try:
|
|
1633
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1634
|
+
except (TypeError, ValueError, AttributeError):
|
|
1635
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1636
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1637
|
+
or type(message) is not str or '\x00' in message):
|
|
1638
|
+
raise ValueError('Invalid infobase denied message')
|
|
1639
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1640
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1641
|
+
try:
|
|
1642
|
+
async with service.factory() as client:
|
|
1643
|
+
return ReadService._public_infobase_info(
|
|
1644
|
+
await client.update_infobase_denied_message(cluster, infobase, message))
|
|
1645
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1646
|
+
raise ValueError('RAC infobase denied message update failed ('
|
|
1647
|
+
+ type(exc).__name__ + ')') from None
|
|
1648
|
+
|
|
1649
|
+
if allow_infobase_reserve_working_processes_update:
|
|
1650
|
+
@server.tool(description='MUTATION: toggle infobase reserve working processes. '
|
|
1651
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1652
|
+
'checks complete info and registration list.')
|
|
1653
|
+
async def rac_update_infobase_reserve_working_processes(
|
|
1654
|
+
cluster_id: StrictStr, infobase_id: StrictStr, reserve: StrictBool,
|
|
1655
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1656
|
+
try:
|
|
1657
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1658
|
+
except (TypeError, ValueError, AttributeError):
|
|
1659
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1660
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1661
|
+
or type(reserve) is not bool):
|
|
1662
|
+
raise ValueError('Invalid infobase reserve working processes flag')
|
|
1663
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1664
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1665
|
+
try:
|
|
1666
|
+
async with service.factory() as client:
|
|
1667
|
+
return ReadService._public_infobase_info(
|
|
1668
|
+
await client.update_infobase_reserve_working_processes(cluster, infobase, reserve))
|
|
1669
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1670
|
+
raise ValueError('RAC infobase reserve working processes update failed ('
|
|
1671
|
+
+ type(exc).__name__ + ')') from None
|
|
1672
|
+
|
|
1673
|
+
if allow_infobase_external_session_manager_update:
|
|
1674
|
+
@server.tool(description='MUTATION: require or release an external session manager '
|
|
1675
|
+
'for an infobase. Confirm cluster UUID:infobase UUID exactly; '
|
|
1676
|
+
'guarded client checks complete info and registration list.')
|
|
1677
|
+
async def rac_update_infobase_external_session_manager_required(
|
|
1678
|
+
cluster_id: StrictStr, infobase_id: StrictStr, required: StrictBool,
|
|
1679
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1680
|
+
try:
|
|
1681
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1682
|
+
except (TypeError, ValueError, AttributeError):
|
|
1683
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1684
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1685
|
+
or type(required) is not bool):
|
|
1686
|
+
raise ValueError('Invalid infobase external session manager flag')
|
|
1687
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1688
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1689
|
+
try:
|
|
1690
|
+
async with service.factory() as client:
|
|
1691
|
+
return ReadService._public_infobase_info(
|
|
1692
|
+
await client.update_infobase_external_session_manager_required(cluster, infobase, required))
|
|
1693
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1694
|
+
raise ValueError('RAC infobase external session manager update failed ('
|
|
1695
|
+
+ type(exc).__name__ + ')') from None
|
|
1696
|
+
|
|
1697
|
+
if allow_infobase_local_speech_update:
|
|
1698
|
+
@server.tool(description='MUTATION: disable or enable local speech-to-text for an '
|
|
1699
|
+
'infobase. Confirm cluster UUID:infobase UUID exactly; guarded '
|
|
1700
|
+
'client checks complete info and registration list.')
|
|
1701
|
+
async def rac_update_infobase_disable_local_speech_to_text(
|
|
1702
|
+
cluster_id: StrictStr, infobase_id: StrictStr, disabled: StrictBool,
|
|
1703
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1704
|
+
try:
|
|
1705
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1706
|
+
except (TypeError, ValueError, AttributeError):
|
|
1707
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1708
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1709
|
+
or type(disabled) is not bool):
|
|
1710
|
+
raise ValueError('Invalid infobase local speech-to-text flag')
|
|
1711
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1712
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1713
|
+
try:
|
|
1714
|
+
async with service.factory() as client:
|
|
1715
|
+
return ReadService._public_infobase_info(
|
|
1716
|
+
await client.update_infobase_disable_local_speech_to_text(cluster, infobase, disabled))
|
|
1717
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1718
|
+
raise ValueError('RAC infobase local speech-to-text update failed ('
|
|
1719
|
+
+ type(exc).__name__ + ')') from None
|
|
1720
|
+
|
|
1721
|
+
if allow_infobase_configuration_unload_delay_update:
|
|
1722
|
+
@server.tool(description='MUTATION: set configuration unload delay by a working '
|
|
1723
|
+
'process without active users. Confirm cluster UUID:infobase UUID '
|
|
1724
|
+
'exactly; guarded client checks complete info and registration list.')
|
|
1725
|
+
async def rac_update_infobase_configuration_unload_delay(
|
|
1726
|
+
cluster_id: StrictStr, infobase_id: StrictStr, delay: StrictInt,
|
|
1727
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1728
|
+
try:
|
|
1729
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1730
|
+
except (TypeError, ValueError, AttributeError):
|
|
1731
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1732
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1733
|
+
or type(delay) is not int or not 0 <= delay <= 0xffffffff):
|
|
1734
|
+
raise ValueError('Invalid infobase configuration unload delay')
|
|
1735
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1736
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1737
|
+
try:
|
|
1738
|
+
async with service.factory() as client:
|
|
1739
|
+
return ReadService._public_infobase_info(
|
|
1740
|
+
await client.update_infobase_configuration_unload_delay(cluster, infobase, delay))
|
|
1741
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1742
|
+
raise ValueError('RAC infobase configuration unload delay update failed ('
|
|
1743
|
+
+ type(exc).__name__ + ')') from None
|
|
1744
|
+
|
|
1745
|
+
if allow_infobase_minimum_scheduled_jobs_period_update:
|
|
1746
|
+
@server.tool(description='MUTATION: set minimum scheduled-jobs start period without '
|
|
1747
|
+
'active users. Confirm cluster UUID:infobase UUID exactly; '
|
|
1748
|
+
'guarded client checks complete info and registration list.')
|
|
1749
|
+
async def rac_update_infobase_minimum_scheduled_jobs_start_period(
|
|
1750
|
+
cluster_id: StrictStr, infobase_id: StrictStr, period: StrictInt,
|
|
1751
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1752
|
+
try:
|
|
1753
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1754
|
+
except (TypeError, ValueError, AttributeError):
|
|
1755
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1756
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1757
|
+
or type(period) is not int or not 0 <= period <= 0xffffffff):
|
|
1758
|
+
raise ValueError('Invalid infobase minimum scheduled jobs period')
|
|
1759
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1760
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1761
|
+
try:
|
|
1762
|
+
async with service.factory() as client:
|
|
1763
|
+
return ReadService._public_infobase_info(
|
|
1764
|
+
await client.update_infobase_minimum_scheduled_jobs_start_period(cluster, infobase, period))
|
|
1765
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1766
|
+
raise ValueError('RAC infobase minimum scheduled jobs period update failed ('
|
|
1767
|
+
+ type(exc).__name__ + ')') from None
|
|
1768
|
+
|
|
1769
|
+
if allow_infobase_maximum_scheduled_jobs_shift_update:
|
|
1770
|
+
@server.tool(description='MUTATION: set maximum scheduled-jobs start shift without '
|
|
1771
|
+
'active users. Confirm cluster UUID:infobase UUID exactly; '
|
|
1772
|
+
'guarded client checks complete info and registration list.')
|
|
1773
|
+
async def rac_update_infobase_maximum_scheduled_jobs_start_shift(
|
|
1774
|
+
cluster_id: StrictStr, infobase_id: StrictStr, shift: StrictInt,
|
|
1775
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1776
|
+
try:
|
|
1777
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1778
|
+
except (TypeError, ValueError, AttributeError):
|
|
1779
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1780
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1781
|
+
or type(shift) is not int or not 0 <= shift <= 255):
|
|
1782
|
+
raise ValueError('Invalid infobase maximum scheduled jobs shift')
|
|
1783
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1784
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1785
|
+
try:
|
|
1786
|
+
async with service.factory() as client:
|
|
1787
|
+
return ReadService._public_infobase_info(
|
|
1788
|
+
await client.update_infobase_maximum_scheduled_jobs_start_shift(cluster, infobase, shift))
|
|
1789
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1790
|
+
raise ValueError('RAC infobase maximum scheduled jobs shift update failed ('
|
|
1791
|
+
+ type(exc).__name__ + ')') from None
|
|
1792
|
+
|
|
1793
|
+
if allow_infobase_safe_mode_security_profile_update:
|
|
1794
|
+
@server.tool(description='MUTATION: assign or clear an infobase safe-mode security '
|
|
1795
|
+
'profile. Confirm cluster UUID:infobase UUID exactly; guarded '
|
|
1796
|
+
'client checks complete infobase info and unchanged registration list.')
|
|
1797
|
+
async def rac_update_infobase_safe_mode_security_profile(
|
|
1798
|
+
cluster_id: StrictStr, infobase_id: StrictStr, name: StrictStr,
|
|
1799
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1800
|
+
try:
|
|
1801
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1802
|
+
except (TypeError, ValueError, AttributeError):
|
|
1803
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1804
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1805
|
+
or type(name) is not str or '\x00' in name
|
|
1806
|
+
or len(name.encode('utf-8')) > 1024):
|
|
1807
|
+
raise ValueError('Invalid infobase safe mode security profile parameters')
|
|
1808
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1809
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1810
|
+
try:
|
|
1811
|
+
async with service.factory() as client:
|
|
1812
|
+
return ReadService._public_infobase_info(
|
|
1813
|
+
await client.update_infobase_safe_mode_security_profile(cluster, infobase, name))
|
|
1814
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1815
|
+
raise ValueError('RAC infobase safe mode security profile update failed ('
|
|
1816
|
+
+ type(exc).__name__ + ')') from None
|
|
1817
|
+
|
|
1818
|
+
if allow_infobase_security_profile_update:
|
|
1819
|
+
@server.tool(description='MUTATION: assign or clear an infobase security profile. '
|
|
1820
|
+
'Confirm cluster UUID:infobase UUID exactly; guarded client '
|
|
1821
|
+
'checks complete infobase info and unchanged registration list.')
|
|
1822
|
+
async def rac_update_infobase_security_profile(cluster_id: StrictStr,
|
|
1823
|
+
infobase_id: StrictStr, name: StrictStr,
|
|
1824
|
+
confirm_cluster_infobase: StrictStr) -> dict:
|
|
1825
|
+
try:
|
|
1826
|
+
cluster, infobase = UUID(cluster_id), UUID(infobase_id)
|
|
1827
|
+
except (TypeError, ValueError, AttributeError):
|
|
1828
|
+
raise ValueError('Invalid cluster or infobase UUID') from None
|
|
1829
|
+
if (str(cluster) != cluster_id or str(infobase) != infobase_id
|
|
1830
|
+
or type(name) is not str or '\x00' in name
|
|
1831
|
+
or len(name.encode('utf-8')) > 1024):
|
|
1832
|
+
raise ValueError('Invalid infobase security profile parameters')
|
|
1833
|
+
if confirm_cluster_infobase != f'{cluster}:{infobase}':
|
|
1834
|
+
raise ValueError('Confirmation must match cluster UUID:infobase UUID exactly')
|
|
1835
|
+
try:
|
|
1836
|
+
async with service.factory() as client:
|
|
1837
|
+
return ReadService._public_infobase_info(
|
|
1838
|
+
await client.update_infobase_security_profile(cluster, infobase, name))
|
|
1839
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1840
|
+
raise ValueError('RAC infobase security profile update failed (' + type(exc).__name__ + ')') from None
|
|
1841
|
+
|
|
1842
|
+
if allow_profile_inet_update:
|
|
1843
|
+
@server.tool(description='MUTATION: update or insert a profile internet-resource ACL. '
|
|
1844
|
+
'Confirm cluster UUID:profile name:resource name; guarded client '
|
|
1845
|
+
'checks full internet-resource list after update.')
|
|
1846
|
+
async def rac_update_profile_inet(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1847
|
+
inet_name: StrictStr,
|
|
1848
|
+
fields: dict[str, StrictStr | StrictInt],
|
|
1849
|
+
confirm_cluster_profile_inet: StrictStr) -> dict:
|
|
1850
|
+
try:
|
|
1851
|
+
cluster = UUID(cluster_id)
|
|
1852
|
+
except (TypeError, ValueError, AttributeError):
|
|
1853
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1854
|
+
if (str(cluster) != cluster_id or any(
|
|
1855
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1856
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, inet_name))
|
|
1857
|
+
or type(fields) is not dict or not fields
|
|
1858
|
+
or set(fields) - {'url', 'port', 'protocol', 'descr'}
|
|
1859
|
+
or any(type(value) is not int or not 0 <= value <= 65535
|
|
1860
|
+
if key == 'port' else type(value) is not str or '\x00' in value
|
|
1861
|
+
or len(value.encode('utf-8')) > 1024
|
|
1862
|
+
for key, value in fields.items())):
|
|
1863
|
+
raise ValueError('Invalid profile inet update parameters')
|
|
1864
|
+
if confirm_cluster_profile_inet != f'{cluster}:{profile_name}:{inet_name}':
|
|
1865
|
+
raise ValueError('Confirmation must match cluster:profile:inet exactly')
|
|
1866
|
+
try:
|
|
1867
|
+
async with service.factory() as client:
|
|
1868
|
+
return await client.update_profile_inet(cluster, profile_name, inet_name,
|
|
1869
|
+
**fields)
|
|
1870
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1871
|
+
raise ValueError('RAC profile inet update failed (' + type(exc).__name__ + ')') from None
|
|
1872
|
+
|
|
1873
|
+
if allow_profile_inet_remove:
|
|
1874
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile internet-resource ACL. '
|
|
1875
|
+
'Confirm cluster UUID:profile name:resource name exactly; '
|
|
1876
|
+
'guarded client checks full inet ACL list after removal.')
|
|
1877
|
+
async def rac_remove_profile_inet(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1878
|
+
inet_name: StrictStr,
|
|
1879
|
+
confirm_cluster_profile_inet: StrictStr) -> dict:
|
|
1880
|
+
try:
|
|
1881
|
+
cluster = UUID(cluster_id)
|
|
1882
|
+
except (TypeError, ValueError, AttributeError):
|
|
1883
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1884
|
+
if (str(cluster) != cluster_id or any(
|
|
1885
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1886
|
+
or len(value.encode('utf-8')) > 1024 for value in
|
|
1887
|
+
(profile_name, inet_name))):
|
|
1888
|
+
raise ValueError('Invalid profile inet removal parameters')
|
|
1889
|
+
if confirm_cluster_profile_inet != f'{cluster}:{profile_name}:{inet_name}':
|
|
1890
|
+
raise ValueError('Confirmation must match cluster:profile:inet exactly')
|
|
1891
|
+
try:
|
|
1892
|
+
async with service.factory() as client:
|
|
1893
|
+
await client.remove_profile_inet(cluster, profile_name, inet_name)
|
|
1894
|
+
return {'removed_profile_inet': inet_name}
|
|
1895
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1896
|
+
raise ValueError('RAC profile inet remove failed (' + type(exc).__name__ + ')') from None
|
|
1897
|
+
|
|
1898
|
+
if allow_profile_app_update:
|
|
1899
|
+
@server.tool(description='MUTATION: update or insert a profile application ACL. '
|
|
1900
|
+
'Confirm cluster UUID:profile name:application name; guarded client '
|
|
1901
|
+
'checks full application list after update.')
|
|
1902
|
+
async def rac_update_profile_app(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1903
|
+
app_name: StrictStr, fields: dict[str, StrictStr],
|
|
1904
|
+
confirm_cluster_profile_app: StrictStr) -> dict:
|
|
1905
|
+
try:
|
|
1906
|
+
cluster = UUID(cluster_id)
|
|
1907
|
+
except (TypeError, ValueError, AttributeError):
|
|
1908
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1909
|
+
if (str(cluster) != cluster_id or any(
|
|
1910
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1911
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, app_name))
|
|
1912
|
+
or type(fields) is not dict or not fields
|
|
1913
|
+
or set(fields) - {'wild', 'descr'}
|
|
1914
|
+
or any(type(value) is not str or '\x00' in value
|
|
1915
|
+
or len(value.encode('utf-8')) > 1024 for value in fields.values())):
|
|
1916
|
+
raise ValueError('Invalid profile app update parameters')
|
|
1917
|
+
if confirm_cluster_profile_app != f'{cluster}:{profile_name}:{app_name}':
|
|
1918
|
+
raise ValueError('Confirmation must match cluster:profile:app exactly')
|
|
1919
|
+
try:
|
|
1920
|
+
async with service.factory() as client:
|
|
1921
|
+
return await client.update_profile_app(cluster, profile_name, app_name,
|
|
1922
|
+
**fields)
|
|
1923
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1924
|
+
raise ValueError('RAC profile app update failed (' + type(exc).__name__ + ')') from None
|
|
1925
|
+
|
|
1926
|
+
if allow_profile_app_remove:
|
|
1927
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile application ACL. '
|
|
1928
|
+
'Confirm cluster UUID:profile name:application name exactly; '
|
|
1929
|
+
'guarded client checks full application ACL list after removal.')
|
|
1930
|
+
async def rac_remove_profile_app(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1931
|
+
app_name: StrictStr,
|
|
1932
|
+
confirm_cluster_profile_app: StrictStr) -> dict:
|
|
1933
|
+
try:
|
|
1934
|
+
cluster = UUID(cluster_id)
|
|
1935
|
+
except (TypeError, ValueError, AttributeError):
|
|
1936
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1937
|
+
if (str(cluster) != cluster_id or any(
|
|
1938
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1939
|
+
or len(value.encode('utf-8')) > 1024 for value in
|
|
1940
|
+
(profile_name, app_name))):
|
|
1941
|
+
raise ValueError('Invalid profile application removal parameters')
|
|
1942
|
+
if confirm_cluster_profile_app != f'{cluster}:{profile_name}:{app_name}':
|
|
1943
|
+
raise ValueError('Confirmation must match cluster:profile:app exactly')
|
|
1944
|
+
try:
|
|
1945
|
+
async with service.factory() as client:
|
|
1946
|
+
await client.remove_profile_app(cluster, profile_name, app_name)
|
|
1947
|
+
return {'removed_profile_app': app_name}
|
|
1948
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1949
|
+
raise ValueError('RAC profile app remove failed (' + type(exc).__name__ + ')') from None
|
|
1950
|
+
|
|
1951
|
+
if allow_profile_module_update:
|
|
1952
|
+
@server.tool(description='MUTATION: update or insert a profile module ACL. '
|
|
1953
|
+
'Confirm cluster UUID:profile name:module name; guarded client '
|
|
1954
|
+
'checks full module list after update.')
|
|
1955
|
+
async def rac_update_profile_module(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1956
|
+
module_name: StrictStr, fields: dict[str, StrictStr],
|
|
1957
|
+
confirm_cluster_profile_module: StrictStr) -> dict:
|
|
1958
|
+
try:
|
|
1959
|
+
cluster = UUID(cluster_id)
|
|
1960
|
+
except (TypeError, ValueError, AttributeError):
|
|
1961
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1962
|
+
if (str(cluster) != cluster_id or any(
|
|
1963
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1964
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, module_name))
|
|
1965
|
+
or type(fields) is not dict or not fields
|
|
1966
|
+
or set(fields) - {'hash_value', 'descr'}
|
|
1967
|
+
or any(type(value) is not str or '\x00' in value
|
|
1968
|
+
or len(value.encode('utf-8')) > 1024 for value in fields.values())):
|
|
1969
|
+
raise ValueError('Invalid profile module update parameters')
|
|
1970
|
+
if confirm_cluster_profile_module != f'{cluster}:{profile_name}:{module_name}':
|
|
1971
|
+
raise ValueError('Confirmation must match cluster:profile:module exactly')
|
|
1972
|
+
try:
|
|
1973
|
+
async with service.factory() as client:
|
|
1974
|
+
return await client.update_profile_module(cluster, profile_name, module_name,
|
|
1975
|
+
**fields)
|
|
1976
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
1977
|
+
raise ValueError('RAC profile module update failed (' + type(exc).__name__ + ')') from None
|
|
1978
|
+
|
|
1979
|
+
if allow_profile_module_remove:
|
|
1980
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile module ACL. '
|
|
1981
|
+
'Confirm cluster UUID:profile name:module name exactly; '
|
|
1982
|
+
'guarded client checks full module ACL list after removal.')
|
|
1983
|
+
async def rac_remove_profile_module(cluster_id: StrictStr, profile_name: StrictStr,
|
|
1984
|
+
module_name: StrictStr,
|
|
1985
|
+
confirm_cluster_profile_module: StrictStr) -> dict:
|
|
1986
|
+
try:
|
|
1987
|
+
cluster = UUID(cluster_id)
|
|
1988
|
+
except (TypeError, ValueError, AttributeError):
|
|
1989
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
1990
|
+
if (str(cluster) != cluster_id or any(
|
|
1991
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
1992
|
+
or len(value.encode('utf-8')) > 1024 for value in
|
|
1993
|
+
(profile_name, module_name))):
|
|
1994
|
+
raise ValueError('Invalid profile module removal parameters')
|
|
1995
|
+
if confirm_cluster_profile_module != f'{cluster}:{profile_name}:{module_name}':
|
|
1996
|
+
raise ValueError('Confirmation must match cluster:profile:module exactly')
|
|
1997
|
+
try:
|
|
1998
|
+
async with service.factory() as client:
|
|
1999
|
+
await client.remove_profile_module(cluster, profile_name, module_name)
|
|
2000
|
+
return {'removed_profile_module': module_name}
|
|
2001
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2002
|
+
raise ValueError('RAC profile module remove failed (' + type(exc).__name__ + ')') from None
|
|
2003
|
+
|
|
2004
|
+
if allow_profile_addin_update:
|
|
2005
|
+
@server.tool(description='MUTATION: update or insert a profile add-in ACL. '
|
|
2006
|
+
'Confirm cluster UUID:profile name:add-in name; guarded client '
|
|
2007
|
+
'checks full add-in list after update.')
|
|
2008
|
+
async def rac_update_profile_addin(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2009
|
+
addin_name: StrictStr, fields: dict[str, StrictStr],
|
|
2010
|
+
confirm_cluster_profile_addin: StrictStr) -> dict:
|
|
2011
|
+
try:
|
|
2012
|
+
cluster = UUID(cluster_id)
|
|
2013
|
+
except (TypeError, ValueError, AttributeError):
|
|
2014
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2015
|
+
if (str(cluster) != cluster_id or any(
|
|
2016
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2017
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, addin_name))
|
|
2018
|
+
or type(fields) is not dict or not fields
|
|
2019
|
+
or set(fields) - {'hash_value', 'descr'}
|
|
2020
|
+
or any(type(value) is not str or '\x00' in value
|
|
2021
|
+
or len(value.encode('utf-8')) > 1024 for value in fields.values())):
|
|
2022
|
+
raise ValueError('Invalid profile addin update parameters')
|
|
2023
|
+
if confirm_cluster_profile_addin != f'{cluster}:{profile_name}:{addin_name}':
|
|
2024
|
+
raise ValueError('Confirmation must match cluster:profile:addin exactly')
|
|
2025
|
+
try:
|
|
2026
|
+
async with service.factory() as client:
|
|
2027
|
+
return await client.update_profile_addin(cluster, profile_name, addin_name,
|
|
2028
|
+
**fields)
|
|
2029
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2030
|
+
raise ValueError('RAC profile addin update failed (' + type(exc).__name__ + ')') from None
|
|
2031
|
+
|
|
2032
|
+
if allow_profile_addin_remove:
|
|
2033
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile add-in ACL. '
|
|
2034
|
+
'Confirm cluster UUID:profile name:add-in name exactly; '
|
|
2035
|
+
'guarded client checks full add-in ACL list after removal.')
|
|
2036
|
+
async def rac_remove_profile_addin(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2037
|
+
addin_name: StrictStr,
|
|
2038
|
+
confirm_cluster_profile_addin: StrictStr) -> dict:
|
|
2039
|
+
try:
|
|
2040
|
+
cluster = UUID(cluster_id)
|
|
2041
|
+
except (TypeError, ValueError, AttributeError):
|
|
2042
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2043
|
+
if (str(cluster) != cluster_id or any(
|
|
2044
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2045
|
+
or len(value.encode('utf-8')) > 1024 for value in
|
|
2046
|
+
(profile_name, addin_name))):
|
|
2047
|
+
raise ValueError('Invalid profile addin removal parameters')
|
|
2048
|
+
if confirm_cluster_profile_addin != f'{cluster}:{profile_name}:{addin_name}':
|
|
2049
|
+
raise ValueError('Confirmation must match cluster:profile:addin exactly')
|
|
2050
|
+
try:
|
|
2051
|
+
async with service.factory() as client:
|
|
2052
|
+
await client.remove_profile_addin(cluster, profile_name, addin_name)
|
|
2053
|
+
return {'removed_profile_addin': addin_name}
|
|
2054
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2055
|
+
raise ValueError('RAC profile addin remove failed (' + type(exc).__name__ + ')') from None
|
|
2056
|
+
|
|
2057
|
+
if allow_profile_com_update:
|
|
2058
|
+
@server.tool(description='MUTATION: update or insert a profile COM-class ACL. '
|
|
2059
|
+
'Confirm cluster UUID:profile name:class name; guarded client '
|
|
2060
|
+
'checks complete COM ACL list after update.')
|
|
2061
|
+
async def rac_update_profile_com_class(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2062
|
+
class_name: StrictStr, fields: dict[str, StrictStr],
|
|
2063
|
+
confirm_cluster_profile_class: StrictStr) -> dict:
|
|
2064
|
+
try:
|
|
2065
|
+
cluster = UUID(cluster_id)
|
|
2066
|
+
except (TypeError, ValueError, AttributeError):
|
|
2067
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2068
|
+
allowed = {'file_name', 'class_id', 'host', 'descr'}
|
|
2069
|
+
if (str(cluster) != cluster_id or any(
|
|
2070
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2071
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, class_name))
|
|
2072
|
+
or type(fields) is not dict or not fields or set(fields) - allowed
|
|
2073
|
+
or any(type(value) is not str or '\x00' in value
|
|
2074
|
+
or len(value.encode('utf-8')) > 1024 for value in fields.values())):
|
|
2075
|
+
raise ValueError('Invalid profile COM update parameters')
|
|
2076
|
+
if confirm_cluster_profile_class != f'{cluster}:{profile_name}:{class_name}':
|
|
2077
|
+
raise ValueError('Confirmation must match cluster:profile:class exactly')
|
|
2078
|
+
try:
|
|
2079
|
+
async with service.factory() as client:
|
|
2080
|
+
return await client.update_profile_com_class(cluster, profile_name, class_name,
|
|
2081
|
+
**fields)
|
|
2082
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2083
|
+
raise ValueError('RAC profile COM update failed (' + type(exc).__name__ + ')') from None
|
|
2084
|
+
|
|
2085
|
+
if allow_profile_com_remove:
|
|
2086
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile COM-class ACL. '
|
|
2087
|
+
'Confirm cluster UUID:profile name:class name exactly; guarded '
|
|
2088
|
+
'client checks full COM-class ACL list after removal.')
|
|
2089
|
+
async def rac_remove_profile_com_class(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2090
|
+
class_name: StrictStr,
|
|
2091
|
+
confirm_cluster_profile_class: StrictStr) -> dict:
|
|
2092
|
+
try:
|
|
2093
|
+
cluster = UUID(cluster_id)
|
|
2094
|
+
except (TypeError, ValueError, AttributeError):
|
|
2095
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2096
|
+
if (str(cluster) != cluster_id or any(
|
|
2097
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2098
|
+
or len(value.encode('utf-8')) > 1024 for value in
|
|
2099
|
+
(profile_name, class_name))):
|
|
2100
|
+
raise ValueError('Invalid profile COM removal parameters')
|
|
2101
|
+
if confirm_cluster_profile_class != f'{cluster}:{profile_name}:{class_name}':
|
|
2102
|
+
raise ValueError('Confirmation must match cluster:profile:class exactly')
|
|
2103
|
+
try:
|
|
2104
|
+
async with service.factory() as client:
|
|
2105
|
+
await client.remove_profile_com_class(cluster, profile_name, class_name)
|
|
2106
|
+
return {'removed_profile_com_class': class_name}
|
|
2107
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2108
|
+
raise ValueError('RAC profile COM remove failed (' + type(exc).__name__ + ')') from None
|
|
2109
|
+
|
|
2110
|
+
if allow_profile_directory_update:
|
|
2111
|
+
@server.tool(description='MUTATION: update or insert a profile directory ACL. '
|
|
2112
|
+
'Confirm cluster UUID:profile name:alias exactly; guarded client '
|
|
2113
|
+
'checks the full directory ACL list after update.')
|
|
2114
|
+
async def rac_update_profile_directory(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2115
|
+
alias: StrictStr,
|
|
2116
|
+
fields: dict[str, StrictBool | StrictStr],
|
|
2117
|
+
confirm_cluster_profile_alias: StrictStr) -> dict:
|
|
2118
|
+
try:
|
|
2119
|
+
cluster = UUID(cluster_id)
|
|
2120
|
+
except (TypeError, ValueError, AttributeError):
|
|
2121
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2122
|
+
text_fields = {'physical_path', 'descr'}
|
|
2123
|
+
bool_fields = {'allowed_read', 'allowed_write'}
|
|
2124
|
+
if (str(cluster) != cluster_id or any(
|
|
2125
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2126
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, alias))
|
|
2127
|
+
or type(fields) is not dict or not fields
|
|
2128
|
+
or set(fields) - (text_fields | bool_fields)
|
|
2129
|
+
or any(type(value) is not str or '\x00' in value
|
|
2130
|
+
or len(value.encode('utf-8')) > 1024
|
|
2131
|
+
for key, value in fields.items() if key in text_fields)
|
|
2132
|
+
or any(type(value) is not bool for key, value in fields.items()
|
|
2133
|
+
if key in bool_fields)):
|
|
2134
|
+
raise ValueError('Invalid profile directory update parameters')
|
|
2135
|
+
if confirm_cluster_profile_alias != f'{cluster}:{profile_name}:{alias}':
|
|
2136
|
+
raise ValueError('Confirmation must match cluster:profile:alias exactly')
|
|
2137
|
+
try:
|
|
2138
|
+
async with service.factory() as client:
|
|
2139
|
+
return await client.update_profile_directory(cluster, profile_name, alias,
|
|
2140
|
+
**fields)
|
|
2141
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2142
|
+
raise ValueError('RAC profile directory update failed (' + type(exc).__name__ + ')') from None
|
|
2143
|
+
|
|
2144
|
+
if allow_profile_directory_remove:
|
|
2145
|
+
@server.tool(description='DESTRUCTIVE: remove a security profile directory ACL. '
|
|
2146
|
+
'Confirm cluster UUID:profile name:directory alias exactly; '
|
|
2147
|
+
'guarded client checks full directory ACL list after removal.')
|
|
2148
|
+
async def rac_remove_profile_directory(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2149
|
+
alias: StrictStr,
|
|
2150
|
+
confirm_cluster_profile_alias: StrictStr) -> dict:
|
|
2151
|
+
try:
|
|
2152
|
+
cluster = UUID(cluster_id)
|
|
2153
|
+
except (TypeError, ValueError, AttributeError):
|
|
2154
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2155
|
+
if (str(cluster) != cluster_id or any(
|
|
2156
|
+
type(value) is not str or not value.strip() or '\x00' in value
|
|
2157
|
+
or len(value.encode('utf-8')) > 1024 for value in (profile_name, alias))):
|
|
2158
|
+
raise ValueError('Invalid profile directory removal parameters')
|
|
2159
|
+
if confirm_cluster_profile_alias != f'{cluster}:{profile_name}:{alias}':
|
|
2160
|
+
raise ValueError('Confirmation must match cluster:profile:alias exactly')
|
|
2161
|
+
try:
|
|
2162
|
+
async with service.factory() as client:
|
|
2163
|
+
await client.remove_profile_directory(cluster, profile_name, alias)
|
|
2164
|
+
return {'removed_profile_directory': alias}
|
|
2165
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2166
|
+
raise ValueError('RAC profile directory remove failed (' + type(exc).__name__ + ')') from None
|
|
2167
|
+
|
|
2168
|
+
if allow_profile_role_fields_update:
|
|
2169
|
+
@server.tool(description='MUTATION: update selected profile role/module fields. '
|
|
2170
|
+
'Confirm cluster UUID:profile name; guarded client checks the '
|
|
2171
|
+
'full list. Underlying update creates a profile if absent.')
|
|
2172
|
+
async def rac_update_profile_role_fields(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2173
|
+
fields: dict[str, StrictStr],
|
|
2174
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2175
|
+
try:
|
|
2176
|
+
cluster = UUID(cluster_id)
|
|
2177
|
+
except (TypeError, ValueError, AttributeError):
|
|
2178
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2179
|
+
allowed = {'right_extension_definition_roles', 'privileged_mode_roles',
|
|
2180
|
+
'modules_available_for_extension', 'modules_not_available_for_extension'}
|
|
2181
|
+
if (str(cluster) != cluster_id or type(profile_name) is not str
|
|
2182
|
+
or not profile_name.strip() or '\x00' in profile_name
|
|
2183
|
+
or len(profile_name.encode('utf-8')) > 1024 or type(fields) is not dict
|
|
2184
|
+
or not fields or set(fields) - allowed
|
|
2185
|
+
or any(type(value) is not str or '\x00' in value
|
|
2186
|
+
or len(value.encode('utf-8')) > 1024 for value in fields.values())):
|
|
2187
|
+
raise ValueError('Invalid profile role fields update parameters')
|
|
2188
|
+
if confirm_cluster_name != f'{cluster}:{profile_name}':
|
|
2189
|
+
raise ValueError('Confirmation must match cluster UUID:profile name exactly')
|
|
2190
|
+
try:
|
|
2191
|
+
async with service.factory() as client:
|
|
2192
|
+
return await client.update_profile(cluster, profile_name, **fields)
|
|
2193
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2194
|
+
raise ValueError('RAC profile role fields update failed (' + type(exc).__name__ + ')') from None
|
|
2195
|
+
|
|
2196
|
+
if allow_profile_access_flags_update:
|
|
2197
|
+
@server.tool(description='MUTATION: update selected security profile access flags. '
|
|
2198
|
+
'Confirm cluster UUID:profile name; guarded client checks full '
|
|
2199
|
+
'profile list. Underlying update creates a profile if absent.')
|
|
2200
|
+
async def rac_update_profile_access_flags(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2201
|
+
flags: dict[str, StrictBool],
|
|
2202
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2203
|
+
try:
|
|
2204
|
+
cluster = UUID(cluster_id)
|
|
2205
|
+
except (TypeError, ValueError, AttributeError):
|
|
2206
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2207
|
+
allowed = {'priv', 'full_privileged_mode', 'crypto', 'directory_access', 'com_access', 'addin_access',
|
|
2208
|
+
'module_access', 'app_access', 'inet_access', 'right_extension',
|
|
2209
|
+
'all_modules_extension'}
|
|
2210
|
+
if (str(cluster) != cluster_id or type(profile_name) is not str
|
|
2211
|
+
or not profile_name.strip() or '\x00' in profile_name
|
|
2212
|
+
or len(profile_name.encode('utf-8')) > 1024 or type(flags) is not dict
|
|
2213
|
+
or not flags or set(flags) - allowed
|
|
2214
|
+
or 'priv' in flags and 'full_privileged_mode' in flags
|
|
2215
|
+
or any(type(value) is not bool for value in flags.values())):
|
|
2216
|
+
raise ValueError('Invalid profile access flags update parameters')
|
|
2217
|
+
if confirm_cluster_name != f'{cluster}:{profile_name}':
|
|
2218
|
+
raise ValueError('Confirmation must match cluster UUID:profile name exactly')
|
|
2219
|
+
try:
|
|
2220
|
+
async with service.factory() as client:
|
|
2221
|
+
return await client.update_profile(cluster, profile_name, **flags)
|
|
2222
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2223
|
+
raise ValueError('RAC profile access flags update failed (' + type(exc).__name__ + ')') from None
|
|
2224
|
+
|
|
2225
|
+
if allow_profile_config_update:
|
|
2226
|
+
@server.tool(description='MUTATION: change security profile configuration access flag. '
|
|
2227
|
+
'Confirm cluster UUID:profile name; guarded client verifies the '
|
|
2228
|
+
'full profile list. Underlying update creates a profile if absent.')
|
|
2229
|
+
async def rac_update_profile_config(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2230
|
+
enabled: StrictBool, confirm_cluster_name: StrictStr) -> dict:
|
|
2231
|
+
try:
|
|
2232
|
+
cluster = UUID(cluster_id)
|
|
2233
|
+
except (TypeError, ValueError, AttributeError):
|
|
2234
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2235
|
+
if (str(cluster) != cluster_id or type(profile_name) is not str
|
|
2236
|
+
or not profile_name.strip() or '\x00' in profile_name
|
|
2237
|
+
or len(profile_name.encode('utf-8')) > 1024 or type(enabled) is not bool):
|
|
2238
|
+
raise ValueError('Invalid profile config update parameters')
|
|
2239
|
+
if confirm_cluster_name != f'{cluster}:{profile_name}':
|
|
2240
|
+
raise ValueError('Confirmation must match cluster UUID:profile name exactly')
|
|
2241
|
+
try:
|
|
2242
|
+
async with service.factory() as client:
|
|
2243
|
+
return await client.update_profile(cluster, profile_name, config=enabled)
|
|
2244
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2245
|
+
raise ValueError('RAC profile config update failed (' + type(exc).__name__ + ')') from None
|
|
2246
|
+
|
|
2247
|
+
if allow_profile_description_update:
|
|
2248
|
+
@server.tool(description='MUTATION: update or clear security profile description. '
|
|
2249
|
+
'Confirm cluster UUID:profile name; guarded client verifies '
|
|
2250
|
+
'the entire profile list after update. Creates if absent.')
|
|
2251
|
+
async def rac_update_profile_description(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2252
|
+
descr: StrictStr, confirm_cluster_name: StrictStr) -> dict:
|
|
2253
|
+
try:
|
|
2254
|
+
cluster = UUID(cluster_id)
|
|
2255
|
+
except (TypeError, ValueError, AttributeError):
|
|
2256
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2257
|
+
if (str(cluster) != cluster_id or type(profile_name) is not str
|
|
2258
|
+
or not profile_name.strip() or '\x00' in profile_name
|
|
2259
|
+
or len(profile_name.encode('utf-8')) > 1024 or type(descr) is not str
|
|
2260
|
+
or '\x00' in descr or len(descr.encode('utf-8')) > 1024):
|
|
2261
|
+
raise ValueError('Invalid profile description update parameters')
|
|
2262
|
+
if confirm_cluster_name != f'{cluster}:{profile_name}':
|
|
2263
|
+
raise ValueError('Confirmation must match cluster UUID:profile name exactly')
|
|
2264
|
+
try:
|
|
2265
|
+
async with service.factory() as client:
|
|
2266
|
+
return await client.update_profile(cluster, profile_name, descr=descr)
|
|
2267
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2268
|
+
raise ValueError('RAC profile description update failed (' + type(exc).__name__ + ')') from None
|
|
2269
|
+
|
|
2270
|
+
if allow_profile_remove:
|
|
2271
|
+
@server.tool(description='DESTRUCTIVE: remove one security profile. Confirm exact '
|
|
2272
|
+
'cluster UUID:profile name; guarded client verifies its absence '
|
|
2273
|
+
'and all other profiles unchanged via complete list read-back.')
|
|
2274
|
+
async def rac_remove_profile(cluster_id: StrictStr, profile_name: StrictStr,
|
|
2275
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2276
|
+
try:
|
|
2277
|
+
cluster = UUID(cluster_id)
|
|
2278
|
+
except (TypeError, ValueError, AttributeError):
|
|
2279
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2280
|
+
if (str(cluster) != cluster_id or type(profile_name) is not str
|
|
2281
|
+
or not profile_name.strip() or '\x00' in profile_name
|
|
2282
|
+
or len(profile_name.encode('utf-8')) > 1024):
|
|
2283
|
+
raise ValueError('Invalid profile removal parameters')
|
|
2284
|
+
if confirm_cluster_name != f'{cluster}:{profile_name}':
|
|
2285
|
+
raise ValueError('Confirmation must match cluster UUID:profile name exactly')
|
|
2286
|
+
try:
|
|
2287
|
+
async with service.factory() as client:
|
|
2288
|
+
await client.remove_profile(cluster, profile_name)
|
|
2289
|
+
return {'removed_profile': profile_name}
|
|
2290
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2291
|
+
raise ValueError('RAC profile remove failed (' + type(exc).__name__ + ')') from None
|
|
2292
|
+
|
|
2293
|
+
if allow_service_setting_insert:
|
|
2294
|
+
@server.tool(description='MUTATION: insert a service setting. Confirm exact '
|
|
2295
|
+
'cluster UUID:working server UUID:service name:infobase name; '
|
|
2296
|
+
'guarded client checks the complete settings list after insert.')
|
|
2297
|
+
async def rac_insert_service_setting(cluster_id: StrictStr, server_id: StrictStr,
|
|
2298
|
+
service_name: StrictStr, infobase_name: StrictStr,
|
|
2299
|
+
service_data_dir: StrictStr,
|
|
2300
|
+
confirm_cluster_server_service_infobase: StrictStr) -> dict:
|
|
2301
|
+
try:
|
|
2302
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
2303
|
+
except (TypeError, ValueError, AttributeError):
|
|
2304
|
+
raise ValueError('Invalid cluster or working server UUID') from None
|
|
2305
|
+
if str(cluster) != cluster_id or str(working_server) != server_id:
|
|
2306
|
+
raise ValueError('UUIDs must use canonical form')
|
|
2307
|
+
for key, value in (('service name', service_name), ('infobase name', infobase_name),
|
|
2308
|
+
('service data dir', service_data_dir)):
|
|
2309
|
+
if (type(value) is not str or '\x00' in value or len(value.encode('utf-8')) > 1024
|
|
2310
|
+
or key == 'service name' and not value.strip()):
|
|
2311
|
+
raise ValueError('Invalid ' + key)
|
|
2312
|
+
if confirm_cluster_server_service_infobase != (
|
|
2313
|
+
f'{cluster}:{working_server}:{service_name}:{infobase_name}'):
|
|
2314
|
+
raise ValueError('Confirmation must match cluster:server:service:infobase exactly')
|
|
2315
|
+
try:
|
|
2316
|
+
async with service.factory() as client:
|
|
2317
|
+
return await client.insert_service_setting(
|
|
2318
|
+
cluster, working_server, service_name,
|
|
2319
|
+
infobase_name=infobase_name, service_data_dir=service_data_dir)
|
|
2320
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2321
|
+
raise ValueError('RAC service setting insert failed (' + type(exc).__name__ + ')') from None
|
|
2322
|
+
|
|
2323
|
+
if allow_service_setting_update:
|
|
2324
|
+
@server.tool(description='MUTATION: update a service setting data directory. '
|
|
2325
|
+
'Confirm cluster UUID:working server UUID:setting UUID exactly; '
|
|
2326
|
+
'guarded client checks the full settings list after update.')
|
|
2327
|
+
async def rac_update_service_setting(cluster_id: StrictStr, server_id: StrictStr,
|
|
2328
|
+
setting_id: StrictStr, service_data_dir: StrictStr,
|
|
2329
|
+
confirm_cluster_server_setting: StrictStr) -> dict:
|
|
2330
|
+
try:
|
|
2331
|
+
cluster, working_server, setting = (UUID(value) for value in
|
|
2332
|
+
(cluster_id, server_id, setting_id))
|
|
2333
|
+
except (TypeError, ValueError, AttributeError):
|
|
2334
|
+
raise ValueError('Invalid cluster, working server, or setting UUID') from None
|
|
2335
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2336
|
+
or str(setting) != setting_id or type(service_data_dir) is not str
|
|
2337
|
+
or '\x00' in service_data_dir
|
|
2338
|
+
or len(service_data_dir.encode('utf-8')) > 1024):
|
|
2339
|
+
raise ValueError('Invalid service setting update parameters')
|
|
2340
|
+
if confirm_cluster_server_setting != f'{cluster}:{working_server}:{setting}':
|
|
2341
|
+
raise ValueError('Confirmation must match cluster:server:setting UUIDs exactly')
|
|
2342
|
+
try:
|
|
2343
|
+
async with service.factory() as client:
|
|
2344
|
+
return await client.update_service_setting(cluster, working_server, setting,
|
|
2345
|
+
service_data_dir)
|
|
2346
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2347
|
+
raise ValueError('RAC service setting update failed (' + type(exc).__name__ + ')') from None
|
|
2348
|
+
|
|
2349
|
+
if allow_service_setting_remove:
|
|
2350
|
+
@server.tool(description='DESTRUCTIVE: remove one service setting. Confirm exact '
|
|
2351
|
+
'cluster UUID:working server UUID:setting UUID; guarded client '
|
|
2352
|
+
'checks the full settings list after removal.')
|
|
2353
|
+
async def rac_remove_service_setting(cluster_id: StrictStr, server_id: StrictStr,
|
|
2354
|
+
setting_id: StrictStr,
|
|
2355
|
+
confirm_cluster_server_setting: StrictStr) -> None:
|
|
2356
|
+
try:
|
|
2357
|
+
cluster, working_server, setting = (UUID(value) for value in
|
|
2358
|
+
(cluster_id, server_id, setting_id))
|
|
2359
|
+
except (TypeError, ValueError, AttributeError):
|
|
2360
|
+
raise ValueError('Invalid cluster, working server, or setting UUID') from None
|
|
2361
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2362
|
+
or str(setting) != setting_id):
|
|
2363
|
+
raise ValueError('UUIDs must use canonical form')
|
|
2364
|
+
if confirm_cluster_server_setting != f'{cluster}:{working_server}:{setting}':
|
|
2365
|
+
raise ValueError('Confirmation must match cluster:server:setting UUIDs exactly')
|
|
2366
|
+
try:
|
|
2367
|
+
async with service.factory() as client:
|
|
2368
|
+
await client.remove_service_setting(cluster, working_server, setting)
|
|
2369
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2370
|
+
raise ValueError('RAC service setting remove failed (' + type(exc).__name__ + ')') from None
|
|
2371
|
+
|
|
2372
|
+
if allow_service_settings_apply:
|
|
2373
|
+
@server.tool(description='MUTATION: apply pending service settings. Confirm the exact '
|
|
2374
|
+
'cluster UUID:working server UUID; guarded client checks all '
|
|
2375
|
+
'settings have become active.')
|
|
2376
|
+
async def rac_apply_service_settings(cluster_id: StrictStr, server_id: StrictStr,
|
|
2377
|
+
confirm_cluster_server: StrictStr) -> list[dict]:
|
|
2378
|
+
try:
|
|
2379
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
2380
|
+
except (TypeError, ValueError, AttributeError):
|
|
2381
|
+
raise ValueError('Invalid cluster or working server UUID') from None
|
|
2382
|
+
if str(cluster) != cluster_id or str(working_server) != server_id:
|
|
2383
|
+
raise ValueError('UUIDs must use canonical form')
|
|
2384
|
+
if confirm_cluster_server != f'{cluster}:{working_server}':
|
|
2385
|
+
raise ValueError('Confirmation must match cluster UUID:working server UUID exactly')
|
|
2386
|
+
try:
|
|
2387
|
+
async with service.factory() as client:
|
|
2388
|
+
return await client.apply_service_settings(cluster, working_server)
|
|
2389
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2390
|
+
raise ValueError('RAC service settings apply failed (' + type(exc).__name__ + ')') from None
|
|
2391
|
+
|
|
2392
|
+
if allow_rule_apply:
|
|
2393
|
+
@server.tool(description='MUTATION: apply cluster assignment rules in full or partial mode. '
|
|
2394
|
+
'Confirm the exact cluster UUID; guarded client checks all rule lists '
|
|
2395
|
+
'unchanged, but effective process allocation is not observable.')
|
|
2396
|
+
async def rac_apply_rules(cluster_id: StrictStr, full: StrictBool,
|
|
2397
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2398
|
+
try:
|
|
2399
|
+
cluster = UUID(cluster_id)
|
|
2400
|
+
except (TypeError, ValueError, AttributeError):
|
|
2401
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2402
|
+
if str(cluster) != cluster_id or type(full) is not bool:
|
|
2403
|
+
raise ValueError('Invalid assignment rule apply parameters')
|
|
2404
|
+
if confirm_cluster_id != cluster_id:
|
|
2405
|
+
raise ValueError('Confirmation must match cluster UUID exactly')
|
|
2406
|
+
try:
|
|
2407
|
+
async with service.factory() as client:
|
|
2408
|
+
return await client.apply_rules(cluster, full=full)
|
|
2409
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2410
|
+
raise ValueError('RAC rule apply failed (' + type(exc).__name__ + ')') from None
|
|
2411
|
+
|
|
2412
|
+
if allow_rule_insert:
|
|
2413
|
+
@server.tool(description='MUTATION: insert a cluster assignment rule at a position. Confirm '
|
|
2414
|
+
'cluster UUID:server UUID:position exactly; guarded client checks full rule list.')
|
|
2415
|
+
async def rac_insert_rule(cluster_id: StrictStr, server_id: StrictStr, position: StrictInt,
|
|
2416
|
+
rule_type: StrictStr, priority: StrictInt,
|
|
2417
|
+
confirm_cluster_server_position: StrictStr) -> dict:
|
|
2418
|
+
try:
|
|
2419
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
2420
|
+
except (TypeError, ValueError, AttributeError):
|
|
2421
|
+
raise ValueError('Invalid cluster or server UUID') from None
|
|
2422
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2423
|
+
or type(position) is not int or not 0 <= position < 2**31
|
|
2424
|
+
or type(priority) is not int or not -(2**31) <= priority < 2**31
|
|
2425
|
+
or rule_type not in ('never', 'auto', 'always')):
|
|
2426
|
+
raise ValueError('Invalid assignment rule parameters')
|
|
2427
|
+
if confirm_cluster_server_position != f'{cluster}:{working_server}:{position}':
|
|
2428
|
+
raise ValueError('Confirmation must match cluster UUID:server UUID:position exactly')
|
|
2429
|
+
try:
|
|
2430
|
+
async with service.factory() as client:
|
|
2431
|
+
return await client.insert_rule(cluster, working_server, position,
|
|
2432
|
+
rule_type=rule_type, priority=priority)
|
|
2433
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2434
|
+
raise ValueError('RAC rule insert failed (' + type(exc).__name__ + ')') from None
|
|
2435
|
+
|
|
2436
|
+
if allow_rule_remove:
|
|
2437
|
+
@server.tool(description='DESTRUCTIVE: remove an assignment rule by exact cluster, working-server '
|
|
2438
|
+
'and rule UUIDs. Guarded client checks the entire rule list after removal.')
|
|
2439
|
+
async def rac_remove_rule(cluster_id: StrictStr, server_id: StrictStr, rule_id: StrictStr,
|
|
2440
|
+
confirm_cluster_server_rule: StrictStr) -> dict:
|
|
2441
|
+
try:
|
|
2442
|
+
cluster, working_server, rule = UUID(cluster_id), UUID(server_id), UUID(rule_id)
|
|
2443
|
+
except (TypeError, ValueError, AttributeError):
|
|
2444
|
+
raise ValueError('Invalid cluster, server or rule UUID') from None
|
|
2445
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2446
|
+
or str(rule) != rule_id):
|
|
2447
|
+
raise ValueError('Non-canonical cluster, server or rule UUID')
|
|
2448
|
+
if confirm_cluster_server_rule != f'{cluster}:{working_server}:{rule}':
|
|
2449
|
+
raise ValueError('Confirmation must match cluster UUID:server UUID:rule UUID exactly')
|
|
2450
|
+
try:
|
|
2451
|
+
async with service.factory() as client:
|
|
2452
|
+
await client.remove_rule(cluster, working_server, rule)
|
|
2453
|
+
return {'removed_rule': str(rule)}
|
|
2454
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2455
|
+
raise ValueError('RAC rule remove failed (' + type(exc).__name__ + ')') from None
|
|
2456
|
+
|
|
2457
|
+
if allow_rule_position_update:
|
|
2458
|
+
@server.tool(description='MUTATION: move an existing assignment rule without changing fields. '
|
|
2459
|
+
'Confirm cluster UUID:server UUID:rule UUID; guarded client checks full rule list.')
|
|
2460
|
+
async def rac_move_rule(cluster_id: StrictStr, server_id: StrictStr, rule_id: StrictStr,
|
|
2461
|
+
position: StrictInt, confirm_cluster_server_rule: StrictStr) -> dict:
|
|
2462
|
+
try:
|
|
2463
|
+
cluster, working_server, rule = UUID(cluster_id), UUID(server_id), UUID(rule_id)
|
|
2464
|
+
except (TypeError, ValueError, AttributeError):
|
|
2465
|
+
raise ValueError('Invalid cluster, server or rule UUID') from None
|
|
2466
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2467
|
+
or str(rule) != rule_id or type(position) is not int
|
|
2468
|
+
or not 0 <= position < 2**31):
|
|
2469
|
+
raise ValueError('Invalid assignment rule position')
|
|
2470
|
+
if confirm_cluster_server_rule != f'{cluster}:{working_server}:{rule}':
|
|
2471
|
+
raise ValueError('Confirmation must match cluster UUID:server UUID:rule UUID exactly')
|
|
2472
|
+
try:
|
|
2473
|
+
async with service.factory() as client:
|
|
2474
|
+
return await client.update_rule(cluster, working_server, rule, position)
|
|
2475
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2476
|
+
raise ValueError('RAC rule position update failed (' + type(exc).__name__ + ')') from None
|
|
2477
|
+
|
|
2478
|
+
if allow_rule_fields_update:
|
|
2479
|
+
@server.tool(description='MUTATION: update selected assignment-rule fields. Confirm '
|
|
2480
|
+
'cluster UUID:server UUID:rule UUID; guarded client checks full rule list.')
|
|
2481
|
+
async def rac_update_rule_fields(cluster_id: StrictStr, server_id: StrictStr,
|
|
2482
|
+
rule_id: StrictStr, position: StrictInt,
|
|
2483
|
+
fields: dict[str, StrictStr | StrictInt],
|
|
2484
|
+
confirm_cluster_server_rule: StrictStr) -> dict:
|
|
2485
|
+
try:
|
|
2486
|
+
cluster, working_server, rule = UUID(cluster_id), UUID(server_id), UUID(rule_id)
|
|
2487
|
+
except (TypeError, ValueError, AttributeError):
|
|
2488
|
+
raise ValueError('Invalid cluster, server or rule UUID') from None
|
|
2489
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id
|
|
2490
|
+
or str(rule) != rule_id or type(position) is not int
|
|
2491
|
+
or not 0 <= position < 2**31 or type(fields) is not dict or not fields
|
|
2492
|
+
or set(fields) - {'object_type', 'infobase_name', 'rule_type',
|
|
2493
|
+
'application_ext', 'priority'}
|
|
2494
|
+
or any((type(value) is not int or not -(2**31) <= value < 2**31)
|
|
2495
|
+
if key == 'priority' else
|
|
2496
|
+
(type(value) is not str or '\x00' in value
|
|
2497
|
+
or len(value.encode('utf-8')) > 1024
|
|
2498
|
+
or key == 'rule_type' and value not in ('never', 'auto', 'always'))
|
|
2499
|
+
for key, value in fields.items())):
|
|
2500
|
+
raise ValueError('Invalid assignment rule fields')
|
|
2501
|
+
if confirm_cluster_server_rule != f'{cluster}:{working_server}:{rule}':
|
|
2502
|
+
raise ValueError('Confirmation must match cluster UUID:server UUID:rule UUID exactly')
|
|
2503
|
+
try:
|
|
2504
|
+
async with service.factory() as client:
|
|
2505
|
+
return await client.update_rule(cluster, working_server, rule, position, **fields)
|
|
2506
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2507
|
+
raise ValueError('RAC rule fields update failed (' + type(exc).__name__ + ')') from None
|
|
2508
|
+
|
|
2509
|
+
if allow_rule_priority_update:
|
|
2510
|
+
@server.tool(description='MUTATION: update priority of a selected assignment rule without changing '
|
|
2511
|
+
'other rule fields. Confirm cluster UUID:server UUID:rule UUID exactly; '
|
|
2512
|
+
'guarded client verifies the entire rule list.')
|
|
2513
|
+
async def rac_update_rule_priority(cluster_id: StrictStr, server_id: StrictStr,
|
|
2514
|
+
rule_id: StrictStr, position: StrictInt,
|
|
2515
|
+
priority: StrictInt,
|
|
2516
|
+
confirm_cluster_server_rule: StrictStr) -> dict:
|
|
2517
|
+
try:
|
|
2518
|
+
cluster, working_server, rule = UUID(cluster_id), UUID(server_id), UUID(rule_id)
|
|
2519
|
+
except (TypeError, ValueError, AttributeError):
|
|
2520
|
+
raise ValueError('Invalid cluster, server or rule UUID') from None
|
|
2521
|
+
if (str(cluster) != cluster_id or str(working_server) != server_id or str(rule) != rule_id
|
|
2522
|
+
or type(position) is not int or not 0 <= position < 2**31
|
|
2523
|
+
or type(priority) is not int or not -(2**31) <= priority < 2**31):
|
|
2524
|
+
raise ValueError('Invalid assignment rule parameters')
|
|
2525
|
+
if confirm_cluster_server_rule != f'{cluster}:{working_server}:{rule}':
|
|
2526
|
+
raise ValueError('Confirmation must match cluster UUID:server UUID:rule UUID exactly')
|
|
2527
|
+
try:
|
|
2528
|
+
async with service.factory() as client:
|
|
2529
|
+
return await client.update_rule(cluster, working_server, rule, position,
|
|
2530
|
+
priority=priority)
|
|
2531
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2532
|
+
raise ValueError('RAC rule priority update failed (' + type(exc).__name__ + ')') from None
|
|
2533
|
+
|
|
2534
|
+
if allow_counter_clear:
|
|
2535
|
+
@server.tool(description='DESTRUCTIVE: request clear of named counter readings. '
|
|
2536
|
+
'Confirm cluster UUID:name exactly; guarded client checks configuration '
|
|
2537
|
+
'unchanged, but populated reading deletion is not live-verified.')
|
|
2538
|
+
async def rac_clear_counter(cluster_id: StrictStr, name: StrictStr,
|
|
2539
|
+
object_filter: StrictStr,
|
|
2540
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2541
|
+
try:
|
|
2542
|
+
cluster = UUID(cluster_id)
|
|
2543
|
+
except (TypeError, ValueError, AttributeError):
|
|
2544
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2545
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2546
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2547
|
+
or type(object_filter) is not str or '\x00' in object_filter
|
|
2548
|
+
or len(object_filter.encode('utf-8')) > 4096):
|
|
2549
|
+
raise ValueError('Invalid counter clear parameters')
|
|
2550
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2551
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2552
|
+
try:
|
|
2553
|
+
async with service.factory() as client:
|
|
2554
|
+
return await client.clear_counter(cluster, name, object_filter)
|
|
2555
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2556
|
+
raise ValueError('RAC counter clear failed (' + type(exc).__name__ + ')') from None
|
|
2557
|
+
|
|
2558
|
+
if allow_counter_analysis_flags_update:
|
|
2559
|
+
@server.tool(description='MUTATION: update selected named-counter analysis flags. '
|
|
2560
|
+
'Confirm cluster UUID:name exactly; guarded client checks full list.')
|
|
2561
|
+
async def rac_update_counter_analysis_flags(cluster_id: StrictStr, name: StrictStr,
|
|
2562
|
+
flags: dict[str, StrictBool],
|
|
2563
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2564
|
+
try:
|
|
2565
|
+
cluster = UUID(cluster_id)
|
|
2566
|
+
except (TypeError, ValueError, AttributeError):
|
|
2567
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2568
|
+
allowed = {'duration', 'cpu_time', 'memory', 'read', 'write', 'duration_dbms',
|
|
2569
|
+
'dbms_bytes', 'service', 'call', 'number_of_active_sessions',
|
|
2570
|
+
'number_of_sessions'}
|
|
2571
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2572
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2573
|
+
or type(flags) is not dict or not flags or any(
|
|
2574
|
+
key not in allowed or type(value) is not bool
|
|
2575
|
+
for key, value in flags.items())):
|
|
2576
|
+
raise ValueError('Invalid counter analysis flags parameters')
|
|
2577
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2578
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2579
|
+
try:
|
|
2580
|
+
async with service.factory() as client:
|
|
2581
|
+
return await client.update_counter(cluster, name, **flags)
|
|
2582
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2583
|
+
raise ValueError('RAC counter analysis flags update failed (' + type(exc).__name__ + ')') from None
|
|
2584
|
+
|
|
2585
|
+
if allow_counter_filter_update:
|
|
2586
|
+
@server.tool(description='MUTATION: update or clear a named counter object filter. '
|
|
2587
|
+
'Confirm cluster UUID:name exactly; guarded client checks full list.')
|
|
2588
|
+
async def rac_update_counter_filter(cluster_id: StrictStr, name: StrictStr,
|
|
2589
|
+
object_filter: StrictStr,
|
|
2590
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2591
|
+
try:
|
|
2592
|
+
cluster = UUID(cluster_id)
|
|
2593
|
+
except (TypeError, ValueError, AttributeError):
|
|
2594
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2595
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2596
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2597
|
+
or type(object_filter) is not str or '\x00' in object_filter
|
|
2598
|
+
or len(object_filter.encode('utf-8')) > 1024):
|
|
2599
|
+
raise ValueError('Invalid counter filter parameters')
|
|
2600
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2601
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2602
|
+
try:
|
|
2603
|
+
async with service.factory() as client:
|
|
2604
|
+
return await client.update_counter(cluster, name, filter=object_filter)
|
|
2605
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2606
|
+
raise ValueError('RAC counter filter update failed (' + type(exc).__name__ + ')') from None
|
|
2607
|
+
|
|
2608
|
+
if allow_counter_filter_type_update:
|
|
2609
|
+
@server.tool(description='MUTATION: update a named counter filter type. Confirm cluster '
|
|
2610
|
+
'UUID:name exactly; guarded client verifies full counter list.')
|
|
2611
|
+
async def rac_update_counter_filter_type(cluster_id: StrictStr, name: StrictStr,
|
|
2612
|
+
mode: StrictStr,
|
|
2613
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2614
|
+
try:
|
|
2615
|
+
cluster = UUID(cluster_id)
|
|
2616
|
+
except (TypeError, ValueError, AttributeError):
|
|
2617
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2618
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2619
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2620
|
+
or mode not in ('all', 'all-selected', 'all-but-selected')):
|
|
2621
|
+
raise ValueError('Invalid counter filter type parameters')
|
|
2622
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2623
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2624
|
+
try:
|
|
2625
|
+
async with service.factory() as client:
|
|
2626
|
+
return await client.update_counter(cluster, name, filter_type=mode)
|
|
2627
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2628
|
+
raise ValueError('RAC counter filter type update failed (' + type(exc).__name__ + ')') from None
|
|
2629
|
+
|
|
2630
|
+
if allow_counter_group_update:
|
|
2631
|
+
@server.tool(description='MUTATION: update a named counter group. Confirm cluster '
|
|
2632
|
+
'UUID:name exactly; guarded client verifies full counter list.')
|
|
2633
|
+
async def rac_update_counter_group(cluster_id: StrictStr, name: StrictStr, group: StrictStr,
|
|
2634
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2635
|
+
try:
|
|
2636
|
+
cluster = UUID(cluster_id)
|
|
2637
|
+
except (TypeError, ValueError, AttributeError):
|
|
2638
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2639
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2640
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2641
|
+
or group not in ('users', 'data-separation')):
|
|
2642
|
+
raise ValueError('Invalid counter group parameters')
|
|
2643
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2644
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2645
|
+
try:
|
|
2646
|
+
async with service.factory() as client:
|
|
2647
|
+
return await client.update_counter(cluster, name, group=group)
|
|
2648
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2649
|
+
raise ValueError('RAC counter group update failed (' + type(exc).__name__ + ')') from None
|
|
2650
|
+
|
|
2651
|
+
if allow_counter_duration_update:
|
|
2652
|
+
@server.tool(description='MUTATION: toggle duration analysis for a named counter. '
|
|
2653
|
+
'Confirm cluster UUID:name exactly; guarded client checks full list.')
|
|
2654
|
+
async def rac_update_counter_duration(cluster_id: StrictStr, name: StrictStr, enabled: StrictBool,
|
|
2655
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2656
|
+
try:
|
|
2657
|
+
cluster = UUID(cluster_id)
|
|
2658
|
+
except (TypeError, ValueError, AttributeError):
|
|
2659
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2660
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2661
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2662
|
+
or type(enabled) is not bool):
|
|
2663
|
+
raise ValueError('Invalid counter duration parameters')
|
|
2664
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2665
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2666
|
+
try:
|
|
2667
|
+
async with service.factory() as client:
|
|
2668
|
+
return await client.update_counter(cluster, name, duration=enabled)
|
|
2669
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2670
|
+
raise ValueError('RAC counter duration update failed (' + type(exc).__name__ + ')') from None
|
|
2671
|
+
|
|
2672
|
+
if allow_counter_collection_time_update:
|
|
2673
|
+
@server.tool(description='MUTATION: update a named counter collection time. Confirm '
|
|
2674
|
+
'cluster UUID:name exactly; guarded client checks the full list.')
|
|
2675
|
+
async def rac_update_counter_collection_time(cluster_id: StrictStr, name: StrictStr, seconds: StrictInt,
|
|
2676
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2677
|
+
try:
|
|
2678
|
+
cluster = UUID(cluster_id)
|
|
2679
|
+
except (TypeError, ValueError, AttributeError):
|
|
2680
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2681
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2682
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2683
|
+
or type(seconds) is not int or not 0 <= seconds < 2**63):
|
|
2684
|
+
raise ValueError('Invalid counter collection time parameters')
|
|
2685
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2686
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2687
|
+
try:
|
|
2688
|
+
async with service.factory() as client:
|
|
2689
|
+
return await client.update_counter(cluster, name, collection_time=seconds)
|
|
2690
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2691
|
+
raise ValueError('RAC counter collection time update failed (' + type(exc).__name__ + ')') from None
|
|
2692
|
+
|
|
2693
|
+
if allow_counter_description_update:
|
|
2694
|
+
@server.tool(description='MUTATION: update the description of a named cluster counter. '
|
|
2695
|
+
'Confirm cluster UUID:name exactly; guarded client verifies full list.')
|
|
2696
|
+
async def rac_update_counter_description(cluster_id: StrictStr, name: StrictStr, descr: StrictStr,
|
|
2697
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2698
|
+
try:
|
|
2699
|
+
cluster = UUID(cluster_id)
|
|
2700
|
+
except (TypeError, ValueError, AttributeError):
|
|
2701
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2702
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2703
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2704
|
+
or type(descr) is not str or '\x00' in descr
|
|
2705
|
+
or len(descr.encode('utf-8')) > 1024):
|
|
2706
|
+
raise ValueError('Invalid counter description parameters')
|
|
2707
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2708
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2709
|
+
try:
|
|
2710
|
+
async with service.factory() as client:
|
|
2711
|
+
return await client.update_counter(cluster, name, descr=descr)
|
|
2712
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2713
|
+
raise ValueError('RAC counter description update failed (' + type(exc).__name__ + ')') from None
|
|
2714
|
+
|
|
2715
|
+
if allow_counter_remove:
|
|
2716
|
+
@server.tool(description='DESTRUCTIVE: remove a named cluster counter. Confirm cluster '
|
|
2717
|
+
'UUID:name exactly; guarded client checks the complete counter list.')
|
|
2718
|
+
async def rac_remove_counter(cluster_id: StrictStr, name: StrictStr,
|
|
2719
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2720
|
+
try:
|
|
2721
|
+
cluster = UUID(cluster_id)
|
|
2722
|
+
except (TypeError, ValueError, AttributeError):
|
|
2723
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2724
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2725
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024):
|
|
2726
|
+
raise ValueError('Invalid counter parameters')
|
|
2727
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2728
|
+
raise ValueError('Confirmation must match cluster UUID:counter name exactly')
|
|
2729
|
+
try:
|
|
2730
|
+
async with service.factory() as client:
|
|
2731
|
+
await client.remove_counter(cluster, name)
|
|
2732
|
+
return {'removed_counter': name}
|
|
2733
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2734
|
+
raise ValueError('RAC counter remove failed (' + type(exc).__name__ + ')') from None
|
|
2735
|
+
|
|
2736
|
+
if allow_limit_update:
|
|
2737
|
+
@server.tool(description='MUTATION: create or update a named cluster limit with action, '
|
|
2738
|
+
'counter and duration threshold. Confirm cluster UUID:name exactly; '
|
|
2739
|
+
'guarded client verifies the complete limit list.')
|
|
2740
|
+
async def rac_update_limit(cluster_id: StrictStr, name: StrictStr, action: StrictStr,
|
|
2741
|
+
counter: StrictStr, duration: StrictInt,
|
|
2742
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2743
|
+
try:
|
|
2744
|
+
cluster = UUID(cluster_id)
|
|
2745
|
+
except (TypeError, ValueError, AttributeError):
|
|
2746
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2747
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2748
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024
|
|
2749
|
+
or action not in ('none', 'set-low-priority-thread', 'interrupt-current-call',
|
|
2750
|
+
'interrupt-session') or type(counter) is not str
|
|
2751
|
+
or '\x00' in counter or len(counter.encode('utf-8')) > 1024
|
|
2752
|
+
or type(duration) is not int or not 0 <= duration < 2**63):
|
|
2753
|
+
raise ValueError('Invalid limit parameters')
|
|
2754
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2755
|
+
raise ValueError('Confirmation must match cluster UUID:limit name exactly')
|
|
2756
|
+
try:
|
|
2757
|
+
async with service.factory() as client:
|
|
2758
|
+
return await client.update_limit(cluster, name, action=action,
|
|
2759
|
+
counter=counter, duration=duration)
|
|
2760
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2761
|
+
raise ValueError('RAC limit update failed (' + type(exc).__name__ + ')') from None
|
|
2762
|
+
|
|
2763
|
+
if allow_limit_remove:
|
|
2764
|
+
@server.tool(description='DESTRUCTIVE: remove a named cluster limit. Confirm the exact cluster '
|
|
2765
|
+
'UUID:name; guarded client checks the complete limit list after removal.')
|
|
2766
|
+
async def rac_remove_limit(cluster_id: StrictStr, name: StrictStr,
|
|
2767
|
+
confirm_cluster_name: StrictStr) -> dict:
|
|
2768
|
+
try:
|
|
2769
|
+
cluster = UUID(cluster_id)
|
|
2770
|
+
except (TypeError, ValueError, AttributeError):
|
|
2771
|
+
raise ValueError('Invalid cluster UUID') from None
|
|
2772
|
+
if (str(cluster) != cluster_id or type(name) is not str or not name.strip()
|
|
2773
|
+
or '\x00' in name or len(name.encode('utf-8')) > 1024):
|
|
2774
|
+
raise ValueError('Invalid limit parameters')
|
|
2775
|
+
if confirm_cluster_name != f'{cluster}:{name}':
|
|
2776
|
+
raise ValueError('Confirmation must match cluster UUID:limit name exactly')
|
|
2777
|
+
try:
|
|
2778
|
+
async with service.factory() as client:
|
|
2779
|
+
await client.remove_limit(cluster, name)
|
|
2780
|
+
return {'removed_limit': name}
|
|
2781
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2782
|
+
raise ValueError('RAC limit remove failed (' + type(exc).__name__ + ')') from None
|
|
2783
|
+
|
|
2784
|
+
if allow_cluster_insert:
|
|
2785
|
+
@server.tool(description='MUTATION: insert a new cluster on the configured RAS. '
|
|
2786
|
+
'Confirm the exact host:port; the client requires a full list/info read-back. '
|
|
2787
|
+
'Only independently verified 16.0 options are exposed.')
|
|
2788
|
+
async def rac_insert_cluster(host: StrictStr, port: StrictInt, name: StrictStr, audit: StrictBool,
|
|
2789
|
+
confirm_host_port: StrictStr, expiration_timeout: StrictInt = 60,
|
|
2790
|
+
security_level: StrictInt = 0,
|
|
2791
|
+
session_fault_tolerance_level: StrictInt = 0,
|
|
2792
|
+
load_balancing_mode: StrictStr = 'performance',
|
|
2793
|
+
kill_problem_processes: StrictBool = True,
|
|
2794
|
+
kill_by_memory_with_dump: StrictBool = False,
|
|
2795
|
+
restart_schedule: StrictStr = '', ping_period: StrictInt = 0,
|
|
2796
|
+
ping_timeout: StrictInt = 0) -> dict:
|
|
2797
|
+
if (type(host) is not str or not host or '\x00' in host
|
|
2798
|
+
or len(host.encode('utf-8')) > 255 or type(port) is not int
|
|
2799
|
+
or not 1 <= port <= 65535 or type(name) is not str or '\x00' in name
|
|
2800
|
+
or len(name.encode('utf-8')) > 256 or type(audit) is not bool):
|
|
2801
|
+
raise ValueError('Invalid cluster insert parameters')
|
|
2802
|
+
if confirm_host_port != f'{host}:{port}':
|
|
2803
|
+
raise ValueError('Confirmation must match the cluster host:port exactly')
|
|
2804
|
+
if (type(ping_period) is not int or type(ping_timeout) is not int
|
|
2805
|
+
or not ((ping_period == ping_timeout == 0)
|
|
2806
|
+
or 0 < ping_period < ping_timeout < 2**31)):
|
|
2807
|
+
raise ValueError('Invalid cluster ping period/timeout pair')
|
|
2808
|
+
for value in (expiration_timeout, security_level, session_fault_tolerance_level):
|
|
2809
|
+
if type(value) is not int or not 0 <= value < 2**31:
|
|
2810
|
+
raise ValueError('Invalid cluster insert numeric setting')
|
|
2811
|
+
if (type(load_balancing_mode) is not str
|
|
2812
|
+
or load_balancing_mode not in ('performance', 'memory')
|
|
2813
|
+
or type(kill_problem_processes) is not bool
|
|
2814
|
+
or type(kill_by_memory_with_dump) is not bool
|
|
2815
|
+
or type(restart_schedule) is not str or '\x00' in restart_schedule
|
|
2816
|
+
or (restart_schedule and (len(restart_schedule.encode('utf-8')) > 256
|
|
2817
|
+
or len(restart_schedule.split()) != 6
|
|
2818
|
+
or any(not part.isascii() or not part.isdecimal()
|
|
2819
|
+
for part in restart_schedule.split())))):
|
|
2820
|
+
raise ValueError('Invalid cluster insert option')
|
|
2821
|
+
defaults = {'expiration_timeout': 60, 'security_level': 0,
|
|
2822
|
+
'session_fault_tolerance_level': 0, 'load_balancing_mode': 'performance',
|
|
2823
|
+
'kill_problem_processes': True, 'kill_by_memory_with_dump': False,
|
|
2824
|
+
'restart_schedule': '', 'ping_period': 0, 'ping_timeout': 0}
|
|
2825
|
+
requested = {'expiration_timeout': expiration_timeout, 'security_level': security_level,
|
|
2826
|
+
'session_fault_tolerance_level': session_fault_tolerance_level,
|
|
2827
|
+
'load_balancing_mode': load_balancing_mode,
|
|
2828
|
+
'kill_problem_processes': kill_problem_processes,
|
|
2829
|
+
'kill_by_memory_with_dump': kill_by_memory_with_dump,
|
|
2830
|
+
'restart_schedule': restart_schedule, 'ping_period': ping_period,
|
|
2831
|
+
'ping_timeout': ping_timeout}
|
|
2832
|
+
options = {key: value for key, value in requested.items() if value != defaults[key]}
|
|
2833
|
+
try:
|
|
2834
|
+
async with service.factory() as client:
|
|
2835
|
+
return await client.insert_cluster(host, port, name, audit=audit, **options)
|
|
2836
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2837
|
+
raise ValueError('RAC cluster insert failed (' + type(exc).__name__ + ')') from None
|
|
2838
|
+
|
|
2839
|
+
if allow_cluster_remove:
|
|
2840
|
+
@server.tool(description='DESTRUCTIVE: remove a cluster from the configured RAS; '
|
|
2841
|
+
'confirm its exact UUID. The client checks the full cluster list before and after.')
|
|
2842
|
+
async def rac_remove_cluster(cluster_id: StrictStr, confirm_cluster_id: StrictStr) -> list[dict]:
|
|
2843
|
+
cluster = UUID(cluster_id)
|
|
2844
|
+
if confirm_cluster_id != str(cluster):
|
|
2845
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2846
|
+
try:
|
|
2847
|
+
async with service.factory() as client:
|
|
2848
|
+
return await client.remove_cluster(cluster)
|
|
2849
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2850
|
+
raise ValueError('RAC cluster remove failed (' + type(exc).__name__ + ')') from None
|
|
2851
|
+
|
|
2852
|
+
if allow_process_turn_off:
|
|
2853
|
+
@server.tool(description='DESTRUCTIVE: turn off an existing process; confirm with its exact UUID. The client verifies disappearance.')
|
|
2854
|
+
async def rac_turn_off_process(cluster_id: StrictStr, process_id: StrictStr,
|
|
2855
|
+
confirm_process_id: StrictStr) -> list[dict]:
|
|
2856
|
+
cluster, process = UUID(cluster_id), UUID(process_id)
|
|
2857
|
+
if confirm_process_id != str(process):
|
|
2858
|
+
raise ValueError('Confirmation must match the process UUID exactly')
|
|
2859
|
+
async with service.factory() as client:
|
|
2860
|
+
try:
|
|
2861
|
+
return await client.turn_off_process(cluster, process)
|
|
2862
|
+
except (RacError, ValueError, OSError, TimeoutError) as exc:
|
|
2863
|
+
raise ValueError('RAC process turn-off failed (' + type(exc).__name__ + ')') from None
|
|
2864
|
+
|
|
2865
|
+
if allow_cluster_name_update:
|
|
2866
|
+
@server.tool(description='MUTATION: change an existing cluster name; confirm with its exact UUID. The client verifies full read-back.')
|
|
2867
|
+
async def rac_update_cluster_name(cluster_id: StrictStr, name: StrictStr,
|
|
2868
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2869
|
+
cluster = UUID(cluster_id)
|
|
2870
|
+
if confirm_cluster_id != str(cluster):
|
|
2871
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2872
|
+
async with service.factory() as client:
|
|
2873
|
+
try:
|
|
2874
|
+
return await client.update_cluster_name(cluster, name)
|
|
2875
|
+
except (RacError, ValueError, OSError, TimeoutError) as exc:
|
|
2876
|
+
raise ValueError('RAC cluster name update failed (' + type(exc).__name__ + ')') from None
|
|
2877
|
+
|
|
2878
|
+
if allow_cluster_expiration_timeout_update:
|
|
2879
|
+
@server.tool(description='MUTATION: change cluster expiration timeout in seconds; confirm its exact UUID. Full read-back is verified.')
|
|
2880
|
+
async def rac_update_cluster_expiration_timeout(cluster_id: StrictStr, seconds: StrictInt,
|
|
2881
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2882
|
+
cluster = UUID(cluster_id)
|
|
2883
|
+
if confirm_cluster_id != str(cluster):
|
|
2884
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2885
|
+
if type(seconds) is not int or not 0 <= seconds <= 2_147_483_647:
|
|
2886
|
+
raise ValueError('Invalid cluster expiration timeout')
|
|
2887
|
+
try:
|
|
2888
|
+
async with service.factory() as client:
|
|
2889
|
+
return await client.update_cluster_expiration_timeout(cluster, seconds)
|
|
2890
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2891
|
+
raise ValueError('RAC cluster timeout update failed (' + type(exc).__name__ + ')') from None
|
|
2892
|
+
|
|
2893
|
+
if allow_cluster_security_level_update:
|
|
2894
|
+
@server.tool(description='MUTATION: change cluster security level; confirm its exact UUID. Full read-back is verified.')
|
|
2895
|
+
async def rac_update_cluster_security_level(cluster_id: StrictStr, level: StrictInt,
|
|
2896
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2897
|
+
cluster = UUID(cluster_id)
|
|
2898
|
+
if confirm_cluster_id != str(cluster):
|
|
2899
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2900
|
+
if type(level) is not int or not 0 <= level <= 2_147_483_647:
|
|
2901
|
+
raise ValueError('Invalid cluster security level')
|
|
2902
|
+
try:
|
|
2903
|
+
async with service.factory() as client:
|
|
2904
|
+
return await client.update_cluster_security_level(cluster, level)
|
|
2905
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2906
|
+
raise ValueError('RAC cluster security level update failed (' + type(exc).__name__ + ')') from None
|
|
2907
|
+
|
|
2908
|
+
if allow_cluster_session_fault_tolerance_level_update:
|
|
2909
|
+
@server.tool(description='MUTATION: change cluster session fault tolerance level; confirm its exact UUID. Full read-back is verified.')
|
|
2910
|
+
async def rac_update_cluster_session_fault_tolerance_level(cluster_id: StrictStr, level: StrictInt,
|
|
2911
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2912
|
+
cluster = UUID(cluster_id)
|
|
2913
|
+
if confirm_cluster_id != str(cluster):
|
|
2914
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2915
|
+
if type(level) is not int or not 0 <= level <= 2_147_483_647:
|
|
2916
|
+
raise ValueError('Invalid cluster session fault tolerance level')
|
|
2917
|
+
try:
|
|
2918
|
+
async with service.factory() as client:
|
|
2919
|
+
return await client.update_cluster_session_fault_tolerance_level(cluster, level)
|
|
2920
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2921
|
+
raise ValueError('RAC cluster session fault tolerance update failed (' + type(exc).__name__ + ')') from None
|
|
2922
|
+
|
|
2923
|
+
if allow_cluster_load_balancing_mode_update:
|
|
2924
|
+
@server.tool(description='MUTATION: set cluster load balancing to performance or memory; exact UUID confirmation and full read-back required.')
|
|
2925
|
+
async def rac_update_cluster_load_balancing_mode(cluster_id: StrictStr, mode: StrictStr,
|
|
2926
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2927
|
+
cluster = UUID(cluster_id)
|
|
2928
|
+
if confirm_cluster_id != str(cluster):
|
|
2929
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2930
|
+
if type(mode) is not str or mode not in ('performance', 'memory'):
|
|
2931
|
+
raise ValueError('Invalid cluster load balancing mode')
|
|
2932
|
+
try:
|
|
2933
|
+
async with service.factory() as client:
|
|
2934
|
+
return await client.update_cluster_load_balancing_mode(cluster, mode)
|
|
2935
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2936
|
+
raise ValueError('RAC cluster load balancing update failed (' + type(exc).__name__ + ')') from None
|
|
2937
|
+
|
|
2938
|
+
if allow_cluster_kill_problem_processes_update:
|
|
2939
|
+
@server.tool(description='MUTATION: change cluster automatic problem-process termination; confirm exact UUID. Full read-back required.')
|
|
2940
|
+
async def rac_update_cluster_kill_problem_processes(cluster_id: StrictStr, enabled: StrictBool,
|
|
2941
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2942
|
+
cluster = UUID(cluster_id)
|
|
2943
|
+
if confirm_cluster_id != str(cluster):
|
|
2944
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2945
|
+
if type(enabled) is not bool:
|
|
2946
|
+
raise ValueError('Invalid kill problem processes flag')
|
|
2947
|
+
try:
|
|
2948
|
+
async with service.factory() as client:
|
|
2949
|
+
return await client.update_cluster_kill_problem_processes(cluster, enabled)
|
|
2950
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2951
|
+
raise ValueError('RAC cluster problem-process policy update failed (' + type(exc).__name__ + ')') from None
|
|
2952
|
+
|
|
2953
|
+
if allow_cluster_kill_by_memory_with_dump_update:
|
|
2954
|
+
@server.tool(description='MUTATION: change cluster kill-by-memory dump policy; confirm exact UUID. Full read-back required.')
|
|
2955
|
+
async def rac_update_cluster_kill_by_memory_with_dump(cluster_id: StrictStr, enabled: StrictBool,
|
|
2956
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2957
|
+
cluster = UUID(cluster_id)
|
|
2958
|
+
if confirm_cluster_id != str(cluster):
|
|
2959
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2960
|
+
if type(enabled) is not bool:
|
|
2961
|
+
raise ValueError('Invalid kill by memory dump flag')
|
|
2962
|
+
try:
|
|
2963
|
+
async with service.factory() as client:
|
|
2964
|
+
return await client.update_cluster_kill_by_memory_with_dump(cluster, enabled)
|
|
2965
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2966
|
+
raise ValueError('RAC cluster kill-by-memory dump update failed (' + type(exc).__name__ + ')') from None
|
|
2967
|
+
|
|
2968
|
+
if allow_cluster_audit_update:
|
|
2969
|
+
@server.tool(description='MUTATION: change cluster access-right audit recording; confirm exact UUID. Full read-back required.')
|
|
2970
|
+
async def rac_update_cluster_audit(cluster_id: StrictStr, enabled: StrictBool,
|
|
2971
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2972
|
+
cluster = UUID(cluster_id)
|
|
2973
|
+
if confirm_cluster_id != str(cluster):
|
|
2974
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2975
|
+
if type(enabled) is not bool:
|
|
2976
|
+
raise ValueError('Invalid cluster audit flag')
|
|
2977
|
+
try:
|
|
2978
|
+
async with service.factory() as client:
|
|
2979
|
+
return await client.update_cluster_audit(cluster, enabled)
|
|
2980
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2981
|
+
raise ValueError('RAC cluster audit update failed (' + type(exc).__name__ + ')') from None
|
|
2982
|
+
|
|
2983
|
+
if allow_cluster_ping_update:
|
|
2984
|
+
@server.tool(description='MUTATION: update cluster ping period and timeout together; confirm exact UUID. Full read-back required.')
|
|
2985
|
+
async def rac_update_cluster_ping(cluster_id: StrictStr, period: StrictInt, timeout: StrictInt,
|
|
2986
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
2987
|
+
cluster = UUID(cluster_id)
|
|
2988
|
+
if confirm_cluster_id != str(cluster):
|
|
2989
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
2990
|
+
if (type(period) is not int or type(timeout) is not int or
|
|
2991
|
+
not ((period == 0 and timeout == 0) or (0 < period < timeout <= 2_147_483_647))):
|
|
2992
|
+
raise ValueError('Invalid cluster ping period/timeout pair')
|
|
2993
|
+
try:
|
|
2994
|
+
async with service.factory() as client:
|
|
2995
|
+
return await client.update_cluster_ping(cluster, period, timeout)
|
|
2996
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
2997
|
+
raise ValueError('RAC cluster ping update failed (' + type(exc).__name__ + ')') from None
|
|
2998
|
+
|
|
2999
|
+
if allow_cluster_restart_schedule_update:
|
|
3000
|
+
@server.tool(description='MUTATION: update cluster restart schedule; confirm exact UUID. Full read-back required.')
|
|
3001
|
+
async def rac_update_cluster_restart_schedule(cluster_id: StrictStr, schedule: StrictStr,
|
|
3002
|
+
confirm_cluster_id: StrictStr) -> dict:
|
|
3003
|
+
cluster = UUID(cluster_id)
|
|
3004
|
+
if confirm_cluster_id != str(cluster):
|
|
3005
|
+
raise ValueError('Confirmation must match the cluster UUID exactly')
|
|
3006
|
+
RacClient._validate_schedule(schedule)
|
|
3007
|
+
try:
|
|
3008
|
+
async with service.factory() as client:
|
|
3009
|
+
return await client.update_restart_schedule(cluster, schedule)
|
|
3010
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3011
|
+
raise ValueError('RAC cluster restart schedule update failed (' + type(exc).__name__ + ')') from None
|
|
3012
|
+
|
|
3013
|
+
if allow_server_restart_schedule_update:
|
|
3014
|
+
@server.tool(description='MUTATION: update working-server restart schedule; confirm exact server UUID. Full read-back required.')
|
|
3015
|
+
async def rac_update_server_restart_schedule(cluster_id: StrictStr, server_id: StrictStr,
|
|
3016
|
+
schedule: StrictStr, confirm_server_id: StrictStr) -> dict:
|
|
3017
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3018
|
+
if confirm_server_id != str(working_server):
|
|
3019
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3020
|
+
RacClient._validate_schedule(schedule)
|
|
3021
|
+
try:
|
|
3022
|
+
async with service.factory() as client:
|
|
3023
|
+
return await client.update_server_restart_schedule(cluster, working_server, schedule)
|
|
3024
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3025
|
+
raise ValueError('RAC server restart schedule update failed (' + type(exc).__name__ + ')') from None
|
|
3026
|
+
|
|
3027
|
+
if allow_server_infobases_limit_update:
|
|
3028
|
+
@server.tool(description='MUTATION: update working-server infobases-per-process limit; confirm exact server UUID. Full read-back required.')
|
|
3029
|
+
async def rac_update_server_infobases_limit(cluster_id: StrictStr, server_id: StrictStr,
|
|
3030
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3031
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3032
|
+
if confirm_server_id != str(working_server):
|
|
3033
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3034
|
+
if type(limit) is not int or not 0 <= limit <= 2_147_483_647:
|
|
3035
|
+
raise ValueError('Invalid working-server infobases limit')
|
|
3036
|
+
try:
|
|
3037
|
+
async with service.factory() as client:
|
|
3038
|
+
return await client.update_server_infobases_limit(cluster, working_server, limit)
|
|
3039
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3040
|
+
raise ValueError('RAC server infobases limit update failed (' + type(exc).__name__ + ')') from None
|
|
3041
|
+
|
|
3042
|
+
if allow_server_connections_limit_update:
|
|
3043
|
+
@server.tool(description='MUTATION: update working-server connections-per-process limit; confirm exact server UUID. Full read-back required.')
|
|
3044
|
+
async def rac_update_server_connections_limit(cluster_id: StrictStr, server_id: StrictStr,
|
|
3045
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3046
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3047
|
+
if confirm_server_id != str(working_server):
|
|
3048
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3049
|
+
if type(limit) is not int or not 0 <= limit <= 2_147_483_647:
|
|
3050
|
+
raise ValueError('Invalid working-server connections limit')
|
|
3051
|
+
try:
|
|
3052
|
+
async with service.factory() as client:
|
|
3053
|
+
return await client.update_server_connections_limit(cluster, working_server, limit)
|
|
3054
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3055
|
+
raise ValueError('RAC server connections limit update failed (' + type(exc).__name__ + ')') from None
|
|
3056
|
+
|
|
3057
|
+
if allow_server_safe_call_memory_limit_update:
|
|
3058
|
+
@server.tool(description='MUTATION: update working-server safe-call memory limit; confirm exact server UUID. Full read-back required.')
|
|
3059
|
+
async def rac_update_server_safe_call_memory_limit(cluster_id: StrictStr, server_id: StrictStr,
|
|
3060
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3061
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3062
|
+
if confirm_server_id != str(working_server):
|
|
3063
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3064
|
+
if type(limit) is not int or not 0 <= limit < 2**63:
|
|
3065
|
+
raise ValueError('Invalid working-server safe-call memory limit')
|
|
3066
|
+
try:
|
|
3067
|
+
async with service.factory() as client:
|
|
3068
|
+
return await client.update_server_safe_call_memory_limit(cluster, working_server, limit)
|
|
3069
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3070
|
+
raise ValueError('RAC server safe-call memory limit update failed (' + type(exc).__name__ + ')') from None
|
|
3071
|
+
|
|
3072
|
+
if allow_server_critical_total_memory_update:
|
|
3073
|
+
@server.tool(description='MUTATION: update working-server critical total memory; confirm exact server UUID. Full read-back required.')
|
|
3074
|
+
async def rac_update_server_critical_total_memory(cluster_id: StrictStr, server_id: StrictStr,
|
|
3075
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3076
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3077
|
+
if confirm_server_id != str(working_server):
|
|
3078
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3079
|
+
if type(limit) is not int or not 0 <= limit < 2**63:
|
|
3080
|
+
raise ValueError('Invalid working-server critical total memory')
|
|
3081
|
+
try:
|
|
3082
|
+
async with service.factory() as client:
|
|
3083
|
+
return await client.update_server_critical_total_memory(cluster, working_server, limit)
|
|
3084
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3085
|
+
raise ValueError('RAC server critical total memory update failed (' + type(exc).__name__ + ')') from None
|
|
3086
|
+
|
|
3087
|
+
if allow_server_temporary_total_memory_update:
|
|
3088
|
+
@server.tool(description='MUTATION: update working-server temporary allowed total memory; confirm exact server UUID. Full read-back required.')
|
|
3089
|
+
async def rac_update_server_temporary_allowed_total_memory(cluster_id: StrictStr, server_id: StrictStr,
|
|
3090
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3091
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3092
|
+
if confirm_server_id != str(working_server):
|
|
3093
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3094
|
+
if type(limit) is not int or not 0 <= limit < 2**63:
|
|
3095
|
+
raise ValueError('Invalid working-server temporary allowed total memory')
|
|
3096
|
+
try:
|
|
3097
|
+
async with service.factory() as client:
|
|
3098
|
+
return await client.update_server_temporary_allowed_total_memory(cluster, working_server, limit)
|
|
3099
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3100
|
+
raise ValueError('RAC server temporary allowed total memory update failed (' + type(exc).__name__ + ')') from None
|
|
3101
|
+
|
|
3102
|
+
if allow_server_temporary_memory_time_limit_update:
|
|
3103
|
+
@server.tool(description='MUTATION: update working-server temporary allowed total memory time limit; confirm exact server UUID. Full read-back required.')
|
|
3104
|
+
async def rac_update_server_temporary_allowed_total_memory_time_limit(cluster_id: StrictStr, server_id: StrictStr,
|
|
3105
|
+
limit: StrictInt, confirm_server_id: StrictStr) -> dict:
|
|
3106
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3107
|
+
if confirm_server_id != str(working_server):
|
|
3108
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3109
|
+
if type(limit) is not int or not 0 <= limit < 2**63:
|
|
3110
|
+
raise ValueError('Invalid working-server temporary memory time limit')
|
|
3111
|
+
try:
|
|
3112
|
+
async with service.factory() as client:
|
|
3113
|
+
return await client.update_server_temporary_allowed_total_memory_time_limit(cluster, working_server, limit)
|
|
3114
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3115
|
+
raise ValueError('RAC server temporary memory time limit update failed (' + type(exc).__name__ + ')') from None
|
|
3116
|
+
|
|
3117
|
+
if allow_server_spn_update:
|
|
3118
|
+
@server.tool(description='MUTATION: update or clear working-server service principal name; confirm exact server UUID. Full read-back required.')
|
|
3119
|
+
async def rac_update_server_service_principal_name(cluster_id: StrictStr, server_id: StrictStr,
|
|
3120
|
+
name: StrictStr, confirm_server_id: StrictStr) -> dict:
|
|
3121
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3122
|
+
if confirm_server_id != str(working_server):
|
|
3123
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3124
|
+
if not isinstance(name, str) or '\x00' in name or len(name.encode('utf-8')) > 256:
|
|
3125
|
+
raise ValueError('Invalid working-server service principal name')
|
|
3126
|
+
try:
|
|
3127
|
+
async with service.factory() as client:
|
|
3128
|
+
return await client.update_server_service_principal_name(cluster, working_server, name)
|
|
3129
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3130
|
+
raise ValueError('RAC server service principal name update failed (' + type(exc).__name__ + ')') from None
|
|
3131
|
+
|
|
3132
|
+
if allow_server_dedicate_managers_update:
|
|
3133
|
+
@server.tool(description='MUTATION: set working-server dedicated managers to all or none; confirm exact server UUID. Full read-back required.')
|
|
3134
|
+
async def rac_update_server_dedicate_managers(cluster_id: StrictStr, server_id: StrictStr,
|
|
3135
|
+
choice: StrictStr, confirm_server_id: StrictStr) -> dict:
|
|
3136
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3137
|
+
if confirm_server_id != str(working_server):
|
|
3138
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3139
|
+
if type(choice) is not str or choice not in ('all', 'none'):
|
|
3140
|
+
raise ValueError('Invalid working-server dedicated managers choice')
|
|
3141
|
+
try:
|
|
3142
|
+
async with service.factory() as client:
|
|
3143
|
+
return await client.update_server_dedicate_managers(cluster, working_server, choice)
|
|
3144
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3145
|
+
raise ValueError('RAC server dedicated managers update failed (' + type(exc).__name__ + ')') from None
|
|
3146
|
+
|
|
3147
|
+
if allow_server_port_ranges_update:
|
|
3148
|
+
@server.tool(description='MUTATION: update working-server port ranges; confirm exact server UUID. Full read-back required.')
|
|
3149
|
+
async def rac_update_server_port_ranges(cluster_id: StrictStr, server_id: StrictStr,
|
|
3150
|
+
ranges: list[list[StrictInt]],
|
|
3151
|
+
confirm_server_id: StrictStr) -> dict:
|
|
3152
|
+
cluster, working_server = UUID(cluster_id), UUID(server_id)
|
|
3153
|
+
if confirm_server_id != str(working_server):
|
|
3154
|
+
raise ValueError('Confirmation must match the working server UUID exactly')
|
|
3155
|
+
if (type(ranges) is not list or not 1 <= len(ranges) <= 64 or
|
|
3156
|
+
any(type(pair) is not list or len(pair) != 2 or
|
|
3157
|
+
any(type(port) is not int or not 1 <= port <= 65535 for port in pair) or
|
|
3158
|
+
pair[0] > pair[1] for pair in ranges)):
|
|
3159
|
+
raise ValueError('Invalid working-server port ranges')
|
|
3160
|
+
ordered = sorted(ranges)
|
|
3161
|
+
if any(left[1] >= right[0] for left, right in zip(ordered, ordered[1:])):
|
|
3162
|
+
raise ValueError('Overlapping working-server port ranges')
|
|
3163
|
+
try:
|
|
3164
|
+
async with service.factory() as client:
|
|
3165
|
+
return await client.update_server_port_ranges(cluster, working_server,
|
|
3166
|
+
[tuple(pair) for pair in ranges])
|
|
3167
|
+
except (RacError, ValueError, OSError, TimeoutError, RuntimeError) as exc:
|
|
3168
|
+
raise ValueError('RAC server port ranges update failed (' + type(exc).__name__ + ')') from None
|
|
3169
|
+
|
|
3170
|
+
return server
|
|
3171
|
+
|
|
3172
|
+
|
|
3173
|
+
def main(argv=None):
|
|
3174
|
+
parser = argparse.ArgumentParser(description='RAC MCP server (read-only by default)')
|
|
3175
|
+
parser.add_argument('--ras-host', default='127.0.0.1')
|
|
3176
|
+
parser.add_argument('--ras-port', type=int, default=1545)
|
|
3177
|
+
parser.add_argument('--ras-server', action='append', default=[], metavar='HOST:PORT/PROTOCOL',
|
|
3178
|
+
help='Additional read-only RAS for cross-server search (repeatable)')
|
|
3179
|
+
parser.add_argument('--protocol', choices=['10.0', '16.0', 'auto'], default='16.0')
|
|
3180
|
+
parser.add_argument('--agent-user', help='OS-authenticated agent administrator name')
|
|
3181
|
+
parser.add_argument('--cluster-user', help='OS-authenticated cluster administrator name')
|
|
3182
|
+
parser.add_argument('--allow-process-turn-off', action='store_true',
|
|
3183
|
+
help='Enable destructive process turn-off MCP tool (disabled by default)')
|
|
3184
|
+
parser.add_argument('--allow-cluster-insert', action='store_true',
|
|
3185
|
+
help='Enable guarded cluster insertion MCP tool (disabled by default)')
|
|
3186
|
+
parser.add_argument('--allow-cluster-remove', action='store_true',
|
|
3187
|
+
help='Enable destructive cluster removal MCP tool (disabled by default)')
|
|
3188
|
+
parser.add_argument('--allow-agent-admin-register', action='store_true',
|
|
3189
|
+
help='Enable guarded password-free agent administrator registration (disabled by default)')
|
|
3190
|
+
parser.add_argument('--allow-agent-admin-remove', action='store_true',
|
|
3191
|
+
help='Enable destructive agent administrator removal (disabled by default)')
|
|
3192
|
+
parser.add_argument('--allow-cluster-admin-register', action='store_true',
|
|
3193
|
+
help='Enable guarded password-free cluster administrator registration (disabled by default)')
|
|
3194
|
+
parser.add_argument('--allow-cluster-admin-remove', action='store_true',
|
|
3195
|
+
help='Enable destructive cluster administrator removal (disabled by default)')
|
|
3196
|
+
parser.add_argument('--allow-rule-insert', action='store_true',
|
|
3197
|
+
help='Enable guarded assignment rule insertion (disabled by default)')
|
|
3198
|
+
parser.add_argument('--allow-rule-apply', action='store_true',
|
|
3199
|
+
help='Enable guarded assignment rule apply (disabled by default)')
|
|
3200
|
+
parser.add_argument('--allow-service-settings-apply', action='store_true',
|
|
3201
|
+
help='Enable guarded pending service settings apply (disabled by default)')
|
|
3202
|
+
parser.add_argument('--allow-service-setting-remove', action='store_true',
|
|
3203
|
+
help='Enable destructive service setting removal (disabled by default)')
|
|
3204
|
+
parser.add_argument('--allow-service-setting-update', action='store_true',
|
|
3205
|
+
help='Enable guarded service setting directory update (disabled by default)')
|
|
3206
|
+
parser.add_argument('--allow-service-setting-insert', action='store_true',
|
|
3207
|
+
help='Enable guarded service setting insertion (disabled by default)')
|
|
3208
|
+
parser.add_argument('--allow-profile-remove', action='store_true',
|
|
3209
|
+
help='Enable destructive security profile removal (disabled by default)')
|
|
3210
|
+
parser.add_argument('--allow-profile-description-update', action='store_true',
|
|
3211
|
+
help='Enable guarded profile description update (disabled by default)')
|
|
3212
|
+
parser.add_argument('--allow-profile-config-update', action='store_true',
|
|
3213
|
+
help='Enable guarded profile configuration access update (disabled by default)')
|
|
3214
|
+
parser.add_argument('--allow-profile-access-flags-update', action='store_true',
|
|
3215
|
+
help='Enable guarded profile access flags update (disabled by default)')
|
|
3216
|
+
parser.add_argument('--allow-profile-role-fields-update', action='store_true',
|
|
3217
|
+
help='Enable guarded profile role/module fields update (disabled by default)')
|
|
3218
|
+
parser.add_argument('--allow-profile-directory-remove', action='store_true',
|
|
3219
|
+
help='Enable destructive profile directory ACL removal (disabled by default)')
|
|
3220
|
+
parser.add_argument('--allow-profile-directory-update', action='store_true',
|
|
3221
|
+
help='Enable guarded profile directory ACL update (disabled by default)')
|
|
3222
|
+
parser.add_argument('--allow-profile-com-remove', action='store_true',
|
|
3223
|
+
help='Enable destructive profile COM-class ACL removal (disabled by default)')
|
|
3224
|
+
parser.add_argument('--allow-profile-com-update', action='store_true',
|
|
3225
|
+
help='Enable guarded profile COM-class ACL update (disabled by default)')
|
|
3226
|
+
parser.add_argument('--allow-profile-addin-remove', action='store_true',
|
|
3227
|
+
help='Enable destructive profile add-in ACL removal (disabled by default)')
|
|
3228
|
+
parser.add_argument('--allow-profile-addin-update', action='store_true',
|
|
3229
|
+
help='Enable guarded profile add-in ACL update (disabled by default)')
|
|
3230
|
+
parser.add_argument('--allow-profile-module-remove', action='store_true',
|
|
3231
|
+
help='Enable destructive profile module ACL removal (disabled by default)')
|
|
3232
|
+
parser.add_argument('--allow-profile-module-update', action='store_true',
|
|
3233
|
+
help='Enable guarded profile module ACL update (disabled by default)')
|
|
3234
|
+
parser.add_argument('--allow-profile-app-remove', action='store_true',
|
|
3235
|
+
help='Enable destructive profile application ACL removal (disabled by default)')
|
|
3236
|
+
parser.add_argument('--allow-profile-app-update', action='store_true',
|
|
3237
|
+
help='Enable guarded profile application ACL update (disabled by default)')
|
|
3238
|
+
parser.add_argument('--allow-profile-inet-remove', action='store_true',
|
|
3239
|
+
help='Enable destructive profile inet ACL removal (disabled by default)')
|
|
3240
|
+
parser.add_argument('--allow-profile-inet-update', action='store_true',
|
|
3241
|
+
help='Enable guarded profile inet ACL update (disabled by default)')
|
|
3242
|
+
parser.add_argument('--allow-infobase-security-profile-update', action='store_true',
|
|
3243
|
+
help='Enable guarded infobase security profile update (disabled by default)')
|
|
3244
|
+
parser.add_argument('--allow-infobase-safe-mode-security-profile-update', action='store_true',
|
|
3245
|
+
help='Enable guarded infobase safe-mode security profile update (disabled by default)')
|
|
3246
|
+
parser.add_argument('--allow-infobase-maximum-scheduled-jobs-shift-update', action='store_true',
|
|
3247
|
+
help='Enable guarded infobase scheduled-jobs shift update (disabled by default)')
|
|
3248
|
+
parser.add_argument('--allow-infobase-minimum-scheduled-jobs-period-update', action='store_true',
|
|
3249
|
+
help='Enable guarded infobase scheduled-jobs period update (disabled by default)')
|
|
3250
|
+
parser.add_argument('--allow-infobase-configuration-unload-delay-update', action='store_true',
|
|
3251
|
+
help='Enable guarded infobase configuration unload delay update (disabled by default)')
|
|
3252
|
+
parser.add_argument('--allow-infobase-local-speech-update', action='store_true',
|
|
3253
|
+
help='Enable guarded infobase local speech-to-text update (disabled by default)')
|
|
3254
|
+
parser.add_argument('--allow-infobase-external-session-manager-update', action='store_true',
|
|
3255
|
+
help='Enable guarded infobase external session manager update (disabled by default)')
|
|
3256
|
+
parser.add_argument('--allow-infobase-reserve-working-processes-update', action='store_true',
|
|
3257
|
+
help='Enable guarded infobase reserve working processes update (disabled by default)')
|
|
3258
|
+
parser.add_argument('--allow-infobase-denied-message-update', action='store_true',
|
|
3259
|
+
help='Enable guarded infobase denied message update (disabled by default)')
|
|
3260
|
+
parser.add_argument('--allow-infobase-license-distribution-update', action='store_true',
|
|
3261
|
+
help='Enable guarded infobase license distribution update (disabled by default)')
|
|
3262
|
+
parser.add_argument('--allow-infobase-sessions-deny-update', action='store_true',
|
|
3263
|
+
help='Enable guarded infobase sessions deny update (disabled by default)')
|
|
3264
|
+
parser.add_argument('--allow-infobase-scheduled-jobs-deny-update', action='store_true',
|
|
3265
|
+
help='Enable guarded infobase scheduled jobs deny update (disabled by default)')
|
|
3266
|
+
parser.add_argument('--allow-infobase-description-update', action='store_true',
|
|
3267
|
+
help='Enable guarded infobase description update (disabled by default)')
|
|
3268
|
+
parser.add_argument('--allow-infobase-summary-description-update', action='store_true',
|
|
3269
|
+
help='Enable guarded infobase summary description update (disabled by default)')
|
|
3270
|
+
parser.add_argument('--allow-infobase-registration-drop', action='store_true',
|
|
3271
|
+
help='Enable destructive infobase registration removal without database deletion (disabled by default)')
|
|
3272
|
+
parser.add_argument('--allow-infobase-existing-registration', action='store_true',
|
|
3273
|
+
help='Enable guarded registration of an existing PostgreSQL infobase (disabled by default)')
|
|
3274
|
+
parser.add_argument('--allow-infobase-database-create', action='store_true',
|
|
3275
|
+
help='Enable database creation and infobase registration (disabled by default)')
|
|
3276
|
+
parser.add_argument('--allow-rule-remove', action='store_true',
|
|
3277
|
+
help='Enable destructive assignment rule removal (disabled by default)')
|
|
3278
|
+
parser.add_argument('--allow-rule-priority-update', action='store_true',
|
|
3279
|
+
help='Enable guarded assignment rule priority update (disabled by default)')
|
|
3280
|
+
parser.add_argument('--allow-rule-fields-update', action='store_true',
|
|
3281
|
+
help='Enable guarded assignment rule fields update (disabled by default)')
|
|
3282
|
+
parser.add_argument('--allow-rule-position-update', action='store_true',
|
|
3283
|
+
help='Enable guarded assignment rule reorder (disabled by default)')
|
|
3284
|
+
parser.add_argument('--allow-limit-remove', action='store_true',
|
|
3285
|
+
help='Enable destructive named-limit removal (disabled by default)')
|
|
3286
|
+
parser.add_argument('--allow-limit-update', action='store_true',
|
|
3287
|
+
help='Enable guarded named-limit update (disabled by default)')
|
|
3288
|
+
parser.add_argument('--allow-counter-remove', action='store_true',
|
|
3289
|
+
help='Enable destructive named-counter removal (disabled by default)')
|
|
3290
|
+
parser.add_argument('--allow-counter-description-update', action='store_true',
|
|
3291
|
+
help='Enable guarded named-counter description update (disabled by default)')
|
|
3292
|
+
parser.add_argument('--allow-counter-collection-time-update', action='store_true',
|
|
3293
|
+
help='Enable guarded named-counter collection time update (disabled by default)')
|
|
3294
|
+
parser.add_argument('--allow-counter-duration-update', action='store_true',
|
|
3295
|
+
help='Enable guarded named-counter duration analysis update (disabled by default)')
|
|
3296
|
+
parser.add_argument('--allow-counter-group-update', action='store_true',
|
|
3297
|
+
help='Enable guarded named-counter group update (disabled by default)')
|
|
3298
|
+
parser.add_argument('--allow-counter-filter-type-update', action='store_true',
|
|
3299
|
+
help='Enable guarded named-counter filter type update (disabled by default)')
|
|
3300
|
+
parser.add_argument('--allow-counter-filter-update', action='store_true',
|
|
3301
|
+
help='Enable guarded named-counter object filter update (disabled by default)')
|
|
3302
|
+
parser.add_argument('--allow-counter-analysis-flags-update', action='store_true',
|
|
3303
|
+
help='Enable guarded named-counter analysis flags update (disabled by default)')
|
|
3304
|
+
parser.add_argument('--allow-counter-clear', action='store_true',
|
|
3305
|
+
help='Enable counter reading clear request (disabled by default; effect unverified)')
|
|
3306
|
+
parser.add_argument('--allow-cluster-name-update', action='store_true',
|
|
3307
|
+
help='Enable verified cluster name update MCP tool (disabled by default)')
|
|
3308
|
+
parser.add_argument('--allow-cluster-expiration-timeout-update', action='store_true',
|
|
3309
|
+
help='Enable guarded cluster expiration timeout update (disabled by default)')
|
|
3310
|
+
parser.add_argument('--allow-cluster-security-level-update', action='store_true',
|
|
3311
|
+
help='Enable guarded cluster security level update (disabled by default)')
|
|
3312
|
+
parser.add_argument('--allow-cluster-session-fault-tolerance-level-update', action='store_true',
|
|
3313
|
+
help='Enable guarded cluster session fault tolerance update (disabled by default)')
|
|
3314
|
+
parser.add_argument('--allow-cluster-load-balancing-mode-update', action='store_true',
|
|
3315
|
+
help='Enable guarded cluster load balancing mode update (disabled by default)')
|
|
3316
|
+
parser.add_argument('--allow-cluster-kill-problem-processes-update', action='store_true',
|
|
3317
|
+
help='Enable guarded cluster problem-process policy update (disabled by default)')
|
|
3318
|
+
parser.add_argument('--allow-cluster-kill-by-memory-with-dump-update', action='store_true',
|
|
3319
|
+
help='Enable guarded cluster kill-by-memory dump update (disabled by default)')
|
|
3320
|
+
parser.add_argument('--allow-cluster-audit-update', action='store_true',
|
|
3321
|
+
help='Enable guarded cluster access-right audit recording update (disabled by default)')
|
|
3322
|
+
parser.add_argument('--allow-cluster-ping-update', action='store_true',
|
|
3323
|
+
help='Enable guarded cluster ping pair update (disabled by default)')
|
|
3324
|
+
parser.add_argument('--allow-cluster-restart-schedule-update', action='store_true',
|
|
3325
|
+
help='Enable guarded cluster restart schedule update (disabled by default)')
|
|
3326
|
+
parser.add_argument('--allow-server-restart-schedule-update', action='store_true',
|
|
3327
|
+
help='Enable guarded working-server restart schedule update (disabled by default)')
|
|
3328
|
+
parser.add_argument('--allow-server-infobases-limit-update', action='store_true',
|
|
3329
|
+
help='Enable guarded working-server infobases limit update (disabled by default)')
|
|
3330
|
+
parser.add_argument('--allow-server-connections-limit-update', action='store_true',
|
|
3331
|
+
help='Enable guarded working-server connections limit update (disabled by default)')
|
|
3332
|
+
parser.add_argument('--allow-server-safe-call-memory-limit-update', action='store_true',
|
|
3333
|
+
help='Enable guarded working-server safe-call memory limit update (disabled by default)')
|
|
3334
|
+
parser.add_argument('--allow-server-critical-total-memory-update', action='store_true',
|
|
3335
|
+
help='Enable guarded working-server critical total memory update (disabled by default)')
|
|
3336
|
+
parser.add_argument('--allow-server-temporary-total-memory-update', action='store_true',
|
|
3337
|
+
help='Enable guarded working-server temporary allowed total memory update (disabled by default)')
|
|
3338
|
+
parser.add_argument('--allow-server-temporary-memory-time-limit-update', action='store_true',
|
|
3339
|
+
help='Enable guarded working-server temporary memory time limit update (disabled by default)')
|
|
3340
|
+
parser.add_argument('--allow-server-spn-update', action='store_true',
|
|
3341
|
+
help='Enable guarded working-server service principal name update (disabled by default)')
|
|
3342
|
+
parser.add_argument('--allow-server-dedicate-managers-update', action='store_true',
|
|
3343
|
+
help='Enable guarded working-server dedicated managers update (disabled by default)')
|
|
3344
|
+
parser.add_argument('--allow-server-port-ranges-update', action='store_true',
|
|
3345
|
+
help='Enable guarded working-server port ranges update (disabled by default)')
|
|
3346
|
+
args = parser.parse_args(argv)
|
|
3347
|
+
endpoints = startup_endpoints(args.ras_host, args.ras_port, args.protocol, args.ras_server)
|
|
3348
|
+
if args.ras_server and (args.agent_user is not None or args.cluster_user is not None or
|
|
3349
|
+
any(value for name, value in vars(args).items() if name.startswith('allow_'))):
|
|
3350
|
+
raise ValueError('Additional RAS endpoints require read-only unauthenticated mode')
|
|
3351
|
+
if args.allow_agent_admin_register and args.protocol != '16.0':
|
|
3352
|
+
raise ValueError('Agent admin register MCP tool requires protocol 16.0')
|
|
3353
|
+
if args.allow_agent_admin_remove and args.protocol != '16.0':
|
|
3354
|
+
raise ValueError('Agent admin remove MCP tool requires protocol 16.0')
|
|
3355
|
+
if args.allow_agent_admin_remove and args.agent_user is None:
|
|
3356
|
+
raise ValueError('Agent admin remove MCP tool requires an authenticated agent-user')
|
|
3357
|
+
if args.allow_cluster_admin_register and args.protocol != '16.0':
|
|
3358
|
+
raise ValueError('Cluster admin register MCP tool requires protocol 16.0')
|
|
3359
|
+
if args.allow_cluster_admin_remove and args.protocol != '16.0':
|
|
3360
|
+
raise ValueError('Cluster admin remove MCP tool requires protocol 16.0')
|
|
3361
|
+
if args.allow_cluster_admin_remove and args.cluster_user is None:
|
|
3362
|
+
raise ValueError('Cluster admin remove MCP tool requires an authenticated cluster-user')
|
|
3363
|
+
if args.allow_rule_insert and args.protocol != '16.0':
|
|
3364
|
+
raise ValueError('Rule insert MCP tool requires protocol 16.0')
|
|
3365
|
+
if args.allow_rule_apply and args.protocol != '16.0':
|
|
3366
|
+
raise ValueError('Rule apply MCP tool requires protocol 16.0')
|
|
3367
|
+
if args.allow_rule_apply and args.cluster_user is not None:
|
|
3368
|
+
raise ValueError('OS-authenticated rule apply MCP tool is not enabled')
|
|
3369
|
+
if args.allow_service_settings_apply and args.protocol != '16.0':
|
|
3370
|
+
raise ValueError('Service settings apply MCP tool requires protocol 16.0')
|
|
3371
|
+
if args.allow_service_settings_apply and args.cluster_user is not None:
|
|
3372
|
+
raise ValueError('OS-authenticated service settings apply MCP tool is not enabled')
|
|
3373
|
+
if args.allow_service_setting_remove and args.protocol != '16.0':
|
|
3374
|
+
raise ValueError('Service setting remove MCP tool requires protocol 16.0')
|
|
3375
|
+
if args.allow_service_setting_remove and args.cluster_user is not None:
|
|
3376
|
+
raise ValueError('OS-authenticated service setting remove MCP tool is not enabled')
|
|
3377
|
+
if args.allow_service_setting_update and args.protocol != '16.0':
|
|
3378
|
+
raise ValueError('Service setting update MCP tool requires protocol 16.0')
|
|
3379
|
+
if args.allow_service_setting_update and args.cluster_user is not None:
|
|
3380
|
+
raise ValueError('OS-authenticated service setting update MCP tool is not enabled')
|
|
3381
|
+
if args.allow_service_setting_insert and args.protocol != '16.0':
|
|
3382
|
+
raise ValueError('Service setting insert MCP tool requires protocol 16.0')
|
|
3383
|
+
if args.allow_service_setting_insert and args.cluster_user is not None:
|
|
3384
|
+
raise ValueError('OS-authenticated service setting insert MCP tool is not enabled')
|
|
3385
|
+
if args.allow_profile_remove and args.protocol != '16.0':
|
|
3386
|
+
raise ValueError('Profile remove MCP tool requires protocol 16.0')
|
|
3387
|
+
if args.allow_profile_remove and args.cluster_user is not None:
|
|
3388
|
+
raise ValueError('OS-authenticated profile remove MCP tool is not enabled')
|
|
3389
|
+
if args.allow_profile_description_update and args.protocol != '16.0':
|
|
3390
|
+
raise ValueError('Profile description update MCP tool requires protocol 16.0')
|
|
3391
|
+
if args.allow_profile_description_update and args.cluster_user is not None:
|
|
3392
|
+
raise ValueError('OS-authenticated profile description update MCP tool is not enabled')
|
|
3393
|
+
if args.allow_profile_config_update and args.protocol != '16.0':
|
|
3394
|
+
raise ValueError('Profile config update MCP tool requires protocol 16.0')
|
|
3395
|
+
if args.allow_profile_config_update and args.cluster_user is not None:
|
|
3396
|
+
raise ValueError('OS-authenticated profile config update MCP tool is not enabled')
|
|
3397
|
+
if args.allow_profile_access_flags_update and args.protocol != '16.0':
|
|
3398
|
+
raise ValueError('Profile access flags update MCP tool requires protocol 16.0')
|
|
3399
|
+
if args.allow_profile_access_flags_update and args.cluster_user is not None:
|
|
3400
|
+
raise ValueError('OS-authenticated profile access flags update MCP tool is not enabled')
|
|
3401
|
+
if args.allow_profile_role_fields_update and args.protocol != '16.0':
|
|
3402
|
+
raise ValueError('Profile role fields update MCP tool requires protocol 16.0')
|
|
3403
|
+
if args.allow_profile_role_fields_update and args.cluster_user is not None:
|
|
3404
|
+
raise ValueError('OS-authenticated profile role fields update MCP tool is not enabled')
|
|
3405
|
+
if args.allow_profile_directory_remove and args.protocol != '16.0':
|
|
3406
|
+
raise ValueError('Profile directory remove MCP tool requires protocol 16.0')
|
|
3407
|
+
if args.allow_profile_directory_remove and args.cluster_user is not None:
|
|
3408
|
+
raise ValueError('OS-authenticated profile directory remove MCP tool is not enabled')
|
|
3409
|
+
if args.allow_profile_directory_update and args.protocol != '16.0':
|
|
3410
|
+
raise ValueError('Profile directory update MCP tool requires protocol 16.0')
|
|
3411
|
+
if args.allow_profile_directory_update and args.cluster_user is not None:
|
|
3412
|
+
raise ValueError('OS-authenticated profile directory update MCP tool is not enabled')
|
|
3413
|
+
if args.allow_profile_com_remove and args.protocol != '16.0':
|
|
3414
|
+
raise ValueError('Profile COM remove MCP tool requires protocol 16.0')
|
|
3415
|
+
if args.allow_profile_com_remove and args.cluster_user is not None:
|
|
3416
|
+
raise ValueError('OS-authenticated profile COM remove MCP tool is not enabled')
|
|
3417
|
+
if args.allow_profile_com_update and args.protocol != '16.0':
|
|
3418
|
+
raise ValueError('Profile COM update MCP tool requires protocol 16.0')
|
|
3419
|
+
if args.allow_profile_com_update and args.cluster_user is not None:
|
|
3420
|
+
raise ValueError('OS-authenticated profile COM update MCP tool is not enabled')
|
|
3421
|
+
if args.allow_profile_addin_remove and args.protocol != '16.0':
|
|
3422
|
+
raise ValueError('Profile addin remove MCP tool requires protocol 16.0')
|
|
3423
|
+
if args.allow_profile_addin_remove and args.cluster_user is not None:
|
|
3424
|
+
raise ValueError('OS-authenticated profile addin remove MCP tool is not enabled')
|
|
3425
|
+
if args.allow_profile_addin_update and args.protocol != '16.0':
|
|
3426
|
+
raise ValueError('Profile addin update MCP tool requires protocol 16.0')
|
|
3427
|
+
if args.allow_profile_addin_update and args.cluster_user is not None:
|
|
3428
|
+
raise ValueError('OS-authenticated profile addin update MCP tool is not enabled')
|
|
3429
|
+
if args.allow_profile_module_remove and args.protocol != '16.0':
|
|
3430
|
+
raise ValueError('Profile module remove MCP tool requires protocol 16.0')
|
|
3431
|
+
if args.allow_profile_module_remove and args.cluster_user is not None:
|
|
3432
|
+
raise ValueError('OS-authenticated profile module remove MCP tool is not enabled')
|
|
3433
|
+
if args.allow_profile_module_update and args.protocol != '16.0':
|
|
3434
|
+
raise ValueError('Profile module update MCP tool requires protocol 16.0')
|
|
3435
|
+
if args.allow_profile_module_update and args.cluster_user is not None:
|
|
3436
|
+
raise ValueError('OS-authenticated profile module update MCP tool is not enabled')
|
|
3437
|
+
if args.allow_profile_app_remove and args.protocol != '16.0':
|
|
3438
|
+
raise ValueError('Profile app remove MCP tool requires protocol 16.0')
|
|
3439
|
+
if args.allow_profile_app_remove and args.cluster_user is not None:
|
|
3440
|
+
raise ValueError('OS-authenticated profile app remove MCP tool is not enabled')
|
|
3441
|
+
if args.allow_profile_app_update and args.protocol != '16.0':
|
|
3442
|
+
raise ValueError('Profile app update MCP tool requires protocol 16.0')
|
|
3443
|
+
if args.allow_profile_app_update and args.cluster_user is not None:
|
|
3444
|
+
raise ValueError('OS-authenticated profile app update MCP tool is not enabled')
|
|
3445
|
+
if args.allow_profile_inet_remove and args.protocol != '16.0':
|
|
3446
|
+
raise ValueError('Profile inet remove MCP tool requires protocol 16.0')
|
|
3447
|
+
if args.allow_profile_inet_remove and args.cluster_user is not None:
|
|
3448
|
+
raise ValueError('OS-authenticated profile inet remove MCP tool is not enabled')
|
|
3449
|
+
if args.allow_profile_inet_update and args.protocol != '16.0':
|
|
3450
|
+
raise ValueError('Profile inet update MCP tool requires protocol 16.0')
|
|
3451
|
+
if args.allow_profile_inet_update and args.cluster_user is not None:
|
|
3452
|
+
raise ValueError('OS-authenticated profile inet update MCP tool is not enabled')
|
|
3453
|
+
if args.allow_infobase_security_profile_update and args.protocol != '16.0':
|
|
3454
|
+
raise ValueError('Infobase security profile update MCP tool requires protocol 16.0')
|
|
3455
|
+
if args.allow_infobase_security_profile_update and args.cluster_user is not None:
|
|
3456
|
+
raise ValueError('OS-authenticated infobase security profile update MCP tool is not enabled')
|
|
3457
|
+
if args.allow_infobase_safe_mode_security_profile_update and args.protocol != '16.0':
|
|
3458
|
+
raise ValueError('Infobase safe mode security profile update MCP tool requires protocol 16.0')
|
|
3459
|
+
if args.allow_infobase_safe_mode_security_profile_update and args.cluster_user is not None:
|
|
3460
|
+
raise ValueError('OS-authenticated infobase safe mode security profile update MCP tool is not enabled')
|
|
3461
|
+
if args.allow_infobase_maximum_scheduled_jobs_shift_update and args.protocol != '16.0':
|
|
3462
|
+
raise ValueError('Infobase maximum scheduled jobs shift update MCP tool requires protocol 16.0')
|
|
3463
|
+
if args.allow_infobase_maximum_scheduled_jobs_shift_update and args.cluster_user is not None:
|
|
3464
|
+
raise ValueError('OS-authenticated infobase maximum scheduled jobs shift update MCP tool is not enabled')
|
|
3465
|
+
if args.allow_infobase_minimum_scheduled_jobs_period_update and args.protocol != '16.0':
|
|
3466
|
+
raise ValueError('Infobase minimum scheduled jobs period update MCP tool requires protocol 16.0')
|
|
3467
|
+
if args.allow_infobase_minimum_scheduled_jobs_period_update and args.cluster_user is not None:
|
|
3468
|
+
raise ValueError('OS-authenticated infobase minimum scheduled jobs period update MCP tool is not enabled')
|
|
3469
|
+
if args.allow_infobase_configuration_unload_delay_update and args.protocol != '16.0':
|
|
3470
|
+
raise ValueError('Infobase configuration unload delay update MCP tool requires protocol 16.0')
|
|
3471
|
+
if args.allow_infobase_configuration_unload_delay_update and args.cluster_user is not None:
|
|
3472
|
+
raise ValueError('OS-authenticated infobase configuration unload delay update MCP tool is not enabled')
|
|
3473
|
+
if args.allow_infobase_local_speech_update and args.protocol != '16.0':
|
|
3474
|
+
raise ValueError('Infobase local speech-to-text update MCP tool requires protocol 16.0')
|
|
3475
|
+
if args.allow_infobase_local_speech_update and args.cluster_user is not None:
|
|
3476
|
+
raise ValueError('OS-authenticated infobase local speech-to-text update MCP tool is not enabled')
|
|
3477
|
+
if args.allow_infobase_external_session_manager_update and args.protocol != '16.0':
|
|
3478
|
+
raise ValueError('Infobase external session manager update MCP tool requires protocol 16.0')
|
|
3479
|
+
if args.allow_infobase_external_session_manager_update and args.cluster_user is not None:
|
|
3480
|
+
raise ValueError('OS-authenticated infobase external session manager update MCP tool is not enabled')
|
|
3481
|
+
if args.allow_infobase_reserve_working_processes_update and args.protocol != '16.0':
|
|
3482
|
+
raise ValueError('Infobase reserve working processes update MCP tool requires protocol 16.0')
|
|
3483
|
+
if args.allow_infobase_reserve_working_processes_update and args.cluster_user is not None:
|
|
3484
|
+
raise ValueError('OS-authenticated infobase reserve working processes update MCP tool is not enabled')
|
|
3485
|
+
if args.allow_infobase_denied_message_update and args.protocol != '16.0':
|
|
3486
|
+
raise ValueError('Infobase denied message update MCP tool requires protocol 16.0')
|
|
3487
|
+
if args.allow_infobase_denied_message_update and args.cluster_user is not None:
|
|
3488
|
+
raise ValueError('OS-authenticated infobase denied message update MCP tool is not enabled')
|
|
3489
|
+
if args.allow_infobase_license_distribution_update and args.protocol != '16.0':
|
|
3490
|
+
raise ValueError('Infobase license distribution update MCP tool requires protocol 16.0')
|
|
3491
|
+
if args.allow_infobase_license_distribution_update and args.cluster_user is not None:
|
|
3492
|
+
raise ValueError('OS-authenticated infobase license distribution update MCP tool is not enabled')
|
|
3493
|
+
if args.allow_infobase_sessions_deny_update and args.protocol != '16.0':
|
|
3494
|
+
raise ValueError('Infobase sessions deny update MCP tool requires protocol 16.0')
|
|
3495
|
+
if args.allow_infobase_sessions_deny_update and args.cluster_user is not None:
|
|
3496
|
+
raise ValueError('OS-authenticated infobase sessions deny update MCP tool is not enabled')
|
|
3497
|
+
if args.allow_infobase_scheduled_jobs_deny_update and args.protocol != '16.0':
|
|
3498
|
+
raise ValueError('Infobase scheduled jobs deny update MCP tool requires protocol 16.0')
|
|
3499
|
+
if args.allow_infobase_scheduled_jobs_deny_update and args.cluster_user is not None:
|
|
3500
|
+
raise ValueError('OS-authenticated infobase scheduled jobs deny update MCP tool is not enabled')
|
|
3501
|
+
if args.allow_infobase_description_update and args.protocol != '16.0':
|
|
3502
|
+
raise ValueError('Infobase description update MCP tool requires protocol 16.0')
|
|
3503
|
+
if args.allow_infobase_description_update and args.cluster_user is not None:
|
|
3504
|
+
raise ValueError('OS-authenticated infobase description update MCP tool is not enabled')
|
|
3505
|
+
if args.allow_infobase_summary_description_update and args.protocol != '16.0':
|
|
3506
|
+
raise ValueError('Infobase summary description update MCP tool requires protocol 16.0')
|
|
3507
|
+
if args.allow_infobase_summary_description_update and args.cluster_user is not None:
|
|
3508
|
+
raise ValueError('OS-authenticated infobase summary description update MCP tool is not enabled')
|
|
3509
|
+
if args.allow_infobase_registration_drop and args.protocol != '16.0':
|
|
3510
|
+
raise ValueError('Infobase registration drop MCP tool requires protocol 16.0')
|
|
3511
|
+
if args.allow_infobase_registration_drop and args.cluster_user is not None:
|
|
3512
|
+
raise ValueError('OS-authenticated infobase registration drop MCP tool is not enabled')
|
|
3513
|
+
if args.allow_infobase_existing_registration and args.protocol != '16.0':
|
|
3514
|
+
raise ValueError('Existing infobase registration MCP tool requires protocol 16.0')
|
|
3515
|
+
if args.allow_infobase_existing_registration and args.cluster_user is not None:
|
|
3516
|
+
raise ValueError('OS-authenticated existing infobase registration MCP tool is not enabled')
|
|
3517
|
+
if args.allow_infobase_database_create and args.protocol != '16.0':
|
|
3518
|
+
raise ValueError('Infobase database creation MCP tool requires protocol 16.0')
|
|
3519
|
+
if args.allow_infobase_database_create and args.cluster_user is not None:
|
|
3520
|
+
raise ValueError('OS-authenticated infobase database creation MCP tool is not enabled')
|
|
3521
|
+
if args.allow_rule_remove and args.protocol != '16.0':
|
|
3522
|
+
raise ValueError('Rule remove MCP tool requires protocol 16.0')
|
|
3523
|
+
if args.allow_rule_priority_update and args.protocol != '16.0':
|
|
3524
|
+
raise ValueError('Rule priority update MCP tool requires protocol 16.0')
|
|
3525
|
+
if args.allow_rule_fields_update and args.protocol != '16.0':
|
|
3526
|
+
raise ValueError('Rule fields update MCP tool requires protocol 16.0')
|
|
3527
|
+
if args.allow_rule_fields_update and args.cluster_user is not None:
|
|
3528
|
+
raise ValueError('OS-authenticated rule fields update MCP tool is not enabled')
|
|
3529
|
+
if args.allow_rule_position_update and args.protocol != '16.0':
|
|
3530
|
+
raise ValueError('Rule position update MCP tool requires protocol 16.0')
|
|
3531
|
+
if args.allow_rule_position_update and args.cluster_user is not None:
|
|
3532
|
+
raise ValueError('OS-authenticated rule position update MCP tool is not enabled')
|
|
3533
|
+
if args.allow_limit_remove and args.protocol != '16.0':
|
|
3534
|
+
raise ValueError('Limit remove MCP tool requires protocol 16.0')
|
|
3535
|
+
if args.allow_limit_update and args.protocol != '16.0':
|
|
3536
|
+
raise ValueError('Limit update MCP tool requires protocol 16.0')
|
|
3537
|
+
if args.allow_limit_update and args.cluster_user is not None:
|
|
3538
|
+
raise ValueError('OS-authenticated limit update MCP tool is not enabled')
|
|
3539
|
+
if args.allow_counter_remove and args.protocol != '16.0':
|
|
3540
|
+
raise ValueError('Counter remove MCP tool requires protocol 16.0')
|
|
3541
|
+
if args.allow_counter_remove and args.cluster_user is not None:
|
|
3542
|
+
raise ValueError('OS-authenticated counter remove MCP tool is not enabled')
|
|
3543
|
+
if args.allow_counter_description_update and args.protocol != '16.0':
|
|
3544
|
+
raise ValueError('Counter description update MCP tool requires protocol 16.0')
|
|
3545
|
+
if args.allow_counter_description_update and args.cluster_user is not None:
|
|
3546
|
+
raise ValueError('OS-authenticated counter description update MCP tool is not enabled')
|
|
3547
|
+
if args.allow_counter_collection_time_update and args.protocol != '16.0':
|
|
3548
|
+
raise ValueError('Counter collection time update MCP tool requires protocol 16.0')
|
|
3549
|
+
if args.allow_counter_collection_time_update and args.cluster_user is not None:
|
|
3550
|
+
raise ValueError('OS-authenticated counter collection time update MCP tool is not enabled')
|
|
3551
|
+
if args.allow_counter_duration_update and args.protocol != '16.0':
|
|
3552
|
+
raise ValueError('Counter duration update MCP tool requires protocol 16.0')
|
|
3553
|
+
if args.allow_counter_duration_update and args.cluster_user is not None:
|
|
3554
|
+
raise ValueError('OS-authenticated counter duration update MCP tool is not enabled')
|
|
3555
|
+
if args.allow_counter_group_update and args.protocol != '16.0':
|
|
3556
|
+
raise ValueError('Counter group update MCP tool requires protocol 16.0')
|
|
3557
|
+
if args.allow_counter_group_update and args.cluster_user is not None:
|
|
3558
|
+
raise ValueError('OS-authenticated counter group update MCP tool is not enabled')
|
|
3559
|
+
if args.allow_counter_filter_type_update and args.protocol != '16.0':
|
|
3560
|
+
raise ValueError('Counter filter type update MCP tool requires protocol 16.0')
|
|
3561
|
+
if args.allow_counter_filter_type_update and args.cluster_user is not None:
|
|
3562
|
+
raise ValueError('OS-authenticated counter filter type update MCP tool is not enabled')
|
|
3563
|
+
if args.allow_counter_filter_update and args.protocol != '16.0':
|
|
3564
|
+
raise ValueError('Counter filter update MCP tool requires protocol 16.0')
|
|
3565
|
+
if args.allow_counter_filter_update and args.cluster_user is not None:
|
|
3566
|
+
raise ValueError('OS-authenticated counter filter update MCP tool is not enabled')
|
|
3567
|
+
if args.allow_counter_analysis_flags_update and args.protocol != '16.0':
|
|
3568
|
+
raise ValueError('Counter analysis flags update MCP tool requires protocol 16.0')
|
|
3569
|
+
if args.allow_counter_analysis_flags_update and args.cluster_user is not None:
|
|
3570
|
+
raise ValueError('OS-authenticated counter analysis flags update MCP tool is not enabled')
|
|
3571
|
+
if args.allow_counter_clear and args.protocol != '16.0':
|
|
3572
|
+
raise ValueError('Counter clear MCP tool requires protocol 16.0')
|
|
3573
|
+
if args.allow_counter_clear and args.cluster_user is not None:
|
|
3574
|
+
raise ValueError('OS-authenticated counter clear MCP tool is not enabled')
|
|
3575
|
+
if args.allow_process_turn_off and args.protocol != '16.0':
|
|
3576
|
+
raise ValueError('Process turn-off MCP tool requires protocol 16.0')
|
|
3577
|
+
if args.allow_process_turn_off and args.cluster_user is not None:
|
|
3578
|
+
raise ValueError('OS-authenticated process turn-off is not verified')
|
|
3579
|
+
if args.allow_cluster_insert and args.protocol != '16.0':
|
|
3580
|
+
raise ValueError('Cluster insert MCP tool requires protocol 16.0')
|
|
3581
|
+
if args.allow_cluster_insert and args.agent_user is not None:
|
|
3582
|
+
raise ValueError('OS-authenticated cluster insert MCP tool is not enabled')
|
|
3583
|
+
if args.allow_cluster_remove and args.protocol != '16.0':
|
|
3584
|
+
raise ValueError('Cluster remove MCP tool requires protocol 16.0')
|
|
3585
|
+
if args.allow_cluster_remove and args.cluster_user is not None:
|
|
3586
|
+
raise ValueError('OS-authenticated cluster remove MCP tool is not enabled')
|
|
3587
|
+
if args.allow_cluster_name_update and args.protocol != '16.0':
|
|
3588
|
+
raise ValueError('Cluster name update MCP tool requires protocol 16.0')
|
|
3589
|
+
if args.allow_cluster_name_update and args.agent_user is not None:
|
|
3590
|
+
raise ValueError('OS-authenticated cluster name update MCP tool is not enabled')
|
|
3591
|
+
if args.allow_cluster_expiration_timeout_update and args.protocol != '16.0':
|
|
3592
|
+
raise ValueError('Cluster timeout update MCP tool requires protocol 16.0')
|
|
3593
|
+
if args.allow_cluster_expiration_timeout_update and args.agent_user is not None:
|
|
3594
|
+
raise ValueError('OS-authenticated cluster timeout update MCP tool is not enabled')
|
|
3595
|
+
if args.allow_cluster_security_level_update and args.protocol != '16.0':
|
|
3596
|
+
raise ValueError('Cluster security level update MCP tool requires protocol 16.0')
|
|
3597
|
+
if args.allow_cluster_security_level_update and args.agent_user is not None:
|
|
3598
|
+
raise ValueError('OS-authenticated cluster security level update MCP tool is not enabled')
|
|
3599
|
+
if args.allow_cluster_session_fault_tolerance_level_update and args.protocol != '16.0':
|
|
3600
|
+
raise ValueError('Cluster session fault tolerance update MCP tool requires protocol 16.0')
|
|
3601
|
+
if args.allow_cluster_session_fault_tolerance_level_update and args.agent_user is not None:
|
|
3602
|
+
raise ValueError('OS-authenticated cluster session fault tolerance update MCP tool is not enabled')
|
|
3603
|
+
if args.allow_cluster_load_balancing_mode_update and args.protocol != '16.0':
|
|
3604
|
+
raise ValueError('Cluster load balancing update MCP tool requires protocol 16.0')
|
|
3605
|
+
if args.allow_cluster_load_balancing_mode_update and args.agent_user is not None:
|
|
3606
|
+
raise ValueError('OS-authenticated cluster load balancing update MCP tool is not enabled')
|
|
3607
|
+
if args.allow_cluster_kill_problem_processes_update and args.protocol != '16.0':
|
|
3608
|
+
raise ValueError('Cluster problem-process policy update MCP tool requires protocol 16.0')
|
|
3609
|
+
if args.allow_cluster_kill_problem_processes_update and args.agent_user is not None:
|
|
3610
|
+
raise ValueError('OS-authenticated cluster problem-process policy update MCP tool is not enabled')
|
|
3611
|
+
if args.allow_cluster_kill_by_memory_with_dump_update and args.protocol != '16.0':
|
|
3612
|
+
raise ValueError('Cluster kill-by-memory dump update MCP tool requires protocol 16.0')
|
|
3613
|
+
if args.allow_cluster_kill_by_memory_with_dump_update and args.agent_user is not None:
|
|
3614
|
+
raise ValueError('OS-authenticated cluster kill-by-memory dump update MCP tool is not enabled')
|
|
3615
|
+
if args.allow_cluster_audit_update and args.protocol != '16.0':
|
|
3616
|
+
raise ValueError('Cluster audit update MCP tool requires protocol 16.0')
|
|
3617
|
+
if args.allow_cluster_audit_update and args.agent_user is not None:
|
|
3618
|
+
raise ValueError('OS-authenticated cluster audit update MCP tool is not enabled')
|
|
3619
|
+
if args.allow_cluster_ping_update and args.protocol != '16.0':
|
|
3620
|
+
raise ValueError('Cluster ping update MCP tool requires protocol 16.0')
|
|
3621
|
+
if args.allow_cluster_ping_update and args.agent_user is not None:
|
|
3622
|
+
raise ValueError('OS-authenticated cluster ping update MCP tool is not enabled')
|
|
3623
|
+
if args.allow_cluster_restart_schedule_update and args.protocol != '16.0':
|
|
3624
|
+
raise ValueError('Cluster restart schedule update MCP tool requires protocol 16.0')
|
|
3625
|
+
if args.allow_cluster_restart_schedule_update and args.agent_user is not None:
|
|
3626
|
+
raise ValueError('OS-authenticated cluster restart schedule update MCP tool is not enabled')
|
|
3627
|
+
if args.allow_server_restart_schedule_update and args.protocol != '16.0':
|
|
3628
|
+
raise ValueError('Server restart schedule update MCP tool requires protocol 16.0')
|
|
3629
|
+
if args.allow_server_restart_schedule_update and args.cluster_user is not None:
|
|
3630
|
+
raise ValueError('OS-authenticated server restart schedule update MCP tool is not enabled')
|
|
3631
|
+
if args.allow_server_infobases_limit_update and args.protocol != '16.0':
|
|
3632
|
+
raise ValueError('Server infobases limit update MCP tool requires protocol 16.0')
|
|
3633
|
+
if args.allow_server_infobases_limit_update and args.cluster_user is not None:
|
|
3634
|
+
raise ValueError('OS-authenticated server infobases limit update MCP tool is not enabled')
|
|
3635
|
+
if args.allow_server_connections_limit_update and args.protocol != '16.0':
|
|
3636
|
+
raise ValueError('Server connections limit update MCP tool requires protocol 16.0')
|
|
3637
|
+
if args.allow_server_connections_limit_update and args.cluster_user is not None:
|
|
3638
|
+
raise ValueError('OS-authenticated server connections limit update MCP tool is not enabled')
|
|
3639
|
+
if args.allow_server_safe_call_memory_limit_update and args.protocol != '16.0':
|
|
3640
|
+
raise ValueError('Server safe-call memory limit update MCP tool requires protocol 16.0')
|
|
3641
|
+
if args.allow_server_safe_call_memory_limit_update and args.cluster_user is not None:
|
|
3642
|
+
raise ValueError('OS-authenticated server safe-call memory limit update MCP tool is not enabled')
|
|
3643
|
+
if args.allow_server_critical_total_memory_update and args.protocol != '16.0':
|
|
3644
|
+
raise ValueError('Server critical total memory update MCP tool requires protocol 16.0')
|
|
3645
|
+
if args.allow_server_critical_total_memory_update and args.cluster_user is not None:
|
|
3646
|
+
raise ValueError('OS-authenticated server critical total memory update MCP tool is not enabled')
|
|
3647
|
+
if args.allow_server_temporary_total_memory_update and args.protocol != '16.0':
|
|
3648
|
+
raise ValueError('Server temporary allowed total memory update MCP tool requires protocol 16.0')
|
|
3649
|
+
if args.allow_server_temporary_total_memory_update and args.cluster_user is not None:
|
|
3650
|
+
raise ValueError('OS-authenticated server temporary allowed total memory update MCP tool is not enabled')
|
|
3651
|
+
if args.allow_server_temporary_memory_time_limit_update and args.protocol != '16.0':
|
|
3652
|
+
raise ValueError('Server temporary memory time limit update MCP tool requires protocol 16.0')
|
|
3653
|
+
if args.allow_server_temporary_memory_time_limit_update and args.cluster_user is not None:
|
|
3654
|
+
raise ValueError('OS-authenticated server temporary memory time limit update MCP tool is not enabled')
|
|
3655
|
+
if args.allow_server_spn_update and args.protocol != '16.0':
|
|
3656
|
+
raise ValueError('Server SPN update MCP tool requires protocol 16.0')
|
|
3657
|
+
if args.allow_server_spn_update and args.cluster_user is not None:
|
|
3658
|
+
raise ValueError('OS-authenticated server SPN update MCP tool is not enabled')
|
|
3659
|
+
if args.allow_server_dedicate_managers_update and args.protocol != '16.0':
|
|
3660
|
+
raise ValueError('Server dedicated managers update MCP tool requires protocol 16.0')
|
|
3661
|
+
if args.allow_server_dedicate_managers_update and args.cluster_user is not None:
|
|
3662
|
+
raise ValueError('OS-authenticated server dedicated managers update MCP tool is not enabled')
|
|
3663
|
+
if args.allow_server_port_ranges_update and args.protocol != '16.0':
|
|
3664
|
+
raise ValueError('Server port ranges update MCP tool requires protocol 16.0')
|
|
3665
|
+
if args.allow_server_port_ranges_update and args.cluster_user is not None:
|
|
3666
|
+
raise ValueError('OS-authenticated server port ranges update MCP tool is not enabled')
|
|
3667
|
+
for name in (args.agent_user, args.cluster_user):
|
|
3668
|
+
if name is not None and (not name or '\x00' in name or len(name.encode('utf-8')) > 1024):
|
|
3669
|
+
raise ValueError('Invalid administrator name')
|
|
3670
|
+
factory = partial(RacClient, args.ras_host, args.ras_port, args.protocol,
|
|
3671
|
+
agent_user=args.agent_user, cluster_user=args.cluster_user)
|
|
3672
|
+
service = ReadService(factory)
|
|
3673
|
+
options = {}
|
|
3674
|
+
if args.ras_server:
|
|
3675
|
+
options['workspace_endpoints'] = endpoints
|
|
3676
|
+
if args.allow_agent_admin_register:
|
|
3677
|
+
options['allow_agent_admin_register'] = True
|
|
3678
|
+
if args.allow_agent_admin_remove:
|
|
3679
|
+
options['allow_agent_admin_remove'] = True
|
|
3680
|
+
if args.allow_cluster_admin_register:
|
|
3681
|
+
options['allow_cluster_admin_register'] = True
|
|
3682
|
+
if args.allow_cluster_admin_remove:
|
|
3683
|
+
options['allow_cluster_admin_remove'] = True
|
|
3684
|
+
if args.allow_rule_insert:
|
|
3685
|
+
options['allow_rule_insert'] = True
|
|
3686
|
+
if args.allow_rule_apply:
|
|
3687
|
+
options['allow_rule_apply'] = True
|
|
3688
|
+
if args.allow_service_settings_apply:
|
|
3689
|
+
options['allow_service_settings_apply'] = True
|
|
3690
|
+
if args.allow_service_setting_remove:
|
|
3691
|
+
options['allow_service_setting_remove'] = True
|
|
3692
|
+
if args.allow_service_setting_update:
|
|
3693
|
+
options['allow_service_setting_update'] = True
|
|
3694
|
+
if args.allow_service_setting_insert:
|
|
3695
|
+
options['allow_service_setting_insert'] = True
|
|
3696
|
+
if args.allow_profile_remove:
|
|
3697
|
+
options['allow_profile_remove'] = True
|
|
3698
|
+
if args.allow_profile_description_update:
|
|
3699
|
+
options['allow_profile_description_update'] = True
|
|
3700
|
+
if args.allow_profile_config_update:
|
|
3701
|
+
options['allow_profile_config_update'] = True
|
|
3702
|
+
if args.allow_profile_access_flags_update:
|
|
3703
|
+
options['allow_profile_access_flags_update'] = True
|
|
3704
|
+
if args.allow_profile_role_fields_update:
|
|
3705
|
+
options['allow_profile_role_fields_update'] = True
|
|
3706
|
+
if args.allow_profile_directory_remove:
|
|
3707
|
+
options['allow_profile_directory_remove'] = True
|
|
3708
|
+
if args.allow_profile_directory_update:
|
|
3709
|
+
options['allow_profile_directory_update'] = True
|
|
3710
|
+
if args.allow_profile_com_remove:
|
|
3711
|
+
options['allow_profile_com_remove'] = True
|
|
3712
|
+
if args.allow_profile_com_update:
|
|
3713
|
+
options['allow_profile_com_update'] = True
|
|
3714
|
+
if args.allow_profile_addin_remove:
|
|
3715
|
+
options['allow_profile_addin_remove'] = True
|
|
3716
|
+
if args.allow_profile_addin_update:
|
|
3717
|
+
options['allow_profile_addin_update'] = True
|
|
3718
|
+
if args.allow_profile_module_remove:
|
|
3719
|
+
options['allow_profile_module_remove'] = True
|
|
3720
|
+
if args.allow_profile_module_update:
|
|
3721
|
+
options['allow_profile_module_update'] = True
|
|
3722
|
+
if args.allow_profile_app_remove:
|
|
3723
|
+
options['allow_profile_app_remove'] = True
|
|
3724
|
+
if args.allow_profile_app_update:
|
|
3725
|
+
options['allow_profile_app_update'] = True
|
|
3726
|
+
if args.allow_profile_inet_remove:
|
|
3727
|
+
options['allow_profile_inet_remove'] = True
|
|
3728
|
+
if args.allow_profile_inet_update:
|
|
3729
|
+
options['allow_profile_inet_update'] = True
|
|
3730
|
+
if args.allow_infobase_security_profile_update:
|
|
3731
|
+
options['allow_infobase_security_profile_update'] = True
|
|
3732
|
+
if args.allow_infobase_safe_mode_security_profile_update:
|
|
3733
|
+
options['allow_infobase_safe_mode_security_profile_update'] = True
|
|
3734
|
+
if args.allow_infobase_maximum_scheduled_jobs_shift_update:
|
|
3735
|
+
options['allow_infobase_maximum_scheduled_jobs_shift_update'] = True
|
|
3736
|
+
if args.allow_infobase_minimum_scheduled_jobs_period_update:
|
|
3737
|
+
options['allow_infobase_minimum_scheduled_jobs_period_update'] = True
|
|
3738
|
+
if args.allow_infobase_configuration_unload_delay_update:
|
|
3739
|
+
options['allow_infobase_configuration_unload_delay_update'] = True
|
|
3740
|
+
if args.allow_infobase_local_speech_update:
|
|
3741
|
+
options['allow_infobase_local_speech_update'] = True
|
|
3742
|
+
if args.allow_infobase_external_session_manager_update:
|
|
3743
|
+
options['allow_infobase_external_session_manager_update'] = True
|
|
3744
|
+
if args.allow_infobase_reserve_working_processes_update:
|
|
3745
|
+
options['allow_infobase_reserve_working_processes_update'] = True
|
|
3746
|
+
if args.allow_infobase_denied_message_update:
|
|
3747
|
+
options['allow_infobase_denied_message_update'] = True
|
|
3748
|
+
if args.allow_infobase_license_distribution_update:
|
|
3749
|
+
options['allow_infobase_license_distribution_update'] = True
|
|
3750
|
+
if args.allow_infobase_sessions_deny_update:
|
|
3751
|
+
options['allow_infobase_sessions_deny_update'] = True
|
|
3752
|
+
if args.allow_infobase_scheduled_jobs_deny_update:
|
|
3753
|
+
options['allow_infobase_scheduled_jobs_deny_update'] = True
|
|
3754
|
+
if args.allow_infobase_description_update:
|
|
3755
|
+
options['allow_infobase_description_update'] = True
|
|
3756
|
+
if args.allow_infobase_summary_description_update:
|
|
3757
|
+
options['allow_infobase_summary_description_update'] = True
|
|
3758
|
+
if args.allow_infobase_registration_drop:
|
|
3759
|
+
options['allow_infobase_registration_drop'] = True
|
|
3760
|
+
if args.allow_infobase_existing_registration:
|
|
3761
|
+
options['allow_infobase_existing_registration'] = True
|
|
3762
|
+
if args.allow_infobase_database_create:
|
|
3763
|
+
options['allow_infobase_database_create'] = True
|
|
3764
|
+
if args.allow_rule_remove:
|
|
3765
|
+
options['allow_rule_remove'] = True
|
|
3766
|
+
if args.allow_rule_priority_update:
|
|
3767
|
+
options['allow_rule_priority_update'] = True
|
|
3768
|
+
if args.allow_rule_fields_update:
|
|
3769
|
+
options['allow_rule_fields_update'] = True
|
|
3770
|
+
if args.allow_rule_position_update:
|
|
3771
|
+
options['allow_rule_position_update'] = True
|
|
3772
|
+
if args.allow_limit_remove:
|
|
3773
|
+
options['allow_limit_remove'] = True
|
|
3774
|
+
if args.allow_limit_update:
|
|
3775
|
+
options['allow_limit_update'] = True
|
|
3776
|
+
if args.allow_counter_remove:
|
|
3777
|
+
options['allow_counter_remove'] = True
|
|
3778
|
+
if args.allow_counter_description_update:
|
|
3779
|
+
options['allow_counter_description_update'] = True
|
|
3780
|
+
if args.allow_counter_collection_time_update:
|
|
3781
|
+
options['allow_counter_collection_time_update'] = True
|
|
3782
|
+
if args.allow_counter_duration_update:
|
|
3783
|
+
options['allow_counter_duration_update'] = True
|
|
3784
|
+
if args.allow_counter_group_update:
|
|
3785
|
+
options['allow_counter_group_update'] = True
|
|
3786
|
+
if args.allow_counter_filter_type_update:
|
|
3787
|
+
options['allow_counter_filter_type_update'] = True
|
|
3788
|
+
if args.allow_counter_filter_update:
|
|
3789
|
+
options['allow_counter_filter_update'] = True
|
|
3790
|
+
if args.allow_counter_analysis_flags_update:
|
|
3791
|
+
options['allow_counter_analysis_flags_update'] = True
|
|
3792
|
+
if args.allow_counter_clear:
|
|
3793
|
+
options['allow_counter_clear'] = True
|
|
3794
|
+
if args.allow_process_turn_off:
|
|
3795
|
+
options['allow_process_turn_off'] = True
|
|
3796
|
+
if args.allow_cluster_insert:
|
|
3797
|
+
options['allow_cluster_insert'] = True
|
|
3798
|
+
if args.allow_cluster_remove:
|
|
3799
|
+
options['allow_cluster_remove'] = True
|
|
3800
|
+
if args.allow_cluster_name_update:
|
|
3801
|
+
options['allow_cluster_name_update'] = True
|
|
3802
|
+
if args.allow_cluster_expiration_timeout_update:
|
|
3803
|
+
options['allow_cluster_expiration_timeout_update'] = True
|
|
3804
|
+
if args.allow_cluster_security_level_update:
|
|
3805
|
+
options['allow_cluster_security_level_update'] = True
|
|
3806
|
+
if args.allow_cluster_session_fault_tolerance_level_update:
|
|
3807
|
+
options['allow_cluster_session_fault_tolerance_level_update'] = True
|
|
3808
|
+
if args.allow_cluster_load_balancing_mode_update:
|
|
3809
|
+
options['allow_cluster_load_balancing_mode_update'] = True
|
|
3810
|
+
if args.allow_cluster_kill_problem_processes_update:
|
|
3811
|
+
options['allow_cluster_kill_problem_processes_update'] = True
|
|
3812
|
+
if args.allow_cluster_kill_by_memory_with_dump_update:
|
|
3813
|
+
options['allow_cluster_kill_by_memory_with_dump_update'] = True
|
|
3814
|
+
if args.allow_cluster_audit_update:
|
|
3815
|
+
options['allow_cluster_audit_update'] = True
|
|
3816
|
+
if args.allow_cluster_ping_update:
|
|
3817
|
+
options['allow_cluster_ping_update'] = True
|
|
3818
|
+
if args.allow_cluster_restart_schedule_update:
|
|
3819
|
+
options['allow_cluster_restart_schedule_update'] = True
|
|
3820
|
+
if args.allow_server_restart_schedule_update:
|
|
3821
|
+
options['allow_server_restart_schedule_update'] = True
|
|
3822
|
+
if args.allow_server_infobases_limit_update:
|
|
3823
|
+
options['allow_server_infobases_limit_update'] = True
|
|
3824
|
+
if args.allow_server_connections_limit_update:
|
|
3825
|
+
options['allow_server_connections_limit_update'] = True
|
|
3826
|
+
if args.allow_server_safe_call_memory_limit_update:
|
|
3827
|
+
options['allow_server_safe_call_memory_limit_update'] = True
|
|
3828
|
+
if args.allow_server_critical_total_memory_update:
|
|
3829
|
+
options['allow_server_critical_total_memory_update'] = True
|
|
3830
|
+
if args.allow_server_temporary_total_memory_update:
|
|
3831
|
+
options['allow_server_temporary_total_memory_update'] = True
|
|
3832
|
+
if args.allow_server_temporary_memory_time_limit_update:
|
|
3833
|
+
options['allow_server_temporary_memory_time_limit_update'] = True
|
|
3834
|
+
if args.allow_server_spn_update:
|
|
3835
|
+
options['allow_server_spn_update'] = True
|
|
3836
|
+
if args.allow_server_dedicate_managers_update:
|
|
3837
|
+
options['allow_server_dedicate_managers_update'] = True
|
|
3838
|
+
if args.allow_server_port_ranges_update:
|
|
3839
|
+
options['allow_server_port_ranges_update'] = True
|
|
3840
|
+
server = create_server(service, **options)
|
|
3841
|
+
asyncio.run(server.run_stdio_async())
|
|
3842
|
+
|
|
3843
|
+
|
|
3844
|
+
if __name__ == '__main__':
|
|
3845
|
+
main()
|