pyxecm 2.0.2__py3-none-any.whl → 2.0.4__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.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/__init__.py +3 -2
- pyxecm/avts.py +3 -1
- pyxecm/coreshare.py +71 -5
- pyxecm/customizer/api/app.py +7 -13
- pyxecm/customizer/api/auth/functions.py +37 -30
- pyxecm/customizer/api/common/functions.py +54 -0
- pyxecm/customizer/api/common/payload_list.py +39 -10
- pyxecm/customizer/api/common/router.py +55 -6
- pyxecm/customizer/api/settings.py +14 -3
- pyxecm/customizer/api/terminal/router.py +43 -18
- pyxecm/customizer/api/v1_csai/models.py +18 -0
- pyxecm/customizer/api/v1_csai/router.py +26 -1
- pyxecm/customizer/api/v1_otcs/router.py +16 -6
- pyxecm/customizer/api/v1_payload/functions.py +9 -3
- pyxecm/customizer/browser_automation.py +506 -199
- pyxecm/customizer/customizer.py +123 -22
- pyxecm/customizer/guidewire.py +170 -37
- pyxecm/customizer/payload.py +723 -330
- pyxecm/customizer/settings.py +21 -3
- pyxecm/customizer/translate.py +14 -10
- pyxecm/helper/data.py +12 -20
- pyxecm/helper/xml.py +1 -1
- pyxecm/maintenance_page/app.py +6 -2
- pyxecm/otawp.py +10 -6
- pyxecm/otca.py +187 -21
- pyxecm/otcs.py +2424 -415
- pyxecm/otds.py +4 -11
- pyxecm/otkd.py +1369 -0
- pyxecm/otmm.py +190 -66
- {pyxecm-2.0.2.dist-info → pyxecm-2.0.4.dist-info}/METADATA +2 -2
- {pyxecm-2.0.2.dist-info → pyxecm-2.0.4.dist-info}/RECORD +34 -32
- {pyxecm-2.0.2.dist-info → pyxecm-2.0.4.dist-info}/WHEEL +1 -1
- {pyxecm-2.0.2.dist-info → pyxecm-2.0.4.dist-info}/licenses/LICENSE +0 -0
- {pyxecm-2.0.2.dist-info → pyxecm-2.0.4.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import contextlib
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import pty
|
|
7
8
|
import signal
|
|
@@ -15,13 +16,16 @@ router = APIRouter(tags=["terminal"])
|
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@router.websocket("/ws/terminal")
|
|
18
|
-
async def ws_terminal(
|
|
19
|
+
async def ws_terminal(
|
|
20
|
+
websocket: WebSocket, pod: str = Query(...), command: str = Query(...), container: str = Query(None)
|
|
21
|
+
) -> None:
|
|
19
22
|
"""Websocket to connect to a shell session in a pod.
|
|
20
23
|
|
|
21
24
|
Args:
|
|
22
25
|
websocket (WebSocket): WebSocket to connect to the shell session.
|
|
23
26
|
pod (str): pod name to connect to.
|
|
24
27
|
command (str): command to be executed.
|
|
28
|
+
container (str, optional): container name to connect to.
|
|
25
29
|
|
|
26
30
|
"""
|
|
27
31
|
await websocket.accept()
|
|
@@ -30,7 +34,7 @@ async def ws_terminal(websocket: WebSocket, pod: str = Query(...), command: str
|
|
|
30
34
|
# Wait for the first message to be the token
|
|
31
35
|
token = await websocket.receive_text()
|
|
32
36
|
|
|
33
|
-
user = await get_current_user(token)
|
|
37
|
+
user = await get_current_user(token=token, api_key=token)
|
|
34
38
|
authrorized = await get_authorized_user(user)
|
|
35
39
|
|
|
36
40
|
if not authrorized:
|
|
@@ -45,11 +49,17 @@ async def ws_terminal(websocket: WebSocket, pod: str = Query(...), command: str
|
|
|
45
49
|
except WebSocketDisconnect:
|
|
46
50
|
return
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
container = ["-c", container] if container else []
|
|
53
|
+
|
|
54
|
+
process = [command] if pod == "customizer" else ["kubectl", "exec", "-it", pod, *container, "--", command]
|
|
49
55
|
|
|
50
56
|
pid, fd = pty.fork()
|
|
57
|
+
import fcntl
|
|
58
|
+
import struct
|
|
59
|
+
import termios
|
|
60
|
+
|
|
51
61
|
if pid == 0:
|
|
52
|
-
subprocess.run(process, check=False) # noqa: ASYNC221
|
|
62
|
+
process = subprocess.run(process, check=False) # noqa: ASYNC221
|
|
53
63
|
|
|
54
64
|
async def read_from_pty() -> None:
|
|
55
65
|
loop = asyncio.get_event_loop()
|
|
@@ -64,24 +74,39 @@ async def ws_terminal(websocket: WebSocket, pod: str = Query(...), command: str
|
|
|
64
74
|
try:
|
|
65
75
|
while True:
|
|
66
76
|
data = await websocket.receive_text()
|
|
67
|
-
|
|
77
|
+
if data.startswith("{") and data.endswith("}"):
|
|
78
|
+
message = json.loads(data)
|
|
79
|
+
if message.get("type") == "resize":
|
|
80
|
+
rows = message.get("rows")
|
|
81
|
+
cols = message.get("cols")
|
|
82
|
+
|
|
83
|
+
packed_size = struct.pack("HHHH", rows, cols, 0, 0)
|
|
84
|
+
fcntl.ioctl(fd, termios.TIOCSWINSZ, packed_size)
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
os.write(fd, data.encode())
|
|
68
88
|
except Exception: # noqa: S110
|
|
69
89
|
pass
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
try:
|
|
92
|
+
# Launch read/write tasks
|
|
93
|
+
read_task = asyncio.create_task(read_from_pty())
|
|
94
|
+
write_task = asyncio.create_task(write_to_pty())
|
|
95
|
+
|
|
96
|
+
done, pending = await asyncio.wait([read_task, write_task], return_when=asyncio.FIRST_COMPLETED)
|
|
74
97
|
|
|
75
|
-
|
|
98
|
+
# Cancel other task
|
|
99
|
+
for task in pending:
|
|
100
|
+
task.cancel()
|
|
101
|
+
finally:
|
|
102
|
+
with contextlib.suppress(Exception):
|
|
103
|
+
await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
|
|
76
104
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
task.cancel()
|
|
105
|
+
with contextlib.suppress(Exception):
|
|
106
|
+
process.terminate()
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
except ProcessLookupError:
|
|
84
|
-
pass # Already exited
|
|
108
|
+
with contextlib.suppress(Exception):
|
|
109
|
+
os.kill(pid, signal.SIGKILL)
|
|
85
110
|
|
|
86
|
-
|
|
87
|
-
|
|
111
|
+
with contextlib.suppress(Exception):
|
|
112
|
+
os.close(fd)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Define Models for Payload."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CSAIEmbedMetadata(BaseModel):
|
|
7
|
+
"""Defines Data Model for embeding metadata for documents and workspaces."""
|
|
8
|
+
|
|
9
|
+
node_id: int = None
|
|
10
|
+
crawl: bool = False
|
|
11
|
+
wait_for_completion: bool = False
|
|
12
|
+
message_override: dict = None
|
|
13
|
+
timeout: float = 30.0
|
|
14
|
+
document_metadata: bool = False
|
|
15
|
+
images: bool = False
|
|
16
|
+
image_prompt: str = ""
|
|
17
|
+
workspace_metadata: bool = True
|
|
18
|
+
remove_existing: bool = False
|
|
@@ -9,15 +9,40 @@ from fastapi.responses import JSONResponse
|
|
|
9
9
|
|
|
10
10
|
from pyxecm.customizer.api.auth.functions import get_authorized_user
|
|
11
11
|
from pyxecm.customizer.api.auth.models import User
|
|
12
|
-
from pyxecm.customizer.api.common.functions import get_k8s_object, get_settings
|
|
12
|
+
from pyxecm.customizer.api.common.functions import get_k8s_object, get_otcs_object, get_settings
|
|
13
13
|
from pyxecm.customizer.api.settings import CustomizerAPISettings
|
|
14
|
+
from pyxecm.customizer.api.v1_csai.models import CSAIEmbedMetadata
|
|
14
15
|
from pyxecm.customizer.k8s import K8s
|
|
16
|
+
from pyxecm.otcs import OTCS
|
|
15
17
|
|
|
16
18
|
router = APIRouter(prefix="/api/v1/csai", tags=["csai"])
|
|
17
19
|
|
|
18
20
|
logger = logging.getLogger("pyxecm.customizer.api.v1_csai")
|
|
19
21
|
|
|
20
22
|
|
|
23
|
+
@router.post("/metadata")
|
|
24
|
+
def embed_metadata(
|
|
25
|
+
user: Annotated[User, Depends(get_authorized_user)], # noqa: ARG001
|
|
26
|
+
otcs_object: Annotated[OTCS, Depends(get_otcs_object)],
|
|
27
|
+
body: Annotated[CSAIEmbedMetadata, Body()],
|
|
28
|
+
) -> JSONResponse:
|
|
29
|
+
"""Embed the Metadata of the given objects.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
user (Annotated[User, Depends): User required for authentication
|
|
33
|
+
otcs_object (Annotated[OTCS, Depends(get_otcs_object)]): OTCS object to interact with OTCS
|
|
34
|
+
body (Annotated[CSAIEmbedMetadata, Body): Request body
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
JSONResponse: JSONResponse with success=true/false
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
success = otcs_object.aviator_embed_metadata(**body.model_dump())
|
|
42
|
+
|
|
43
|
+
return JSONResponse({"success": success})
|
|
44
|
+
|
|
45
|
+
|
|
21
46
|
@router.get("")
|
|
22
47
|
def get_csai_config_data(
|
|
23
48
|
user: Annotated[User, Depends(get_authorized_user)],
|
|
@@ -35,10 +35,15 @@ async def put_otcs_logs(
|
|
|
35
35
|
|
|
36
36
|
if "all" in hosts:
|
|
37
37
|
hosts = []
|
|
38
|
-
for sts in ["otcs-admin", "otcs-frontend", "otcs-backend-search"]:
|
|
38
|
+
for sts in ["otcs-admin", "otcs-frontend", "otcs-backend-search", "otcs-da"]:
|
|
39
39
|
try:
|
|
40
|
-
sts_replicas = k8s_object.get_stateful_set_scale(sts)
|
|
41
|
-
|
|
40
|
+
sts_replicas = k8s_object.get_stateful_set_scale(sts)
|
|
41
|
+
|
|
42
|
+
if sts_replicas is None:
|
|
43
|
+
logger.debug("Cannot get statefulset {sts}")
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
hosts.extend([f"{sts}-{i}" for i in range(sts_replicas.status.replicas)])
|
|
42
47
|
except Exception as e:
|
|
43
48
|
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR) from e
|
|
44
49
|
|
|
@@ -118,10 +123,15 @@ async def get_otcs_log_files(
|
|
|
118
123
|
response = {"status": {host: bool(otcs_logs_lock[host].locked()) for host in otcs_logs_lock}, "files": files}
|
|
119
124
|
|
|
120
125
|
# Extend response with all hosts
|
|
121
|
-
for sts in ["otcs-admin", "otcs-frontend", "otcs-backend-search"]:
|
|
126
|
+
for sts in ["otcs-admin", "otcs-frontend", "otcs-backend-search", "otcs-da"]:
|
|
122
127
|
try:
|
|
123
|
-
sts_replicas = k8s_object.get_stateful_set_scale(sts)
|
|
124
|
-
|
|
128
|
+
sts_replicas = k8s_object.get_stateful_set_scale(sts)
|
|
129
|
+
|
|
130
|
+
if sts_replicas is None:
|
|
131
|
+
logger.debug("Cannot get statefulset {sts}")
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
for i in range(sts_replicas.status.replicas):
|
|
125
135
|
host = f"{sts}-{i}"
|
|
126
136
|
|
|
127
137
|
if host in otcs_logs_lock:
|
|
@@ -129,16 +129,22 @@ def import_payload(
|
|
|
129
129
|
payload_dir = payload
|
|
130
130
|
|
|
131
131
|
if payload_dir is None:
|
|
132
|
-
|
|
132
|
+
try:
|
|
133
|
+
import_payload_file(payload, enabled, dependencies)
|
|
134
|
+
except PayloadImportError as exc:
|
|
135
|
+
logger.error(exc)
|
|
136
|
+
logger.debug(exc, exc_info=True)
|
|
133
137
|
return
|
|
138
|
+
|
|
134
139
|
elif not os.path.isdir(payload_dir):
|
|
135
140
|
return
|
|
136
141
|
|
|
137
142
|
for filename in sorted(os.listdir(payload_dir)):
|
|
138
143
|
try:
|
|
139
144
|
import_payload_file(os.path.join(payload_dir, filename), enabled, dependencies)
|
|
140
|
-
except PayloadImportError:
|
|
141
|
-
logger.error(
|
|
145
|
+
except PayloadImportError as exc:
|
|
146
|
+
logger.error(exc)
|
|
147
|
+
logger.debug(exc, exc_info=True)
|
|
142
148
|
|
|
143
149
|
|
|
144
150
|
def prepare_dependencies(dependencies: list) -> list | None:
|