pyxecm 3.0.1__py3-none-any.whl → 3.1.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.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/avts.py +4 -4
- pyxecm/coreshare.py +14 -15
- pyxecm/helper/data.py +2 -1
- pyxecm/helper/web.py +11 -11
- pyxecm/helper/xml.py +41 -10
- pyxecm/otac.py +1 -1
- pyxecm/otawp.py +19 -19
- pyxecm/otca.py +870 -67
- pyxecm/otcs.py +1567 -280
- pyxecm/otds.py +332 -153
- pyxecm/otkd.py +4 -4
- pyxecm/otmm.py +1 -1
- pyxecm/otpd.py +246 -30
- {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/METADATA +2 -1
- pyxecm-3.1.0.dist-info/RECORD +82 -0
- pyxecm_api/app.py +45 -35
- pyxecm_api/auth/functions.py +2 -2
- pyxecm_api/auth/router.py +2 -3
- pyxecm_api/common/functions.py +164 -12
- pyxecm_api/settings.py +0 -8
- pyxecm_api/terminal/router.py +1 -1
- pyxecm_api/v1_csai/router.py +33 -18
- pyxecm_customizer/browser_automation.py +98 -48
- pyxecm_customizer/customizer.py +43 -25
- pyxecm_customizer/guidewire.py +422 -8
- pyxecm_customizer/k8s.py +23 -27
- pyxecm_customizer/knowledge_graph.py +501 -20
- pyxecm_customizer/m365.py +45 -44
- pyxecm_customizer/payload.py +1684 -1159
- pyxecm_customizer/payload_list.py +3 -0
- pyxecm_customizer/salesforce.py +122 -79
- pyxecm_customizer/servicenow.py +27 -7
- pyxecm_customizer/settings.py +3 -1
- pyxecm_customizer/successfactors.py +2 -2
- pyxecm_customizer/translate.py +1 -1
- pyxecm-3.0.1.dist-info/RECORD +0 -96
- pyxecm_api/agents/__init__.py +0 -7
- pyxecm_api/agents/app.py +0 -13
- pyxecm_api/agents/functions.py +0 -119
- pyxecm_api/agents/models.py +0 -10
- pyxecm_api/agents/otcm_knowledgegraph/__init__.py +0 -1
- pyxecm_api/agents/otcm_knowledgegraph/functions.py +0 -85
- pyxecm_api/agents/otcm_knowledgegraph/models.py +0 -61
- pyxecm_api/agents/otcm_knowledgegraph/router.py +0 -74
- pyxecm_api/agents/otcm_user_agent/__init__.py +0 -1
- pyxecm_api/agents/otcm_user_agent/models.py +0 -20
- pyxecm_api/agents/otcm_user_agent/router.py +0 -65
- pyxecm_api/agents/otcm_workspace_agent/__init__.py +0 -1
- pyxecm_api/agents/otcm_workspace_agent/models.py +0 -40
- pyxecm_api/agents/otcm_workspace_agent/router.py +0 -200
- {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/WHEEL +0 -0
- {pyxecm-3.0.1.dist-info → pyxecm-3.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -90,8 +90,8 @@ except ModuleNotFoundError:
|
|
|
90
90
|
# "wait until" strategy.
|
|
91
91
|
DEFAULT_WAIT_UNTIL_STRATEGY = "networkidle"
|
|
92
92
|
|
|
93
|
-
REQUEST_TIMEOUT = 30
|
|
94
|
-
REQUEST_RETRY_DELAY = 2
|
|
93
|
+
REQUEST_TIMEOUT = 30.0
|
|
94
|
+
REQUEST_RETRY_DELAY = 2.0
|
|
95
95
|
REQUEST_MAX_RETRIES = 3
|
|
96
96
|
|
|
97
97
|
|
|
@@ -182,7 +182,7 @@ class BrowserAutomation:
|
|
|
182
182
|
self.screenshot_names,
|
|
183
183
|
"screenshots",
|
|
184
184
|
)
|
|
185
|
-
self.logger.debug("Creating
|
|
185
|
+
self.logger.debug("Creating screenshot directory... -> %s", self.screenshot_directory)
|
|
186
186
|
if self.take_screenshots and not os.path.exists(self.screenshot_directory):
|
|
187
187
|
os.makedirs(self.screenshot_directory)
|
|
188
188
|
|
|
@@ -194,21 +194,21 @@ class BrowserAutomation:
|
|
|
194
194
|
self.logger.info("Using HTTP proxy -> %s", os.getenv("HTTP_PROXY"))
|
|
195
195
|
|
|
196
196
|
browser = browser or os.getenv("BROWSER", "webkit")
|
|
197
|
-
self.logger.info("Using
|
|
197
|
+
self.logger.info("Using browser -> '%s'...", browser)
|
|
198
198
|
|
|
199
199
|
if not self.setup_playwright(browser=browser):
|
|
200
200
|
self.logger.error("Failed to initialize Playwright browser automation!")
|
|
201
201
|
return
|
|
202
202
|
|
|
203
|
-
self.logger.info("Creating
|
|
203
|
+
self.logger.info("Creating browser context...")
|
|
204
204
|
self.context: BrowserContext = self.browser.new_context(
|
|
205
205
|
accept_downloads=True,
|
|
206
206
|
)
|
|
207
207
|
|
|
208
|
-
self.logger.info("Creating
|
|
208
|
+
self.logger.info("Creating page...")
|
|
209
209
|
self.page: Page = self.context.new_page()
|
|
210
210
|
self.main_page = self.page
|
|
211
|
-
self.logger.info("Browser
|
|
211
|
+
self.logger.info("Browser automation initialized.")
|
|
212
212
|
|
|
213
213
|
# end method definition
|
|
214
214
|
|
|
@@ -217,7 +217,12 @@ class BrowserAutomation:
|
|
|
217
217
|
|
|
218
218
|
Args:
|
|
219
219
|
browser (str):
|
|
220
|
-
Name of the browser engine.
|
|
220
|
+
Name of the browser engine. Supported:
|
|
221
|
+
* chromium
|
|
222
|
+
* chrome
|
|
223
|
+
* msedge
|
|
224
|
+
* webkit
|
|
225
|
+
* firefox
|
|
221
226
|
|
|
222
227
|
Returns:
|
|
223
228
|
bool:
|
|
@@ -232,6 +237,9 @@ class BrowserAutomation:
|
|
|
232
237
|
self.logger.error("Failed to start Playwright!")
|
|
233
238
|
return False
|
|
234
239
|
|
|
240
|
+
result = True
|
|
241
|
+
|
|
242
|
+
# Install and launch the selected browser in Playwright:
|
|
235
243
|
match browser:
|
|
236
244
|
case "chromium":
|
|
237
245
|
try:
|
|
@@ -239,10 +247,11 @@ class BrowserAutomation:
|
|
|
239
247
|
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
240
248
|
)
|
|
241
249
|
except Exception:
|
|
242
|
-
self.install_browser(browser=browser)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
result = self.install_browser(browser=browser)
|
|
251
|
+
if result:
|
|
252
|
+
self.browser: Browser = self.playwright.chromium.launch(
|
|
253
|
+
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
254
|
+
)
|
|
246
255
|
|
|
247
256
|
case "chrome":
|
|
248
257
|
try:
|
|
@@ -253,13 +262,14 @@ class BrowserAutomation:
|
|
|
253
262
|
proxy=self.proxy,
|
|
254
263
|
)
|
|
255
264
|
except Exception:
|
|
256
|
-
self.install_browser(browser=browser)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
265
|
+
result = self.install_browser(browser=browser)
|
|
266
|
+
if result:
|
|
267
|
+
self.browser: Browser = self.playwright.chromium.launch(
|
|
268
|
+
channel="chrome",
|
|
269
|
+
headless=self.headless,
|
|
270
|
+
slow_mo=100 if not self.headless else None,
|
|
271
|
+
proxy=self.proxy,
|
|
272
|
+
)
|
|
263
273
|
|
|
264
274
|
case "msedge":
|
|
265
275
|
try:
|
|
@@ -270,13 +280,14 @@ class BrowserAutomation:
|
|
|
270
280
|
proxy=self.proxy,
|
|
271
281
|
)
|
|
272
282
|
except Exception:
|
|
273
|
-
self.install_browser(browser=browser)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
283
|
+
result = self.install_browser(browser=browser)
|
|
284
|
+
if result:
|
|
285
|
+
self.browser: Browser = self.playwright.chromium.launch(
|
|
286
|
+
channel="msedge",
|
|
287
|
+
headless=self.headless,
|
|
288
|
+
slow_mo=100 if not self.headless else None,
|
|
289
|
+
proxy=self.proxy,
|
|
290
|
+
)
|
|
280
291
|
|
|
281
292
|
case "webkit":
|
|
282
293
|
try:
|
|
@@ -284,10 +295,11 @@ class BrowserAutomation:
|
|
|
284
295
|
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
285
296
|
)
|
|
286
297
|
except Exception:
|
|
287
|
-
self.install_browser(browser=browser)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
298
|
+
result = self.install_browser(browser=browser)
|
|
299
|
+
if result:
|
|
300
|
+
self.browser: Browser = self.playwright.webkit.launch(
|
|
301
|
+
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
302
|
+
)
|
|
291
303
|
|
|
292
304
|
case "firefox":
|
|
293
305
|
try:
|
|
@@ -295,16 +307,30 @@ class BrowserAutomation:
|
|
|
295
307
|
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
296
308
|
)
|
|
297
309
|
except Exception:
|
|
298
|
-
self.install_browser(browser=browser)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
result = self.install_browser(browser=browser)
|
|
311
|
+
if result:
|
|
312
|
+
self.browser: Browser = self.playwright.firefox.launch(
|
|
313
|
+
headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
|
|
314
|
+
)
|
|
315
|
+
case _:
|
|
316
|
+
self.logger.error("Unknown browser -> '%s'. Cannot install and launch it.", browser)
|
|
317
|
+
result = False
|
|
318
|
+
|
|
319
|
+
return result
|
|
303
320
|
|
|
304
321
|
# end method definition
|
|
305
322
|
|
|
306
323
|
def install_browser(self, browser: str) -> bool:
|
|
307
|
-
"""
|
|
324
|
+
"""Install a browser with a provided name in Playwright.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
browser (str):
|
|
328
|
+
Name of the browser to be installed.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
bool: True = installation successful, False = installation failed.
|
|
332
|
+
|
|
333
|
+
"""
|
|
308
334
|
|
|
309
335
|
self.logger.info("Installing Browser -> '%s'...", browser)
|
|
310
336
|
process = subprocess.Popen(
|
|
@@ -314,12 +340,13 @@ class BrowserAutomation:
|
|
|
314
340
|
shell=False,
|
|
315
341
|
)
|
|
316
342
|
output, error = process.communicate()
|
|
317
|
-
if process.returncode == 0:
|
|
318
|
-
self.logger.info("
|
|
343
|
+
if process.returncode == 0: # 0 = success
|
|
344
|
+
self.logger.info("Successfuly completed installation of browser -> '%s'.", browser)
|
|
319
345
|
self.logger.debug(output.decode())
|
|
320
346
|
else:
|
|
321
|
-
self.logger.error("Installation failed
|
|
347
|
+
self.logger.error("Installation of browser -> '%s' failed! Error -> %s", browser, error.decode())
|
|
322
348
|
self.logger.error(output.decode())
|
|
349
|
+
return False
|
|
323
350
|
|
|
324
351
|
return True
|
|
325
352
|
|
|
@@ -634,6 +661,7 @@ class BrowserAutomation:
|
|
|
634
661
|
wait_state: str = "visible",
|
|
635
662
|
exact_match: bool | None = None,
|
|
636
663
|
regex: bool = False,
|
|
664
|
+
occurrence: int = 1,
|
|
637
665
|
iframe: str | None = None,
|
|
638
666
|
repeat_reload: int | None = None,
|
|
639
667
|
repeat_reload_delay: int = 60,
|
|
@@ -657,6 +685,9 @@ class BrowserAutomation:
|
|
|
657
685
|
If an exact matching is required. Default is None (not set).
|
|
658
686
|
regex (bool, optional):
|
|
659
687
|
Should the name be interpreted as a regular expression?
|
|
688
|
+
occurrence (int, optional):
|
|
689
|
+
If multiple elements match the selector, this defines which one to return.
|
|
690
|
+
Default is 1 (the first one).
|
|
660
691
|
iframe (str | None):
|
|
661
692
|
Is the element in an iFrame? Then provide the name of the iframe with this parameter.
|
|
662
693
|
repeat_reload (int | None):
|
|
@@ -674,17 +705,19 @@ class BrowserAutomation:
|
|
|
674
705
|
|
|
675
706
|
"""
|
|
676
707
|
|
|
677
|
-
failure_message = "Cannot find page element with selector -> '{}' ({}){}{}".format(
|
|
708
|
+
failure_message = "Cannot find page element with selector -> '{}' ({}){}{}{}".format(
|
|
678
709
|
selector,
|
|
679
710
|
selector_type,
|
|
680
711
|
" and role type -> '{}'".format(role_type) if role_type else "",
|
|
681
712
|
" in iframe -> '{}'".format(iframe) if iframe else "",
|
|
713
|
+
", occurrence -> {}".format(occurrence) if occurrence > 1 else "",
|
|
682
714
|
)
|
|
683
|
-
success_message = "Found page element with selector -> '{}' ('{}'){}{}".format(
|
|
715
|
+
success_message = "Found page element with selector -> '{}' ('{}'){}{}{}".format(
|
|
684
716
|
selector,
|
|
685
717
|
selector_type,
|
|
686
718
|
" and role type -> '{}'".format(role_type) if role_type else "",
|
|
687
719
|
" in iframe -> '{}'".format(iframe) if iframe else "",
|
|
720
|
+
", occurrence -> {}".format(occurrence) if occurrence > 1 else "",
|
|
688
721
|
)
|
|
689
722
|
|
|
690
723
|
def do_find() -> Locator | None:
|
|
@@ -709,8 +742,13 @@ class BrowserAutomation:
|
|
|
709
742
|
# are not yet loaded:
|
|
710
743
|
|
|
711
744
|
try:
|
|
745
|
+
index = occurrence - 1 # convert to 0-based index
|
|
746
|
+
if index < 0: # basic validation
|
|
747
|
+
self.logger.error("Occurrence must be >= 1")
|
|
748
|
+
return None
|
|
712
749
|
self.logger.debug(
|
|
713
|
-
"Wait for locator to find
|
|
750
|
+
"Wait for locator to find %selement with selector -> '%s' (%s%s%s) and state -> '%s'%s...",
|
|
751
|
+
"occurrence #{} of ".format(occurrence) if occurrence > 1 else "",
|
|
714
752
|
selector,
|
|
715
753
|
"selector type -> '{}'".format(selector_type),
|
|
716
754
|
", role type -> '{}'".format(role_type) if role_type else "",
|
|
@@ -718,7 +756,9 @@ class BrowserAutomation:
|
|
|
718
756
|
wait_state,
|
|
719
757
|
" in iframe -> '{}'".format(iframe) if iframe else "",
|
|
720
758
|
)
|
|
721
|
-
|
|
759
|
+
|
|
760
|
+
locator = locator.first if occurrence == 1 else locator.nth(index)
|
|
761
|
+
# Wait for the element to be in the desired state:
|
|
722
762
|
locator.wait_for(state=wait_state)
|
|
723
763
|
except PlaywrightError as pe:
|
|
724
764
|
if show_error and repeat_reload is None:
|
|
@@ -763,6 +803,7 @@ class BrowserAutomation:
|
|
|
763
803
|
selector: str,
|
|
764
804
|
selector_type: str = "id",
|
|
765
805
|
role_type: str | None = None,
|
|
806
|
+
occurrence: int = 1,
|
|
766
807
|
scroll_to_element: bool = True,
|
|
767
808
|
desired_checkbox_state: bool | None = None,
|
|
768
809
|
is_navigation_trigger: bool = False,
|
|
@@ -793,6 +834,9 @@ class BrowserAutomation:
|
|
|
793
834
|
role_type (str | None, optional):
|
|
794
835
|
ARIA role when using selector_type="role", e.g., "button", "textbox".
|
|
795
836
|
If irrelevant then None should be passed for role_type.
|
|
837
|
+
occurrence (int, optional):
|
|
838
|
+
If multiple elements match the selector, this defines which one to return.
|
|
839
|
+
Default is 1 (the first one).
|
|
796
840
|
scroll_to_element (bool, optional):
|
|
797
841
|
Scroll the element into view.
|
|
798
842
|
desired_checkbox_state (bool | None, optional):
|
|
@@ -879,6 +923,7 @@ class BrowserAutomation:
|
|
|
879
923
|
role_type=role_type,
|
|
880
924
|
exact_match=exact_match,
|
|
881
925
|
regex=regex,
|
|
926
|
+
occurrence=occurrence,
|
|
882
927
|
iframe=iframe,
|
|
883
928
|
repeat_reload=repeat_reload,
|
|
884
929
|
repeat_reload_delay=repeat_reload_delay,
|
|
@@ -977,6 +1022,7 @@ class BrowserAutomation:
|
|
|
977
1022
|
value: str | bool,
|
|
978
1023
|
selector_type: str = "id",
|
|
979
1024
|
role_type: str | None = None,
|
|
1025
|
+
occurrence: int = 1,
|
|
980
1026
|
is_sensitive: bool = False,
|
|
981
1027
|
press_enter: bool = False,
|
|
982
1028
|
exact_match: bool | None = None,
|
|
@@ -998,6 +1044,9 @@ class BrowserAutomation:
|
|
|
998
1044
|
role_type (str | None, optional):
|
|
999
1045
|
ARIA role when using selector_type="role", e.g., "button", "textbox".
|
|
1000
1046
|
If irrelevant then None should be passed for role_type.
|
|
1047
|
+
occurrence (int, optional):
|
|
1048
|
+
If multiple elements match the selector, this defines which one to return.
|
|
1049
|
+
Default is 1 (the first one).
|
|
1001
1050
|
is_sensitive (bool, optional):
|
|
1002
1051
|
True for suppressing sensitive information in logging.
|
|
1003
1052
|
press_enter (bool, optional):
|
|
@@ -1028,6 +1077,7 @@ class BrowserAutomation:
|
|
|
1028
1077
|
role_type=role_type,
|
|
1029
1078
|
exact_match=exact_match,
|
|
1030
1079
|
regex=regex,
|
|
1080
|
+
occurrence=occurrence,
|
|
1031
1081
|
iframe=iframe,
|
|
1032
1082
|
show_error=True,
|
|
1033
1083
|
)
|
|
@@ -1263,7 +1313,7 @@ class BrowserAutomation:
|
|
|
1263
1313
|
return (None, 0)
|
|
1264
1314
|
|
|
1265
1315
|
self.logger.info(
|
|
1266
|
-
"Check if at least %d element%s found by selector -> %s (%s%s)%s%s%s...",
|
|
1316
|
+
"Check if at least %d element%s found by selector -> '%s' (%s%s)%s%s%s...",
|
|
1267
1317
|
min_count,
|
|
1268
1318
|
"s are" if min_count > 1 else " is",
|
|
1269
1319
|
selector,
|
|
@@ -1467,7 +1517,7 @@ class BrowserAutomation:
|
|
|
1467
1517
|
return False
|
|
1468
1518
|
|
|
1469
1519
|
if "Verify" in title:
|
|
1470
|
-
self.logger.error("Site is asking for a
|
|
1520
|
+
self.logger.error("Site is asking for a verification token. You may need to whitelist your IP!")
|
|
1471
1521
|
return False
|
|
1472
1522
|
if "Login" in title:
|
|
1473
1523
|
self.logger.error("Authentication failed. You may have given the wrong password!")
|
|
@@ -1502,11 +1552,11 @@ class BrowserAutomation:
|
|
|
1502
1552
|
def end_session(self) -> None:
|
|
1503
1553
|
"""End the browser session and close the browser."""
|
|
1504
1554
|
|
|
1505
|
-
self.logger.info("Close
|
|
1555
|
+
self.logger.info("Close browser page...")
|
|
1506
1556
|
self.page.close()
|
|
1507
|
-
self.logger.info("Close
|
|
1557
|
+
self.logger.info("Close browser context...")
|
|
1508
1558
|
self.context.close()
|
|
1509
|
-
self.logger.info("Close
|
|
1559
|
+
self.logger.info("Close browser...")
|
|
1510
1560
|
self.browser.close()
|
|
1511
1561
|
self.logged_in = False
|
|
1512
1562
|
self.logger.info("Stop Playwright instance...")
|
pyxecm_customizer/customizer.py
CHANGED
|
@@ -200,7 +200,7 @@ class Customizer:
|
|
|
200
200
|
# of unsetting 2 checkboxes on that config page - we reset these checkboxes
|
|
201
201
|
# with the settings file "O365Settings.xml"):
|
|
202
202
|
file_path = os.path.join(tempfile.gettempdir(), "ot.xecm.teams.zip")
|
|
203
|
-
|
|
203
|
+
_ = self.otcs_frontend_object.download_config_file(
|
|
204
204
|
otcs_url_suffix="/cs/cs?func=officegroups.DownloadTeamsPackage",
|
|
205
205
|
file_path=file_path,
|
|
206
206
|
)
|
|
@@ -257,7 +257,7 @@ class Customizer:
|
|
|
257
257
|
# by its wrong name in the customizer automation. This can
|
|
258
258
|
# happen if the app is installed manually or the environment
|
|
259
259
|
# variable is set to a wrong name.
|
|
260
|
-
app_catalog_name = m365_object.get_result_value(response, "displayName")
|
|
260
|
+
app_catalog_name = m365_object.get_result_value(response=response, key="displayName")
|
|
261
261
|
if app_catalog_name != self.settings.m365.teams_app_name:
|
|
262
262
|
self.logger.warning(
|
|
263
263
|
"The Extended ECM app name -> '%s' in the M365 Teams catalog does not match the defined app name -> '%s'!",
|
|
@@ -624,14 +624,14 @@ class Customizer:
|
|
|
624
624
|
)
|
|
625
625
|
if not otcs_frontend_scale:
|
|
626
626
|
self.logger.error(
|
|
627
|
-
"Cannot find Kubernetes
|
|
627
|
+
"Cannot find Kubernetes stateful set -> '%s' for OTCS Frontends!",
|
|
628
628
|
self.settings.k8s.sts_otcs_frontend,
|
|
629
629
|
)
|
|
630
630
|
sys.exit()
|
|
631
631
|
|
|
632
632
|
self.settings.k8s.sts_otcs_frontend_replicas = otcs_frontend_scale.spec.replicas
|
|
633
633
|
self.logger.info(
|
|
634
|
-
"Stateful
|
|
634
|
+
"Stateful set -> '%s' has -> %s replicas",
|
|
635
635
|
self.settings.k8s.sts_otcs_frontend,
|
|
636
636
|
self.settings.k8s.sts_otcs_frontend_replicas,
|
|
637
637
|
)
|
|
@@ -642,16 +642,17 @@ class Customizer:
|
|
|
642
642
|
)
|
|
643
643
|
if not otcs_backend_scale:
|
|
644
644
|
self.logger.error(
|
|
645
|
-
"Cannot find Kubernetes
|
|
645
|
+
"Cannot find Kubernetes stateful set -> '%s' for OTCS Backends!",
|
|
646
646
|
self.settings.k8s.sts_otcs_admin,
|
|
647
647
|
)
|
|
648
648
|
sys.exit()
|
|
649
649
|
|
|
650
650
|
self.settings.k8s.sts_otcs_admin_replicas = otcs_backend_scale.spec.replicas
|
|
651
651
|
self.logger.info(
|
|
652
|
-
"Stateful
|
|
652
|
+
"Stateful set -> '%s' has -> %s replica%s",
|
|
653
653
|
self.settings.k8s.sts_otcs_admin,
|
|
654
654
|
self.settings.k8s.sts_otcs_admin_replicas,
|
|
655
|
+
"s" if self.settings.k8s.sts_otcs_admin_replicas > 1 else "",
|
|
655
656
|
)
|
|
656
657
|
|
|
657
658
|
return k8s_object
|
|
@@ -782,7 +783,7 @@ class Customizer:
|
|
|
782
783
|
if not response:
|
|
783
784
|
self.logger.error("Failed to enable OTAC certificate for OTCS!")
|
|
784
785
|
else:
|
|
785
|
-
self.logger.info("Successfully enabled OTAC certificate for OTCS
|
|
786
|
+
self.logger.info("Successfully enabled OTAC certificate for OTCS.")
|
|
786
787
|
|
|
787
788
|
# is there a known server configured for Archive Center (to sync content with)
|
|
788
789
|
if otac_object and self.settings.otac.known_server != "":
|
|
@@ -918,7 +919,9 @@ class Customizer:
|
|
|
918
919
|
otcs_resource_id = otcs_resource["resourceID"]
|
|
919
920
|
otcs_object.set_resource_id(resource_id=otcs_resource_id)
|
|
920
921
|
self.logger.info(
|
|
921
|
-
"OTCS has resource
|
|
922
|
+
"OTCS has resource -> '%s' (%s) in OTDS.",
|
|
923
|
+
self.settings.otcs.resource_name,
|
|
924
|
+
otcs_resource_id,
|
|
922
925
|
)
|
|
923
926
|
|
|
924
927
|
if "OTCS_RESSOURCE_ID" not in self.settings.placeholder_values:
|
|
@@ -1257,7 +1260,7 @@ class Customizer:
|
|
|
1257
1260
|
)
|
|
1258
1261
|
while otcs_partition is None:
|
|
1259
1262
|
self.logger.warning(
|
|
1260
|
-
"OTDS user partition
|
|
1263
|
+
"OTDS user partition -> '%s' for Content Server does not exist yet. Waiting...",
|
|
1261
1264
|
self.settings.otcs.partition,
|
|
1262
1265
|
)
|
|
1263
1266
|
|
|
@@ -1350,24 +1353,27 @@ class Customizer:
|
|
|
1350
1353
|
)
|
|
1351
1354
|
if not otcs_da_scale:
|
|
1352
1355
|
self.logger.warning(
|
|
1353
|
-
"Cannot find Kubernetes
|
|
1356
|
+
"Cannot find Kubernetes stateful set -> '%s' for OTCS DA!",
|
|
1354
1357
|
self.settings.k8s.sts_otcs_da,
|
|
1355
1358
|
)
|
|
1356
1359
|
self.settings.k8s.sts_otcs_da_replicas = 0
|
|
1357
1360
|
else:
|
|
1358
1361
|
self.settings.k8s.sts_otcs_da_replicas = otcs_da_scale.spec.replicas
|
|
1359
1362
|
|
|
1363
|
+
if not self.settings.k8s.sts_otcs_da_replicas:
|
|
1364
|
+
self.settings.k8s.sts_otcs_da_replicas = 0
|
|
1365
|
+
|
|
1360
1366
|
# Restart all da:
|
|
1361
1367
|
for x in range(self.settings.k8s.sts_otcs_da_replicas):
|
|
1362
1368
|
pod_name = self.settings.k8s.sts_otcs_da + "-" + str(x)
|
|
1363
1369
|
|
|
1364
|
-
self.logger.info("Deactivate
|
|
1370
|
+
self.logger.info("Deactivate liveness probe for pod -> '%s'...", pod_name)
|
|
1365
1371
|
self.k8s_object.exec_pod_command(
|
|
1366
1372
|
pod_name,
|
|
1367
1373
|
["/bin/sh", "-c", "touch /tmp/keepalive"],
|
|
1368
1374
|
container="otcs-da-container",
|
|
1369
1375
|
)
|
|
1370
|
-
self.logger.info("Restarting OTCS in pod -> '%s'", pod_name)
|
|
1376
|
+
self.logger.info("Restarting OTCS in pod -> '%s'...", pod_name)
|
|
1371
1377
|
self.k8s_object.exec_pod_command(
|
|
1372
1378
|
pod_name,
|
|
1373
1379
|
["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"],
|
|
@@ -1385,24 +1391,27 @@ class Customizer:
|
|
|
1385
1391
|
)
|
|
1386
1392
|
if not otcs_frontend_scale:
|
|
1387
1393
|
self.logger.error(
|
|
1388
|
-
"Cannot find Kubernetes
|
|
1394
|
+
"Cannot find Kubernetes stateful set -> '%s' for OTCS frontends!",
|
|
1389
1395
|
self.settings.k8s.sts_otcs_frontend,
|
|
1390
1396
|
)
|
|
1391
1397
|
self.settings.k8s.sts_otcs_frontend_replicas = 0
|
|
1392
1398
|
else:
|
|
1393
1399
|
self.settings.k8s.sts_otcs_frontend_replicas = otcs_frontend_scale.spec.replicas
|
|
1394
1400
|
|
|
1401
|
+
if not self.settings.k8s.sts_otcs_frontend_replicas:
|
|
1402
|
+
self.settings.k8s.sts_otcs_frontend_replicas = 0
|
|
1403
|
+
|
|
1395
1404
|
# Restart all frontends:
|
|
1396
1405
|
for x in range(self.settings.k8s.sts_otcs_frontend_replicas):
|
|
1397
1406
|
pod_name = self.settings.k8s.sts_otcs_frontend + "-" + str(x)
|
|
1398
1407
|
|
|
1399
|
-
self.logger.info("Deactivate
|
|
1408
|
+
self.logger.info("Deactivate liveness probe for pod -> '%s'...", pod_name)
|
|
1400
1409
|
self.k8s_object.exec_pod_command(
|
|
1401
1410
|
pod_name,
|
|
1402
1411
|
["/bin/sh", "-c", "touch /tmp/keepalive"],
|
|
1403
1412
|
container="otcs-frontend-container",
|
|
1404
1413
|
)
|
|
1405
|
-
self.logger.info("Restarting OTCS in pod -> '%s'", pod_name)
|
|
1414
|
+
self.logger.info("Restarting OTCS in pod -> '%s'...", pod_name)
|
|
1406
1415
|
self.k8s_object.exec_pod_command(
|
|
1407
1416
|
pod_name,
|
|
1408
1417
|
["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"],
|
|
@@ -1418,13 +1427,13 @@ class Customizer:
|
|
|
1418
1427
|
for x in range(self.settings.k8s.sts_otcs_admin_replicas):
|
|
1419
1428
|
pod_name = self.settings.k8s.sts_otcs_admin + "-" + str(x)
|
|
1420
1429
|
|
|
1421
|
-
self.logger.info("Deactivate
|
|
1430
|
+
self.logger.info("Deactivate liveness probe for pod -> '%s'...", pod_name)
|
|
1422
1431
|
self.k8s_object.exec_pod_command(
|
|
1423
1432
|
pod_name,
|
|
1424
1433
|
["/bin/sh", "-c", "touch /tmp/keepalive"],
|
|
1425
1434
|
container="otcs-admin-container",
|
|
1426
1435
|
)
|
|
1427
|
-
self.logger.info("Restarting OTCS in pod -> '%s'", pod_name)
|
|
1436
|
+
self.logger.info("Restarting OTCS in pod -> '%s'...", pod_name)
|
|
1428
1437
|
self.k8s_object.exec_pod_command(
|
|
1429
1438
|
pod_name,
|
|
1430
1439
|
["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"],
|
|
@@ -1438,7 +1447,7 @@ class Customizer:
|
|
|
1438
1447
|
|
|
1439
1448
|
# Reauthenticate at frontend:
|
|
1440
1449
|
self.logger.info(
|
|
1441
|
-
"Re-
|
|
1450
|
+
"Re-authenticating to OTCS frontend after restart of frontend pods...",
|
|
1442
1451
|
)
|
|
1443
1452
|
otcs_cookie = frontend.authenticate(revalidate=True)
|
|
1444
1453
|
while otcs_cookie is None:
|
|
@@ -1449,7 +1458,7 @@ class Customizer:
|
|
|
1449
1458
|
|
|
1450
1459
|
# Reauthenticate at backend:
|
|
1451
1460
|
self.logger.info(
|
|
1452
|
-
"Re-
|
|
1461
|
+
"Re-authenticating to OTCS backend after restart of backend pods...",
|
|
1453
1462
|
)
|
|
1454
1463
|
otcs_cookie = backend.authenticate(revalidate=True)
|
|
1455
1464
|
while otcs_cookie is None:
|
|
@@ -1458,11 +1467,11 @@ class Customizer:
|
|
|
1458
1467
|
otcs_cookie = backend.authenticate(revalidate=True)
|
|
1459
1468
|
self.logger.info("OTCS backend is ready again.")
|
|
1460
1469
|
|
|
1461
|
-
# Reactivate
|
|
1470
|
+
# Reactivate Kubernetes liveness probes in all pods:
|
|
1462
1471
|
for x in range(self.settings.k8s.sts_otcs_frontend_replicas):
|
|
1463
1472
|
pod_name = self.settings.k8s.sts_otcs_frontend + "-" + str(x)
|
|
1464
1473
|
|
|
1465
|
-
self.logger.info("Reactivate
|
|
1474
|
+
self.logger.info("Reactivate liveness probe for pod -> '%s'...", pod_name)
|
|
1466
1475
|
self.k8s_object.exec_pod_command(
|
|
1467
1476
|
pod_name,
|
|
1468
1477
|
["/bin/sh", "-c", "rm /tmp/keepalive"],
|
|
@@ -1472,7 +1481,7 @@ class Customizer:
|
|
|
1472
1481
|
for x in range(self.settings.k8s.sts_otcs_admin_replicas):
|
|
1473
1482
|
pod_name = self.settings.k8s.sts_otcs_admin + "-" + str(x)
|
|
1474
1483
|
|
|
1475
|
-
self.logger.info("Reactivate
|
|
1484
|
+
self.logger.info("Reactivate liveness probe for pod -> '%s'...", pod_name)
|
|
1476
1485
|
self.k8s_object.exec_pod_command(
|
|
1477
1486
|
pod_name,
|
|
1478
1487
|
["/bin/sh", "-c", "rm /tmp/keepalive"],
|
|
@@ -1522,7 +1531,7 @@ class Customizer:
|
|
|
1522
1531
|
# end method definition
|
|
1523
1532
|
|
|
1524
1533
|
def restart_otawp_pod(self) -> None:
|
|
1525
|
-
"""Delete the AppWorks Platform
|
|
1534
|
+
"""Delete the AppWorks Platform pod to make Kubernetes restart it."""
|
|
1526
1535
|
|
|
1527
1536
|
self.k8s_object.delete_pod(self.settings.k8s.sts_otawp + "-0")
|
|
1528
1537
|
|
|
@@ -1899,6 +1908,15 @@ class Customizer:
|
|
|
1899
1908
|
|
|
1900
1909
|
# Upload payload file for later review to Enterprise Workspace
|
|
1901
1910
|
if self.settings.otcs.upload_config_files:
|
|
1911
|
+
# Wait until OTCS is ready to accept uploads. Parallel running
|
|
1912
|
+
# payload processing might be in the process of restarting OTCS:
|
|
1913
|
+
while not self.otcs_backend_object.is_ready():
|
|
1914
|
+
self.logger.info(
|
|
1915
|
+
"OTCS is not ready. Cannot upload payload file -> '%s' to OTCS. Waiting 30 seconds and retry...",
|
|
1916
|
+
os.path.basename(cust_payload),
|
|
1917
|
+
)
|
|
1918
|
+
time.sleep(30)
|
|
1919
|
+
|
|
1902
1920
|
self.log_header("Upload Payload file to OpenText Content Management")
|
|
1903
1921
|
response = self.otcs_backend_object.get_node_from_nickname(
|
|
1904
1922
|
nickname=self.settings.cust_target_folder_nickname,
|
|
@@ -1908,7 +1926,7 @@ class Customizer:
|
|
|
1908
1926
|
key="id",
|
|
1909
1927
|
)
|
|
1910
1928
|
if not target_folder_id:
|
|
1911
|
-
target_folder_id =
|
|
1929
|
+
target_folder_id = 2004 # use Enterprise Workspace as fallback
|
|
1912
1930
|
# Write YAML file with upadated payload (including IDs, etc.).
|
|
1913
1931
|
# We need to write to a temporary location as initial location is read-only:
|
|
1914
1932
|
payload_file = os.path.basename(cust_payload)
|
|
@@ -2017,7 +2035,7 @@ class Customizer:
|
|
|
2017
2035
|
key="id",
|
|
2018
2036
|
)
|
|
2019
2037
|
if not target_folder_id:
|
|
2020
|
-
target_folder_id =
|
|
2038
|
+
target_folder_id = 2004 # use Enterprise Workspace as fallback
|
|
2021
2039
|
# Check if the log file has been uploaded before.
|
|
2022
2040
|
# This can happen if we re-run the python container:
|
|
2023
2041
|
# In this case we add a version to the existing document:
|