pyxecm 3.0.0__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.

Files changed (53) hide show
  1. pyxecm/avts.py +4 -4
  2. pyxecm/coreshare.py +14 -15
  3. pyxecm/helper/data.py +2 -1
  4. pyxecm/helper/web.py +11 -11
  5. pyxecm/helper/xml.py +41 -10
  6. pyxecm/otac.py +1 -1
  7. pyxecm/otawp.py +19 -19
  8. pyxecm/otca.py +870 -67
  9. pyxecm/otcs.py +1567 -280
  10. pyxecm/otds.py +332 -153
  11. pyxecm/otkd.py +4 -4
  12. pyxecm/otmm.py +1 -1
  13. pyxecm/otpd.py +246 -30
  14. pyxecm-3.1.0.dist-info/METADATA +127 -0
  15. pyxecm-3.1.0.dist-info/RECORD +82 -0
  16. pyxecm_api/app.py +45 -35
  17. pyxecm_api/auth/functions.py +2 -2
  18. pyxecm_api/auth/router.py +2 -3
  19. pyxecm_api/common/functions.py +164 -12
  20. pyxecm_api/settings.py +0 -8
  21. pyxecm_api/terminal/router.py +1 -1
  22. pyxecm_api/v1_csai/router.py +33 -18
  23. pyxecm_customizer/browser_automation.py +98 -48
  24. pyxecm_customizer/customizer.py +43 -25
  25. pyxecm_customizer/guidewire.py +422 -8
  26. pyxecm_customizer/k8s.py +23 -27
  27. pyxecm_customizer/knowledge_graph.py +501 -20
  28. pyxecm_customizer/m365.py +45 -44
  29. pyxecm_customizer/payload.py +1684 -1159
  30. pyxecm_customizer/payload_list.py +3 -0
  31. pyxecm_customizer/salesforce.py +122 -79
  32. pyxecm_customizer/servicenow.py +27 -7
  33. pyxecm_customizer/settings.py +3 -1
  34. pyxecm_customizer/successfactors.py +2 -2
  35. pyxecm_customizer/translate.py +1 -1
  36. pyxecm-3.0.0.dist-info/METADATA +0 -48
  37. pyxecm-3.0.0.dist-info/RECORD +0 -96
  38. pyxecm_api/agents/__init__.py +0 -7
  39. pyxecm_api/agents/app.py +0 -13
  40. pyxecm_api/agents/functions.py +0 -119
  41. pyxecm_api/agents/models.py +0 -10
  42. pyxecm_api/agents/otcm_knowledgegraph/__init__.py +0 -1
  43. pyxecm_api/agents/otcm_knowledgegraph/functions.py +0 -85
  44. pyxecm_api/agents/otcm_knowledgegraph/models.py +0 -61
  45. pyxecm_api/agents/otcm_knowledgegraph/router.py +0 -74
  46. pyxecm_api/agents/otcm_user_agent/__init__.py +0 -1
  47. pyxecm_api/agents/otcm_user_agent/models.py +0 -20
  48. pyxecm_api/agents/otcm_user_agent/router.py +0 -65
  49. pyxecm_api/agents/otcm_workspace_agent/__init__.py +0 -1
  50. pyxecm_api/agents/otcm_workspace_agent/models.py +0 -40
  51. pyxecm_api/agents/otcm_workspace_agent/router.py +0 -200
  52. {pyxecm-3.0.0.dist-info → pyxecm-3.1.0.dist-info}/WHEEL +0 -0
  53. {pyxecm-3.0.0.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 Screenshot directory... -> %s", self.screenshot_directory)
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 Browser -> '%s'...", browser)
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 Browser Context...")
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 Page...")
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 Automation initialized.")
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
- self.browser: Browser = self.playwright.chromium.launch(
244
- headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
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
- self.browser: Browser = self.playwright.chromium.launch(
258
- channel="chrome",
259
- headless=self.headless,
260
- slow_mo=100 if not self.headless else None,
261
- proxy=self.proxy,
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
- self.browser: Browser = self.playwright.chromium.launch(
275
- channel="msedge",
276
- headless=self.headless,
277
- slow_mo=100 if not self.headless else None,
278
- proxy=self.proxy,
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
- self.browser: Browser = self.playwright.webkit.launch(
289
- headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
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
- self.browser: Browser = self.playwright.firefox.launch(
300
- headless=self.headless, slow_mo=100 if not self.headless else None, proxy=self.proxy
301
- )
302
- return True
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
- """Check if browser is already installed if not install it."""
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("Installation completed successfullly.")
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 with -> %s", error.decode())
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 element with selector -> '%s' (%s%s%s) and state -> '%s'%s...",
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
- locator = locator.first
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 Verification Token. You may need to whitelist your IP!")
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 Browser Page...")
1555
+ self.logger.info("Close browser page...")
1506
1556
  self.page.close()
1507
- self.logger.info("Close Browser Context...")
1557
+ self.logger.info("Close browser context...")
1508
1558
  self.context.close()
1509
- self.logger.info("Close Browser...")
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...")
@@ -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
- response = self.otcs_frontend_object.download_config_file(
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 Stateful Set -> '%s' for OTCS Frontends!",
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 Set -> '%s' has -> %s replicas",
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 Stateful Set -> '%s' for OTCS Backends!",
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 Set -> '%s' has -> %s replicas",
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 ID -> '%s' for resource name -> '%s'", otcs_resource_id, self.settings.otcs.resource_name
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 for Content Server with name -> '%s' does not exist yet. Waiting...",
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 Stateful Set -> '%s' for OTCS DA!",
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 Liveness probe for pod -> '%s'", pod_name)
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 Stateful Set -> '%s' for OTCS Frontends!",
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 Liveness probe for pod -> '%s'", pod_name)
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 Liveness probe for pod -> '%s'", pod_name)
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-Authenticating to OTCS frontend after restart of frontend pods...",
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-Authenticating to OTCS backend after restart of backend pods...",
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 Liveness probes in all pods:
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 Liveness probe for pod -> '%s'", pod_name)
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 Liveness probe for pod -> '%s'", pod_name)
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 Pod to make Kubernetes restart it."""
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 = 2000 # use Enterprise Workspace as fallback
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 = 2000 # use Enterprise Workspace as fallback
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: