pyxecm 1.6__py3-none-any.whl → 2.0.1__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.

Files changed (78) hide show
  1. pyxecm/__init__.py +7 -4
  2. pyxecm/avts.py +727 -254
  3. pyxecm/coreshare.py +686 -467
  4. pyxecm/customizer/__init__.py +16 -4
  5. pyxecm/customizer/__main__.py +58 -0
  6. pyxecm/customizer/api/__init__.py +5 -0
  7. pyxecm/customizer/api/__main__.py +6 -0
  8. pyxecm/customizer/api/app.py +163 -0
  9. pyxecm/customizer/api/auth/__init__.py +1 -0
  10. pyxecm/customizer/api/auth/functions.py +92 -0
  11. pyxecm/customizer/api/auth/models.py +13 -0
  12. pyxecm/customizer/api/auth/router.py +78 -0
  13. pyxecm/customizer/api/common/__init__.py +1 -0
  14. pyxecm/customizer/api/common/functions.py +47 -0
  15. pyxecm/customizer/api/common/metrics.py +92 -0
  16. pyxecm/customizer/api/common/models.py +21 -0
  17. pyxecm/customizer/api/common/payload_list.py +870 -0
  18. pyxecm/customizer/api/common/router.py +72 -0
  19. pyxecm/customizer/api/settings.py +128 -0
  20. pyxecm/customizer/api/terminal/__init__.py +1 -0
  21. pyxecm/customizer/api/terminal/router.py +87 -0
  22. pyxecm/customizer/api/v1_csai/__init__.py +1 -0
  23. pyxecm/customizer/api/v1_csai/router.py +87 -0
  24. pyxecm/customizer/api/v1_maintenance/__init__.py +1 -0
  25. pyxecm/customizer/api/v1_maintenance/functions.py +100 -0
  26. pyxecm/customizer/api/v1_maintenance/models.py +12 -0
  27. pyxecm/customizer/api/v1_maintenance/router.py +76 -0
  28. pyxecm/customizer/api/v1_otcs/__init__.py +1 -0
  29. pyxecm/customizer/api/v1_otcs/functions.py +61 -0
  30. pyxecm/customizer/api/v1_otcs/router.py +179 -0
  31. pyxecm/customizer/api/v1_payload/__init__.py +1 -0
  32. pyxecm/customizer/api/v1_payload/functions.py +179 -0
  33. pyxecm/customizer/api/v1_payload/models.py +51 -0
  34. pyxecm/customizer/api/v1_payload/router.py +499 -0
  35. pyxecm/customizer/browser_automation.py +721 -286
  36. pyxecm/customizer/customizer.py +1076 -1425
  37. pyxecm/customizer/exceptions.py +35 -0
  38. pyxecm/customizer/guidewire.py +1186 -0
  39. pyxecm/customizer/k8s.py +901 -379
  40. pyxecm/customizer/log.py +107 -0
  41. pyxecm/customizer/m365.py +2967 -920
  42. pyxecm/customizer/nhc.py +1169 -0
  43. pyxecm/customizer/openapi.py +258 -0
  44. pyxecm/customizer/payload.py +18228 -7820
  45. pyxecm/customizer/pht.py +717 -286
  46. pyxecm/customizer/salesforce.py +516 -342
  47. pyxecm/customizer/sap.py +58 -41
  48. pyxecm/customizer/servicenow.py +611 -372
  49. pyxecm/customizer/settings.py +445 -0
  50. pyxecm/customizer/successfactors.py +408 -346
  51. pyxecm/customizer/translate.py +83 -48
  52. pyxecm/helper/__init__.py +5 -2
  53. pyxecm/helper/assoc.py +83 -43
  54. pyxecm/helper/data.py +2406 -870
  55. pyxecm/helper/logadapter.py +27 -0
  56. pyxecm/helper/web.py +229 -101
  57. pyxecm/helper/xml.py +596 -171
  58. pyxecm/maintenance_page/__init__.py +5 -0
  59. pyxecm/maintenance_page/__main__.py +6 -0
  60. pyxecm/maintenance_page/app.py +51 -0
  61. pyxecm/maintenance_page/settings.py +28 -0
  62. pyxecm/maintenance_page/static/favicon.avif +0 -0
  63. pyxecm/maintenance_page/templates/maintenance.html +165 -0
  64. pyxecm/otac.py +235 -141
  65. pyxecm/otawp.py +2668 -1220
  66. pyxecm/otca.py +569 -0
  67. pyxecm/otcs.py +7956 -3237
  68. pyxecm/otds.py +2178 -925
  69. pyxecm/otiv.py +36 -21
  70. pyxecm/otmm.py +1272 -325
  71. pyxecm/otpd.py +231 -127
  72. pyxecm-2.0.1.dist-info/METADATA +122 -0
  73. pyxecm-2.0.1.dist-info/RECORD +76 -0
  74. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/WHEEL +1 -1
  75. pyxecm-1.6.dist-info/METADATA +0 -53
  76. pyxecm-1.6.dist-info/RECORD +0 -32
  77. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info/licenses}/LICENSE +0 -0
  78. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,24 @@
1
- """PYXECM classes for Customizer"""
1
+ """PYXECM classes for Customizer."""
2
2
 
3
3
  from .browser_automation import BrowserAutomation
4
-
5
4
  from .customizer import Customizer
6
5
  from .k8s import K8s
7
6
  from .m365 import M365
8
7
  from .payload import Payload
9
- from .sap import SAP
10
8
  from .salesforce import Salesforce
11
- from .successfactors import SuccessFactors
9
+ from .sap import SAP
12
10
  from .servicenow import ServiceNow
11
+ from .successfactors import SuccessFactors
12
+
13
+ __all__ = [
14
+ "M365",
15
+ "SAP",
16
+ "BrowserAutomation",
17
+ "Customizer",
18
+ "Guidewire",
19
+ "K8s",
20
+ "Payload",
21
+ "Salesforce",
22
+ "ServiceNow",
23
+ "SuccessFactors",
24
+ ]
@@ -0,0 +1,58 @@
1
+ """Start the Customizer to process a payload."""
2
+
3
+ __author__ = "Dr. Marc Diefenbruch"
4
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
5
+ __credits__ = ["Kai-Philip Gatzweiler"]
6
+ __maintainer__ = "Dr. Marc Diefenbruch"
7
+ __email__ = "mdiefenb@opentext.com"
8
+
9
+ import logging
10
+ import os
11
+ import sys
12
+
13
+ from pyxecm.customizer import Customizer
14
+ from pyxecm.customizer.payload import load_payload
15
+
16
+ logger = logging.getLogger("customizer")
17
+
18
+
19
+ def main(argv: list[str]) -> int:
20
+ """Start the Customizer."""
21
+
22
+ if len(argv) < 2:
23
+ logger.error("No input file specified")
24
+ sys.exit(1)
25
+
26
+ payload_filename = argv[1]
27
+
28
+ if not os.path.isfile(payload_filename):
29
+ logger.error("Input file does not exist")
30
+ sys.exit(1)
31
+
32
+ payload = load_payload(payload_filename)
33
+ customizer_settings = payload.get("customizerSettings", {})
34
+
35
+ # Overwrite the customizer settings with the payload specific ones:
36
+ customizer_settings.update({"cust_payload": payload_filename})
37
+
38
+ my_customizer = Customizer(logger=logger, settings=customizer_settings)
39
+
40
+ my_customizer.customization_run()
41
+
42
+ return 0
43
+
44
+
45
+ if __name__ == "__main__":
46
+ logging.basicConfig(
47
+ format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
48
+ datefmt="%d-%b-%Y %H:%M:%S",
49
+ level=logging.INFO,
50
+ handlers=[
51
+ logging.StreamHandler(sys.stdout),
52
+ ],
53
+ )
54
+
55
+ try:
56
+ main(sys.argv)
57
+ except KeyboardInterrupt:
58
+ logger.warning("KeyboardInterrupt - exiting")
@@ -0,0 +1,5 @@
1
+ """PYXECM API main imports."""
2
+
3
+ from .app import app, run_api
4
+
5
+ __all__ = ["app", "run_api"]
@@ -0,0 +1,6 @@
1
+ """Wrapper to start the Customizer API."""
2
+
3
+ from pyxecm.customizer.api import run_api
4
+
5
+ if __name__ == "__main__":
6
+ run_api()
@@ -0,0 +1,163 @@
1
+ """API Implemenation for the Customizer to start and control the payload processing."""
2
+
3
+ __author__ = "Dr. Marc Diefenbruch"
4
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
5
+ __credits__ = ["Kai-Philip Gatzweiler"]
6
+ __maintainer__ = "Dr. Marc Diefenbruch"
7
+ __email__ = "mdiefenb@opentext.com"
8
+
9
+ import logging
10
+ import os
11
+ import sys
12
+ from collections.abc import AsyncGenerator
13
+ from contextlib import asynccontextmanager
14
+ from datetime import datetime, timezone
15
+ from importlib.metadata import version
16
+ from threading import Thread
17
+
18
+ import uvicorn
19
+ from fastapi import FastAPI
20
+ from fastapi.middleware.cors import CORSMiddleware
21
+ from prometheus_fastapi_instrumentator import Instrumentator
22
+
23
+ from pyxecm.customizer.api.auth.router import router as auth_router
24
+ from pyxecm.customizer.api.common.functions import PAYLOAD_LIST
25
+ from pyxecm.customizer.api.common.metrics import payload_logs_by_payload, payload_logs_total
26
+ from pyxecm.customizer.api.common.router import router as common_router
27
+ from pyxecm.customizer.api.settings import api_settings
28
+ from pyxecm.customizer.api.terminal.router import router as terminal_router
29
+ from pyxecm.customizer.api.v1_csai.router import router as v1_csai_router
30
+ from pyxecm.customizer.api.v1_maintenance.router import router as v1_maintenance_router
31
+ from pyxecm.customizer.api.v1_otcs.router import router as v1_otcs_router
32
+ from pyxecm.customizer.api.v1_payload.functions import import_payload
33
+ from pyxecm.customizer.api.v1_payload.router import router as v1_payload_router
34
+ from pyxecm.maintenance_page import run_maintenance_page
35
+
36
+ # Check if Temp dir exists
37
+ if not os.path.exists(api_settings.temp_dir):
38
+ os.makedirs(api_settings.temp_dir)
39
+
40
+ # Check if Logfile and folder exists and is unique
41
+ if os.path.isfile(os.path.join(api_settings.logfolder, api_settings.logfile)):
42
+ customizer_start_time = datetime.now(timezone.utc).strftime(
43
+ "%Y-%m-%d_%H-%M",
44
+ )
45
+ api_settings.logfile = f"customizer_{customizer_start_time}.log"
46
+ elif not os.path.exists(api_settings.logfolder):
47
+ os.makedirs(api_settings.logfolder)
48
+
49
+ handlers = [
50
+ logging.FileHandler(os.path.join(api_settings.logfolder, api_settings.logfile)),
51
+ logging.StreamHandler(sys.stdout),
52
+ ]
53
+
54
+ logging.basicConfig(
55
+ format="%(asctime)s %(levelname)s [%(name)s] [%(threadName)s] %(message)s",
56
+ datefmt="%d-%b-%Y %H:%M:%S",
57
+ level=api_settings.loglevel,
58
+ handlers=handlers,
59
+ )
60
+
61
+
62
+ @asynccontextmanager
63
+ async def lifespan(
64
+ app: FastAPI, # noqa: ARG001
65
+ ) -> AsyncGenerator:
66
+ """Lifespan Method for FASTAPI to handle the startup and shutdown process.
67
+
68
+ Args:
69
+ app (FastAPI):
70
+ The application.
71
+
72
+ """
73
+
74
+ logger.debug("Settings -> %s", api_settings)
75
+
76
+ if api_settings.import_payload:
77
+ logger.info("Importing filesystem payloads...")
78
+
79
+ # Base Payload
80
+ import_payload(payload=api_settings.payload)
81
+
82
+ # External Payload
83
+ import_payload(payload_dir=api_settings.payload_dir, dependencies=True)
84
+
85
+ # Optional Payload
86
+ import_payload(payload_dir=api_settings.payload_dir_optional)
87
+
88
+ if api_settings.maintenance_mode:
89
+ logger.info("Starting maintenance_page thread...")
90
+ maint_thread = Thread(target=run_maintenance_page, name="maintenance_page")
91
+ maint_thread.start()
92
+
93
+ logger.info("Starting processing thread...")
94
+ thread = Thread(
95
+ target=PAYLOAD_LIST.run_payload_processing,
96
+ name="customization_run_api",
97
+ )
98
+ thread.start()
99
+
100
+ yield
101
+ logger.info("Shutdown")
102
+ PAYLOAD_LIST.stop_payload_processing()
103
+
104
+
105
+ app = FastAPI(
106
+ docs_url="/api",
107
+ title="Customizer API",
108
+ openapi_url="/api/openapi.json",
109
+ lifespan=lifespan,
110
+ version=version("pyxecm"),
111
+ openapi_tags=[
112
+ {
113
+ "name": "auth",
114
+ "description": "Authentication Endpoint - Users are authenticated against Opentext Directory Services",
115
+ },
116
+ {
117
+ "name": "payload",
118
+ "description": "Get status and manipulate payload objects ",
119
+ },
120
+ {
121
+ "name": "maintenance",
122
+ "description": "Enable, disable or alter the maintenance mode.",
123
+ },
124
+ ],
125
+ )
126
+
127
+ ## Add all Routers
128
+ app.include_router(router=common_router)
129
+ app.include_router(router=auth_router)
130
+ app.include_router(router=v1_maintenance_router)
131
+ app.include_router(router=v1_otcs_router)
132
+ app.include_router(router=v1_payload_router)
133
+ if api_settings.ws_terminal:
134
+ app.include_router(router=terminal_router)
135
+ if api_settings.csai:
136
+ app.include_router(router=v1_csai_router)
137
+
138
+
139
+ logger = logging.getLogger("CustomizerAPI")
140
+ app.add_middleware(
141
+ CORSMiddleware,
142
+ allow_origins=api_settings.trusted_origins,
143
+ allow_credentials=True,
144
+ allow_methods=["*"],
145
+ allow_headers=["*"],
146
+ )
147
+
148
+ if api_settings.metrics:
149
+ # Add Prometheus Instrumentator for /metrics
150
+ instrumentator = Instrumentator().instrument(app).expose(app)
151
+ instrumentator.add(payload_logs_by_payload(PAYLOAD_LIST))
152
+ instrumentator.add(payload_logs_total(PAYLOAD_LIST))
153
+
154
+
155
+ def run_api() -> None:
156
+ """Start the FASTAPI Webserver."""
157
+
158
+ uvicorn.run(
159
+ "pyxecm.customizer.api:app",
160
+ host=api_settings.bind_address,
161
+ port=api_settings.bind_port,
162
+ workers=api_settings.workers,
163
+ )
@@ -0,0 +1 @@
1
+ """init module."""
@@ -0,0 +1,92 @@
1
+ """Utility library to handle the authentication with OTDS."""
2
+
3
+ import json
4
+ from typing import Annotated
5
+
6
+ import requests
7
+ from fastapi import APIRouter, Depends, HTTPException, status
8
+ from fastapi.security import OAuth2PasswordBearer
9
+
10
+ from pyxecm.customizer.api.auth.models import User
11
+ from pyxecm.customizer.api.settings import api_settings
12
+
13
+ router = APIRouter()
14
+
15
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
16
+
17
+
18
+ def get_groups(response: dict, token: str) -> list:
19
+ """Get the groups of the user.
20
+
21
+ Args:
22
+ response (_type_): _description_
23
+ token (_type_): _description_
24
+
25
+ Returns:
26
+ list: _description_
27
+
28
+ """
29
+
30
+ headers = {
31
+ "Accept": "application/json",
32
+ "otdsticket": token,
33
+ }
34
+ url = api_settings.otds_url + "/otdsws/rest/users/" + response["user"]["id"] + "/memberof"
35
+
36
+ response = requests.request("GET", url, headers=headers, timeout=5)
37
+ if response.ok:
38
+ response = json.loads(response.text)
39
+ return [group["id"] for group in response.get("groups", [])]
40
+
41
+ # Retur empty list if request wasn't successful
42
+ return []
43
+
44
+
45
+ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> User:
46
+ """Get the current user from OTDS and verify it."""
47
+
48
+ if api_settings.api_token is not None and token == api_settings.api_token:
49
+ return User(
50
+ id="api",
51
+ full_name="API Token",
52
+ groups=["otadmins@otds.admin"],
53
+ is_admin=True,
54
+ is_sysadmin=True,
55
+ )
56
+
57
+ url = api_settings.otds_url + "/otdsws/rest/currentuser"
58
+ headers = {
59
+ "Accept": "application/json",
60
+ "otdsticket": token,
61
+ }
62
+ response = requests.request("GET", url, headers=headers, timeout=2)
63
+
64
+ if response.ok:
65
+ response = json.loads(response.text)
66
+
67
+ user = User(
68
+ id=response["user"]["id"],
69
+ full_name=response["user"]["name"],
70
+ groups=get_groups(response, token),
71
+ is_admin=response["isAdmin"],
72
+ is_sysadmin=response["isSysAdmin"],
73
+ )
74
+
75
+ return user
76
+ else:
77
+ raise HTTPException(
78
+ status_code=status.HTTP_401_UNAUTHORIZED,
79
+ detail="Invalid authentication credentials",
80
+ headers={"WWW-Authenticate": "Bearer"},
81
+ )
82
+
83
+
84
+ async def get_authorized_user(current_user: Annotated[User, Depends(get_current_user)]) -> User:
85
+ """Check if the user is authorized (member of the Group otadmin@otds.admin)."""
86
+
87
+ if "otadmins@otds.admin" not in current_user.groups:
88
+ raise HTTPException(
89
+ status_code=status.HTTP_403_FORBIDDEN,
90
+ detail=f"User {current_user.id} is not authorized",
91
+ )
92
+ return current_user
@@ -0,0 +1,13 @@
1
+ """Models for FastAPI."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class User(BaseModel):
7
+ """Model for users authenticated by OTDS."""
8
+
9
+ id: str
10
+ full_name: str | None = None
11
+ groups: list[str] | None = None
12
+ is_admin: bool = False
13
+ is_sysadmin: bool = False
@@ -0,0 +1,78 @@
1
+ """Utility library to handle the authentication with OTDS."""
2
+
3
+ import json
4
+ from typing import Annotated
5
+
6
+ import requests
7
+ from fastapi import APIRouter, Depends, HTTPException
8
+ from fastapi.responses import JSONResponse
9
+ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
10
+
11
+ from pyxecm.customizer.api.auth.functions import get_current_user
12
+ from pyxecm.customizer.api.auth.models import User
13
+ from pyxecm.customizer.api.settings import api_settings
14
+
15
+ router = APIRouter()
16
+
17
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
18
+
19
+
20
+ @router.post("/token", tags=["auth"])
21
+ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> JSONResponse:
22
+ """Login using OTDS and return a token."""
23
+
24
+ url = api_settings.otds_url + "/otdsws/rest/authentication/credentials"
25
+
26
+ payload = json.dumps(
27
+ {"userName": form_data.username, "password": form_data.password},
28
+ )
29
+ headers = {
30
+ "Content-Type": "application/json",
31
+ "Accept": "application/json",
32
+ }
33
+
34
+ try:
35
+ response = requests.request(
36
+ "POST",
37
+ url,
38
+ headers=headers,
39
+ data=payload,
40
+ timeout=10,
41
+ )
42
+ except requests.exceptions.ConnectionError as exc:
43
+ raise HTTPException(
44
+ status_code=500,
45
+ detail=f"{exc.request.url} cannot be reached",
46
+ ) from exc
47
+
48
+ if response.ok:
49
+ response = json.loads(response.text)
50
+ else:
51
+ raise HTTPException(status_code=400, detail="Incorrect username or password")
52
+
53
+ return JSONResponse(
54
+ {
55
+ "access_token": response["ticket"],
56
+ "token_type": "bearer",
57
+ "userId": response["userId"],
58
+ },
59
+ )
60
+
61
+
62
+ @router.get("/users/me", tags=["auth"])
63
+ async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]) -> JSONResponse:
64
+ """Get the current user.
65
+
66
+ current_user:
67
+ type: User
68
+ description: The current user.
69
+
70
+ """
71
+
72
+ if "otadmins@otds.admin" in current_user.groups:
73
+ return JSONResponse(current_user.model_dump())
74
+ else:
75
+ raise HTTPException(
76
+ status_code=403,
77
+ detail=f"User {current_user.id} is not authorized",
78
+ )
@@ -0,0 +1 @@
1
+ """init module."""
@@ -0,0 +1,47 @@
1
+ """Define common functions."""
2
+
3
+ import logging
4
+
5
+ from pyxecm.customizer.api.common.payload_list import PayloadList
6
+ from pyxecm.customizer.api.settings import CustomizerAPISettings, api_settings
7
+ from pyxecm.customizer.k8s import K8s
8
+
9
+ logger = logging.getLogger("pyxecm.customizer.api")
10
+
11
+ # Create a LOCK dict for singleton logs collection
12
+ LOGS_LOCK = {}
13
+ # Initialize the globel Payloadlist object
14
+ PAYLOAD_LIST = PayloadList(logger=logger)
15
+
16
+
17
+ def get_k8s_object() -> K8s:
18
+ """Get an instance of a K8s object.
19
+
20
+ Returns:
21
+ K8s: Return a K8s object
22
+
23
+ """
24
+
25
+ return K8s(logger=logger, namespace=api_settings.namespace)
26
+
27
+
28
+ def get_settings() -> CustomizerAPISettings:
29
+ """Get the API Settings object.
30
+
31
+ Returns:
32
+ CustomizerPISettings: Returns the API Settings
33
+
34
+ """
35
+
36
+ return api_settings
37
+
38
+
39
+ def get_otcs_logs_lock() -> dict:
40
+ """Get the Logs LOCK dict.
41
+
42
+ Returns:
43
+ The dict with all LOCKS for the logs
44
+
45
+ """
46
+
47
+ return LOGS_LOCK
@@ -0,0 +1,92 @@
1
+ """Metics for payload logs."""
2
+
3
+ from collections.abc import Callable
4
+
5
+ from prometheus_client import Gauge
6
+ from prometheus_fastapi_instrumentator.metrics import Info
7
+
8
+ from pyxecm.customizer.api.common.payload_list import PayloadList
9
+
10
+
11
+ ## By Payload
12
+ def payload_logs_by_payload(payload_list: PayloadList) -> Callable[[Info], None]:
13
+ """Metrics for payload logs by payload."""
14
+
15
+ metrics_error = Gauge(
16
+ "payload_error",
17
+ "Number of ERROR log messages for by payload",
18
+ labelnames=("index", "name", "logfile"),
19
+ )
20
+
21
+ metrics_warning = Gauge(
22
+ "payload_warning",
23
+ "Number of WARNING log messages for by payload",
24
+ labelnames=("index", "name", "logfile"),
25
+ )
26
+
27
+ metrics_info = Gauge(
28
+ "payload_info",
29
+ "Number of INFO log messages for by payload",
30
+ labelnames=("index", "name", "logfile"),
31
+ )
32
+
33
+ metrics_debug = Gauge(
34
+ "payload_debug",
35
+ "Number of DEBUG log messages for by payload",
36
+ labelnames=("index", "name", "logfile"),
37
+ )
38
+
39
+ def instrumentation(info: Info) -> None: # noqa: ARG001
40
+ df = payload_list.get_payload_items()
41
+ data = [{"index": idx, **row} for idx, row in df.iterrows()]
42
+
43
+ for item in data:
44
+ metrics_error.labels(item["index"], item["name"], item["logfile"]).set(
45
+ item["log_error"],
46
+ )
47
+ metrics_warning.labels(item["index"], item["name"], item["logfile"]).set(
48
+ item["log_warning"],
49
+ )
50
+ metrics_info.labels(item["index"], item["name"], item["logfile"]).set(
51
+ item["log_info"],
52
+ )
53
+ metrics_debug.labels(item["index"], item["name"], item["logfile"]).set(
54
+ item["log_debug"],
55
+ )
56
+
57
+ return instrumentation
58
+
59
+
60
+ ## Total
61
+ def payload_logs_total(payload_list: PayloadList) -> Callable[[Info], None]:
62
+ """Metrics for total payload logs messages."""
63
+
64
+ metrics_error = Gauge(
65
+ "payload_error_total",
66
+ "Total number of ERROR log messages",
67
+ )
68
+
69
+ metrics_warning = Gauge(
70
+ "payload_warning_total",
71
+ "Total number of WARNING log messages",
72
+ )
73
+
74
+ metrics_info = Gauge(
75
+ "payload_info_total",
76
+ "Total number of INFO log messages",
77
+ )
78
+
79
+ metrics_debug = Gauge(
80
+ "payload_debug_total",
81
+ "Total number of DEBUG log messages",
82
+ )
83
+
84
+ def instrumentation(info: Info) -> None: # noqa: ARG001
85
+ df = payload_list.get_payload_items()
86
+
87
+ metrics_error.set(df["log_error"].sum())
88
+ metrics_warning.set(df["log_warning"].sum())
89
+ metrics_info.set(df["log_info"].sum())
90
+ metrics_debug.set(df["log_debug"].sum())
91
+
92
+ return instrumentation
@@ -0,0 +1,21 @@
1
+ """Define common base Models."""
2
+
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class CustomizerStatus(BaseModel):
9
+ """Define Model for Customizer Status."""
10
+
11
+ version: int = 2
12
+ customizer_duration: Any | None
13
+ customizer_end_time: Any | None
14
+ customizer_start_time: Any | None
15
+ status_details: dict
16
+ status: str = "Stopped"
17
+ debug: int = 0
18
+ info: int = 0
19
+ warning: int = 0
20
+ error: int = 0
21
+ critical: int = 0