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

@@ -41,7 +41,7 @@ customization_run: Central function to initiate the customization
41
41
  """
42
42
 
43
43
  __author__ = "Dr. Marc Diefenbruch"
44
- __copyright__ = "Copyright 2023, OpenText"
44
+ __copyright__ = "Copyright 2024, OpenText"
45
45
  __credits__ = ["Kai-Philip Gatzweiler"]
46
46
  __maintainer__ = "Dr. Marc Diefenbruch"
47
47
  __email__ = "mdiefenb@opentext.com"
@@ -59,7 +59,7 @@ import requests
59
59
 
60
60
  # OpenText specific modules:
61
61
  import yaml
62
- from pyxecm import OTAC, OTCS, OTDS, OTIV, OTPD
62
+ from pyxecm import OTAC, OTCS, OTDS, OTIV, OTPD, OTMM, CoreShare
63
63
  from pyxecm.customizer.k8s import K8s
64
64
  from pyxecm.customizer.m365 import M365
65
65
  from pyxecm.customizer.payload import Payload
@@ -74,7 +74,7 @@ class CustomizerSettings:
74
74
  """Class to manage settings"""
75
75
 
76
76
  placeholder_values: dict = field(default_factory=dict)
77
- stop_on_error: bool = os.environ.get("LOGLEVEL", "INFO") == "DEBUG"
77
+ stop_on_error: bool = os.environ.get("STOP_ON_ERROR", "false").lower() == "true"
78
78
  cust_log_file: str = "/tmp/customizing.log"
79
79
  customizer_start_time = customizer_end_time = datetime.now()
80
80
 
@@ -123,6 +123,7 @@ class CustomizerSettingsOTCS:
123
123
  port: int = os.environ.get("OTCS_SERVICE_PORT_OTCS", 8080)
124
124
  port_backend: int = os.environ.get("OTCS_SERVICE_PORT_OTCS", 8080)
125
125
  port_frontend: int = 80
126
+ base_path: str = "/cs/cs"
126
127
  admin: str = os.environ.get("OTCS_ADMIN", "admin")
127
128
  password: str = os.environ.get("OTCS_PASSWORD")
128
129
  partition: str = os.environ.get("OTCS_PARTITION", "Content Server Members")
@@ -142,7 +143,11 @@ class CustomizerSettingsOTCS:
142
143
  replicas_frontend = 0
143
144
  replicas_backend = 0
144
145
 
146
+ # Add configuration options for Customizer behaviour
145
147
  update_admin_user: bool = True
148
+ upload_config_files: bool = True
149
+ upload_status_files: bool = True
150
+ upload_log_file: bool = True
146
151
 
147
152
 
148
153
  @dataclass
@@ -231,7 +236,23 @@ class CustomizerSettingsM365:
231
236
  password: str = os.environ.get("O365_PASSWORD", "")
232
237
  domain: str = os.environ.get("O365_DOMAIN", "")
233
238
  sku_id: str = os.environ.get("O365_SKU_ID", "c7df2760-2c81-4ef7-b578-5b5392b571df")
234
- teams_app_name: str = "OpenText Extended ECM"
239
+ teams_app_name: str = os.environ.get("O365_TEAMS_APP_NAME", "OpenText Extended ECM")
240
+ teams_app_external_id: str = os.environ.get(
241
+ "O365_TEAMS_APP_ID", "dd4af790-d8ff-47a0-87ad-486318272c7a"
242
+ )
243
+
244
+
245
+ @dataclass
246
+ class CustomizerSettingsCoreShare:
247
+ """Class for Core Share related settings"""
248
+
249
+ enabled: bool = os.environ.get("CORE_SHARE_ENABLED", "false").lower() == "true"
250
+ base_url: str = os.environ.get("CORE_SHARE_BASE_URL", "https://core.opentext.com")
251
+ sso_url: str = os.environ.get("CORE_SHARE_SSO_URL", "https://sso.core.opentext.com")
252
+ client_id: str = os.environ.get("CORE_SHARE_CLIENT_ID", "")
253
+ client_secret = os.environ.get("CORE_SHARE_CLIENT_SECRET", "")
254
+ username: str = os.environ.get("CORE_SHARE_USERNAME", "")
255
+ password: str = os.environ.get("CORE_SHARE_PASSWORD", "")
235
256
 
236
257
 
237
258
  @dataclass
@@ -258,6 +279,7 @@ class Customizer:
258
279
  k8s: CustomizerSettingsK8S = CustomizerSettingsK8S(),
259
280
  otawp: CustomizerSettingsOTAWP = CustomizerSettingsOTAWP(),
260
281
  m365: CustomizerSettingsM365 = CustomizerSettingsM365(),
282
+ core_share: CustomizerSettingsCoreShare = CustomizerSettingsCoreShare(),
261
283
  aviator: CustomizerSettingsAviator = CustomizerSettingsAviator(),
262
284
  ):
263
285
  self.settings = settings
@@ -286,6 +308,9 @@ class Customizer:
286
308
  # Microsoft 365 Environment variables:
287
309
  self.m365_settings = m365
288
310
 
311
+ # Core Share Environment variables:
312
+ self.core_share_settings = core_share
313
+
289
314
  # Aviator variables:
290
315
  self.aviator_settings = aviator
291
316
 
@@ -299,15 +324,18 @@ class Customizer:
299
324
  self.otiv_object: OTIV | None = None
300
325
  self.k8s_object: K8s | None = None
301
326
  self.m365_object: M365 | None = None
327
+ self.core_share_object: CoreShare | None = None
302
328
  self.browser_automation_object: BrowserAutomation | None = None
303
329
 
304
- def log_header(self, text: str, char: str = "=", length: int = 60):
330
+ # end initializer
331
+
332
+ def log_header(self, text: str, char: str = "=", length: int = 80):
305
333
  """Helper method to output a section header in the log file
306
334
 
307
335
  Args:
308
- text (str): _description_
336
+ text (str): Headline text to output into the log file.
309
337
  char (str, optional): header line character. Defaults to "=".
310
- length (int, optional): maxium length. Defaults to 60.
338
+ length (int, optional): maxium length. Defaults to 80.
311
339
  Returns:
312
340
  None
313
341
  """
@@ -329,7 +357,7 @@ class Customizer:
329
357
  "%s %s %s", char * char_count, text, char * (char_count + extra_char)
330
358
  )
331
359
 
332
- # end function definition
360
+ # end method definition
333
361
 
334
362
  def init_m365(self) -> M365:
335
363
  """Initialize the M365 object we use to talk to the Microsoft Graph API.
@@ -373,9 +401,13 @@ class Customizer:
373
401
  "Microsoft 365 Default License SKU = %s", self.m365_settings.sku_id
374
402
  )
375
403
  logger.info(
376
- "Microsoft 365 Teams App = %s",
404
+ "Microsoft 365 Teams App Name = %s",
377
405
  self.m365_settings.teams_app_name,
378
406
  )
407
+ logger.info(
408
+ "Microsoft 365 Teams App External ID = %s",
409
+ self.m365_settings.teams_app_external_id,
410
+ )
379
411
 
380
412
  m365_object = M365(
381
413
  tenant_id=self.m365_settings.tenant_id,
@@ -384,16 +416,265 @@ class Customizer:
384
416
  domain=self.m365_settings.domain,
385
417
  sku_id=self.m365_settings.sku_id,
386
418
  teams_app_name=self.m365_settings.teams_app_name,
419
+ teams_app_external_id=self.m365_settings.teams_app_external_id,
387
420
  )
388
421
 
389
422
  if m365_object and m365_object.authenticate():
390
423
  logger.info("Connected to Microsoft Graph API.")
391
- return m365_object
392
424
  else:
393
425
  logger.error("Failed to connect to Microsoft Graph API.")
394
426
  return m365_object
395
427
 
396
- # end function definition
428
+ logger.info(
429
+ "Download M365 Teams App -> '%s' (external ID = %s) from Extended ECM (OTCS)...",
430
+ self.m365_settings.teams_app_name,
431
+ self.m365_settings.teams_app_external_id,
432
+ )
433
+
434
+ # Download MS Teams App from OTCS (this has with 23.2 a nasty side-effect
435
+ # of unsetting 2 checkboxes on that config page - we reset these checkboxes
436
+ # with the settings file "O365Settings.xml"):
437
+ response = self.otcs_frontend_object.download_config_file(
438
+ "/cs/cs?func=officegroups.DownloadTeamsPackage",
439
+ "/tmp/ot.xecm.teams.zip",
440
+ )
441
+ # this app upload will be done with the user credentials - this is required:
442
+ m365_object.authenticate_user(
443
+ self.m365_settings.user, self.m365_settings.password
444
+ )
445
+
446
+ # Check if the app is already installed in the apps catalog
447
+ # ideally we want to use the
448
+ app_exist = False
449
+
450
+ # If the App External ID is provided via Env variable then we
451
+ # prefer to use it instead of the App name:
452
+ if self.m365_settings.teams_app_external_id:
453
+ logger.info(
454
+ "Check if M365 Teams App -> '%s' (%s) is already installed in catalog using external app ID...",
455
+ self.m365_settings.teams_app_name,
456
+ self.m365_settings.teams_app_external_id,
457
+ )
458
+ response = m365_object.get_teams_apps(
459
+ filter_expression="externalId eq '{}'".format(
460
+ self.m365_settings.teams_app_external_id
461
+ )
462
+ )
463
+ # this should always be True as ID is unique:
464
+ app_exist = m365_object.exist_result_item(
465
+ response=response,
466
+ key="externalId",
467
+ value=self.m365_settings.teams_app_external_id,
468
+ )
469
+ # If the app could not be found via the external ID we fall back to
470
+ # search for the app by name:
471
+ if not app_exist:
472
+ if self.m365_settings.teams_app_external_id:
473
+ logger.info(
474
+ "Could not find M365 Teams App using the external ID -> %s. Try to lookup the app by name -> '%s' instead...",
475
+ self.m365_settings.teams_app_external_id,
476
+ self.m365_settings.teams_app_name,
477
+ )
478
+ logger.info(
479
+ "Check if M365 Teams App -> '%s' is already installed in catalog (using app name)...",
480
+ self.m365_settings.teams_app_name,
481
+ )
482
+ response = m365_object.get_teams_apps(
483
+ filter_expression="contains(displayName, '{}')".format(
484
+ self.m365_settings.teams_app_name
485
+ )
486
+ )
487
+ app_exist = m365_object.exist_result_item(
488
+ response=response,
489
+ key="displayName",
490
+ value=self.m365_settings.teams_app_name,
491
+ )
492
+ if app_exist:
493
+ # We double check that we have the effective name of the app
494
+ # in the catalog to avoid errors when the app is looked up
495
+ # by its wrong name in the customizer automation. This can
496
+ # happen if the app is installed manually or the environment
497
+ # variable is set to a wrong name.
498
+ app_catalog_name = m365_object.get_result_value(response, "displayName")
499
+ if app_catalog_name != self.m365_settings.teams_app_name:
500
+ logger.warning(
501
+ "The Extended ECM app name -> '%s' in the M365 Teams catalog does not match the defined app name '%s'! Somebody must have manually installed the app with the wrong name!",
502
+ app_catalog_name,
503
+ self.m365_settings.teams_app_name,
504
+ )
505
+ # Align the name in the settings dict with the existing name in the catalog.
506
+ self.m365_settings.teams_app_name = app_catalog_name
507
+ # Align the name in the M365 object config dict with the existing name in the catalog.
508
+ m365_object.config()["teamsAppName"] = app_catalog_name
509
+ app_internal_id = m365_object.get_result_value(
510
+ response=response, key="id", index=0
511
+ ) # 0 = Index = first item
512
+ # Store the internal ID for later use
513
+ m365_object.config()["teamsAppInternalId"] = app_internal_id
514
+ app_catalog_version = m365_object.get_result_value(
515
+ response=response,
516
+ key="version",
517
+ index=0,
518
+ sub_dict_name="appDefinitions",
519
+ )
520
+ logger.info(
521
+ "M365 Teams App -> '%s' (external ID = %s) is already in app catalog with app internal ID -> %s and version -> %s. Check if we have a newer version to upload...",
522
+ self.m365_settings.teams_app_name,
523
+ self.m365_settings.teams_app_external_id,
524
+ app_internal_id,
525
+ app_catalog_version,
526
+ )
527
+ app_download_version = m365_object.extract_version_from_app_manifest(
528
+ app_path="/tmp/ot.xecm.teams.zip"
529
+ )
530
+ if app_catalog_version < app_download_version:
531
+ logger.info(
532
+ "Upgrading Extended ECM Teams App in catalog from version -> %s to version -> %s...",
533
+ app_catalog_version,
534
+ app_download_version,
535
+ )
536
+ response = m365_object.upload_teams_app(
537
+ app_path="/tmp/ot.xecm.teams.zip",
538
+ update_existing_app=True,
539
+ app_catalog_id=app_internal_id,
540
+ )
541
+ app_internal_id = m365_object.get_result_value(
542
+ response=response,
543
+ key="teamsAppId",
544
+ )
545
+ if app_internal_id:
546
+ logger.info(
547
+ "Successfully upgraded Extended ECM Teams App -> %s (external ID = %s). Internal App ID -> %s",
548
+ self.m365_settings.teams_app_name,
549
+ self.m365_settings.teams_app_external_id,
550
+ app_internal_id,
551
+ )
552
+ # Store the internal ID for later use
553
+ m365_object.config()["teamsAppInternalId"] = app_internal_id
554
+ else:
555
+ logger.error(
556
+ "Failed to upgrade Extended ECM Teams App -> %s (external ID = %s).",
557
+ self.m365_settings.teams_app_name,
558
+ self.m365_settings.teams_app_external_id,
559
+ )
560
+ else:
561
+ logger.info(
562
+ "No upgrade required. The downloaded version -> %s is not newer than the version -> %s which is already in the M365 app catalog.",
563
+ app_download_version,
564
+ app_catalog_version,
565
+ )
566
+ else: # Extended ECM M365 Teams app is not yet installed...
567
+ logger.info(
568
+ "Extended Teams ECM App -> '%s' (external ID = %s) is not yet in app catalog. Installing as new app...",
569
+ self.m365_settings.teams_app_name,
570
+ self.m365_settings.teams_app_external_id,
571
+ )
572
+ response = m365_object.upload_teams_app(
573
+ app_path="/tmp/ot.xecm.teams.zip", update_existing_app=False
574
+ )
575
+ app_internal_id = m365_object.get_result_value(
576
+ response=response,
577
+ key="id", # for new installs it is NOT "teamsAppId" but "id" as we use a different M365 Graph API endpoint !!!
578
+ )
579
+ if app_internal_id:
580
+ logger.info(
581
+ "Successfully installed Extended ECM Teams App -> '%s' (external ID = %s). Internal App ID -> %s",
582
+ self.m365_settings.teams_app_name,
583
+ self.m365_settings.teams_app_external_id,
584
+ app_internal_id,
585
+ )
586
+ # Store the internal ID for later use
587
+ m365_object.config()["teamsAppInternalId"] = app_internal_id
588
+ else:
589
+ logger.error(
590
+ "Failed to install Extended ECM Teams App -> '%s' (external ID = %s).",
591
+ self.m365_settings.teams_app_name,
592
+ self.m365_settings.teams_app_external_id,
593
+ )
594
+
595
+ # logger.info("======== Upload Outlook Add-In ============")
596
+
597
+ # # Download MS Outlook Add-In from OTCS:
598
+ # MANIFEST_FILE = "/tmp/BusinessWorkspace.Manifest.xml"
599
+ # if not self.otcs_frontend_object.download_config_file(
600
+ # "/cs/cs?func=outlookaddin.DownloadManifest",
601
+ # MANIFEST_FILE,
602
+ # "DeployedContentServer",
603
+ # self.otcs_settings.public_url,
604
+ # ):
605
+ # logger.error("Failed to download M365 Outlook Add-In from Extended ECM!")
606
+ # else:
607
+ # # THIS IS NOT IMPLEMENTED DUE TO LACK OF M365 GRAPH API SUPPORT!
608
+ # # Do it manually for now: https://admin.microsoft.com/#/Settings/IntegratedApps
609
+ # logger.info("Successfully downloaded M365 Outlook Add-In from Extended ECM to %s", MANIFEST_FILE)
610
+ # m365_object.upload_outlook_app(MANIFEST_FILE)
611
+
612
+ return m365_object
613
+
614
+ # end method definition
615
+
616
+ def init_coreshare(self) -> M365:
617
+ """Initialize the Core Share object we use to talk to the Core Share API.
618
+
619
+ Args:
620
+ None
621
+ Returns:
622
+ object: CoreShare object or None if the object couldn't be created or
623
+ the authentication fails.
624
+ """
625
+
626
+ logger.info(
627
+ "Core Share Base URL = %s", self.core_share_settings.base_url
628
+ )
629
+ logger.info(
630
+ "Core Share SSO URL = %s", self.core_share_settings.sso_url
631
+ )
632
+ logger.info(
633
+ "Core Share Client ID = %s", self.core_share_settings.client_id
634
+ )
635
+ logger.debug(
636
+ "Core Share Client Secret = %s",
637
+ self.core_share_settings.client_secret,
638
+ )
639
+ logger.info(
640
+ "Core Share User = %s",
641
+ (
642
+ self.core_share_settings.username
643
+ if self.core_share_settings.username != ""
644
+ else "<not configured>"
645
+ ),
646
+ )
647
+ logger.debug(
648
+ "Core Share Password = %s",
649
+ (
650
+ self.core_share_settings.password
651
+ if self.core_share_settings.password != ""
652
+ else "<not configured>"
653
+ ),
654
+ )
655
+
656
+ core_share_object = CoreShare(
657
+ base_url=self.core_share_settings.base_url,
658
+ sso_url=self.core_share_settings.sso_url,
659
+ client_id=self.core_share_settings.client_id,
660
+ client_secret=self.core_share_settings.client_secret,
661
+ username=self.core_share_settings.username,
662
+ password=self.core_share_settings.password,
663
+ )
664
+
665
+ if core_share_object and core_share_object.authenticate_admin():
666
+ logger.info("Connected to Core Share as Tenant Admin.")
667
+ else:
668
+ logger.error("Failed to connect to Core Share as Tenant Admin.")
669
+
670
+ if core_share_object and core_share_object.authenticate_user():
671
+ logger.info("Connected to Core Share as Tenant Service User.")
672
+ else:
673
+ logger.error("Failed to connect to Core Share as Tenant Service User.")
674
+
675
+ return core_share_object
676
+
677
+ # end method definition
397
678
 
398
679
  def init_k8s(self) -> K8s:
399
680
  """Initialize the Kubernetes object we use to talk to the Kubernetes API.
@@ -407,10 +688,10 @@ class Customizer:
407
688
  """
408
689
 
409
690
  logger.info("Connection parameters Kubernetes (K8s):")
410
- logger.info("K8s inCluster -> %s", self.k8s_settings.in_cluster)
411
- logger.info("K8s namespace -> %s", self.k8s_settings.namespace)
691
+ logger.info("K8s inCluster = %s", self.k8s_settings.in_cluster)
692
+ logger.info("K8s namespace = %s", self.k8s_settings.namespace)
412
693
  logger.info(
413
- "K8s kubeconfig file -> %s",
694
+ "K8s kubeconfig file = %s",
414
695
  self.k8s_settings.kubeconfig_file,
415
696
  )
416
697
 
@@ -430,14 +711,14 @@ class Customizer:
430
711
  )
431
712
  if not otcs_frontend_scale:
432
713
  logger.error(
433
- "Cannot find Kubernetes Stateful Set for OTCS Frontends -> %s",
714
+ "Cannot find Kubernetes Stateful Set -> '%s' for OTCS Frontends!",
434
715
  self.otcs_settings.k8s_statefulset_frontend,
435
716
  )
436
717
  sys.exit()
437
718
 
438
719
  self.otcs_settings.replicas_frontend = otcs_frontend_scale.spec.replicas # type: ignore
439
720
  logger.info(
440
- "Stateful Set -> %s has -> %s replicas",
721
+ "Stateful Set -> '%s' has -> %s replicas",
441
722
  self.otcs_settings.k8s_statefulset_frontend,
442
723
  self.otcs_settings.replicas_frontend,
443
724
  )
@@ -448,21 +729,21 @@ class Customizer:
448
729
  )
449
730
  if not otcs_backend_scale:
450
731
  logger.error(
451
- "Cannot find Kubernetes Stateful Set for OTCS Backends -> %s",
732
+ "Cannot find Kubernetes Stateful Set -> '%s' for OTCS Backends!",
452
733
  self.otcs_settings.k8s_statefulset_backend,
453
734
  )
454
735
  sys.exit()
455
736
 
456
737
  self.otcs_settings.replicas_backend = otcs_backend_scale.spec.replicas # type: ignore
457
738
  logger.info(
458
- "Stateful Set -> %s has -> %s replicas",
739
+ "Stateful Set -> '%s' has -> %s replicas",
459
740
  self.otcs_settings.k8s_statefulset_backend,
460
741
  self.otcs_settings.replicas_backend,
461
742
  )
462
743
 
463
744
  return k8s_object
464
745
 
465
- # end function definition
746
+ # end method definition
466
747
 
467
748
  def init_otds(self) -> OTDS:
468
749
  """Initialize the OTDS object and parameters and authenticate at OTDS once it is ready.
@@ -519,7 +800,7 @@ class Customizer:
519
800
 
520
801
  return otds_object
521
802
 
522
- # end function definition
803
+ # end method definition
523
804
 
524
805
  def init_otac(self) -> OTAC:
525
806
  """Initialize the OTAC object and parameters.
@@ -557,6 +838,16 @@ class Customizer:
557
838
  self.otds_settings.password,
558
839
  )
559
840
 
841
+ # This is a work-around as OTCS container automation is not
842
+ # enabling the certificate reliable.
843
+ response = otac_object.enable_certificate(
844
+ cert_name="SP_otcs-admin-0", cert_type="ARC"
845
+ )
846
+ if not response:
847
+ logger.error("Failed to enable OTAC certificate for Extended ECM!")
848
+ else:
849
+ logger.info("Successfully enabled OTAC certificate for Extended ECM!")
850
+
560
851
  # is there a known server configured for Archive Center (to sync content with)
561
852
  if otac_object and self.otac_settings.known_server != "":
562
853
  # wait until the OTAC pod is in ready state
@@ -587,7 +878,7 @@ class Customizer:
587
878
 
588
879
  return otac_object
589
880
 
590
- # end function definition
881
+ # end method definition
591
882
 
592
883
  def init_otcs(
593
884
  self,
@@ -646,6 +937,7 @@ class Customizer:
646
937
  partition_name,
647
938
  resource_name,
648
939
  otds_ticket=otds_ticket,
940
+ base_path=self.otcs_settings.base_path,
649
941
  )
650
942
 
651
943
  # It is important to wait for OTCS to be configured - otherwise we
@@ -666,17 +958,17 @@ class Customizer:
666
958
  otcs_cookie = otcs_object.authenticate()
667
959
  logger.info("OTCS is ready now.")
668
960
 
669
- if self.otcs_settings.update_admin_user:
670
- # Set first name and last name of Admin user (ID = 1000):
671
- otcs_object.update_user(1000, field="first_name", value="Terrarium")
672
- otcs_object.update_user(1000, field="last_name", value="Admin")
961
+ # if self.otcs_settings.update_admin_user:
962
+ # Set first name and last name of Admin user (ID = 1000):
963
+ # otcs_object.update_user(1000, field="first_name", value="Terrarium")
964
+ # otcs_object.update_user(1000, field="last_name", value="Admin")
673
965
 
674
966
  if "OTCS_RESSOURCE_ID" not in self.settings.placeholder_values:
675
- self.settings.placeholder_values[
676
- "OTCS_RESSOURCE_ID"
677
- ] = self.otds_object.get_resource(self.otcs_settings.resource_name)[
678
- "resourceID"
679
- ]
967
+ self.settings.placeholder_values["OTCS_RESSOURCE_ID"] = (
968
+ self.otds_object.get_resource(self.otcs_settings.resource_name)[
969
+ "resourceID"
970
+ ]
971
+ )
680
972
  logger.debug(
681
973
  "Placeholder values after OTCS init = %s",
682
974
  self.settings.placeholder_values,
@@ -686,9 +978,9 @@ class Customizer:
686
978
  otcs_resource = self.otds_object.get_resource(
687
979
  self.otcs_settings.resource_name
688
980
  )
689
- otcs_resource[
690
- "logoutURL"
691
- ] = f"{self.otawp_settings.public_protocol}://{self.otawp_settings.public_url}/home/system/wcp/sso/sso_logout.htm"
981
+ otcs_resource["logoutURL"] = (
982
+ f"{self.otawp_settings.public_protocol}://{self.otawp_settings.public_url}/home/system/wcp/sso/sso_logout.htm"
983
+ )
692
984
  otcs_resource["logoutMethod"] = "GET"
693
985
 
694
986
  self.otds_object.update_resource(name="cs", resource=otcs_resource)
@@ -698,7 +990,7 @@ class Customizer:
698
990
 
699
991
  return otcs_object
700
992
 
701
- # end function definition
993
+ # end method definition
702
994
 
703
995
  def init_otiv(self) -> OTIV | None:
704
996
  """Initialize the OTIV (Intelligent Viewing) object and its OTDS settings.
@@ -750,9 +1042,27 @@ class Customizer:
750
1042
  )
751
1043
  return None
752
1044
 
1045
+ # Workaround for VAT-4580 (24.2.0)
1046
+ update_publisher = self.otds_object.update_user(
1047
+ partition="Content Server Service Users",
1048
+ user_id="iv-publisher",
1049
+ attribute_name="oTType",
1050
+ attribute_value="ServiceUser",
1051
+ )
1052
+ while update_publisher is None:
1053
+ update_publisher = self.otds_object.update_user(
1054
+ partition="Content Server Service Users",
1055
+ user_id="iv-publisher",
1056
+ attribute_name="oTType",
1057
+ attribute_value="ServiceUser",
1058
+ )
1059
+ time.sleep(30)
1060
+
1061
+ logger.info("OTDS user iv-publisher -> updating oTType=ServiceUser")
1062
+
753
1063
  return otiv_object
754
1064
 
755
- # end function definition
1065
+ # end method definition
756
1066
 
757
1067
  def init_otpd(self) -> OTPD:
758
1068
  """Initialize the OTPD (PowerDocs) object and parameters.
@@ -829,7 +1139,7 @@ class Customizer:
829
1139
  logger.info("OTAWP K8s Config Map = %s", self.otawp_settings.k8s_configmap)
830
1140
 
831
1141
  logger.info(
832
- "Wait for OTCS to create its OTDS resource with name -> %s...",
1142
+ "Wait for OTCS to create its OTDS resource with name -> '%s'...",
833
1143
  self.otcs_settings.resource_name,
834
1144
  )
835
1145
 
@@ -838,7 +1148,7 @@ class Customizer:
838
1148
  otcs_resource = self.otds_object.get_resource(self.otcs_settings.resource_name)
839
1149
  while otcs_resource is None:
840
1150
  logger.warning(
841
- "OTDS resource for Content Server with name -> %s does not exist yet. Waiting...",
1151
+ "OTDS resource for Content Server with name -> '%s' does not exist yet. Waiting...",
842
1152
  self.otcs_settings.resource_name,
843
1153
  )
844
1154
  time.sleep(30)
@@ -1150,7 +1460,7 @@ class Customizer:
1150
1460
  )
1151
1461
  while otcs_partition is None:
1152
1462
  logger.warning(
1153
- "OTDS user partition for Content Server with name -> %s does not exist yet. Waiting...",
1463
+ "OTDS user partition for Content Server with name -> '%s' does not exist yet. Waiting...",
1154
1464
  self.otcs_settings.partition,
1155
1465
  )
1156
1466
 
@@ -1188,7 +1498,7 @@ class Customizer:
1188
1498
  # check if the license file exists, otherwise skip for versions pre 24.1
1189
1499
  if os.path.isfile(self.otawp_settings.license_file):
1190
1500
  logger.info(
1191
- "OTAWP license file (%s) found, assiging to ressource %s",
1501
+ "Found OTAWP license file -> '%s', assiging it to ressource '%s'...",
1192
1502
  self.otawp_settings.license_file,
1193
1503
  self.otawp_settings.resource_name,
1194
1504
  )
@@ -1201,12 +1511,20 @@ class Customizer:
1201
1511
  )
1202
1512
  if not otawp_license:
1203
1513
  logger.error(
1204
- "Couldn't apply license -> %s for product -> %s.",
1514
+ "Couldn't apply license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1515
+ self.otawp_settings.license_file,
1516
+ self.otawp_settings.product_name,
1517
+ awp_resource["resourceID"],
1518
+ )
1519
+ else:
1520
+ logger.info(
1521
+ "Successfully applied license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1205
1522
  self.otawp_settings.license_file,
1206
1523
  self.otawp_settings.product_name,
1524
+ awp_resource["resourceID"],
1207
1525
  )
1208
1526
 
1209
- # Assign license to Content Server Members Partiton and otds.admin
1527
+ # Assign AppWorks license to Content Server Members Partiton and otds.admin:
1210
1528
  for partition_name in ["otds.admin", self.otcs_settings.partition]:
1211
1529
  if self.otds_object.is_partition_licensed(
1212
1530
  partition_name=partition_name,
@@ -1229,13 +1547,20 @@ class Customizer:
1229
1547
  )
1230
1548
  if not assigned_license:
1231
1549
  logger.error(
1232
- "Partition -> %s could not be assigned to license -> %s (%s)",
1550
+ "Partition -> '%s' could not be assigned to license -> '%s' (%s)",
1551
+ partition_name,
1552
+ self.otawp_settings.product_name,
1553
+ "USERS",
1554
+ )
1555
+ else:
1556
+ logger.info(
1557
+ "Partition -> '%s' successfully assigned to license -> '%s' (%s)",
1233
1558
  partition_name,
1234
1559
  self.otawp_settings.product_name,
1235
1560
  "USERS",
1236
1561
  )
1237
1562
 
1238
- # end function definition
1563
+ # end method definition
1239
1564
 
1240
1565
  def restart_otcs_service(self, otcs_object: OTCS, extra_wait_time: int = 60):
1241
1566
  """Restart the Content Server service in all OTCS pods
@@ -1258,11 +1583,11 @@ class Customizer:
1258
1583
  for x in range(0, self.otcs_settings.replicas_frontend):
1259
1584
  pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1260
1585
 
1261
- logger.info("Deactivate Liveness probe for pod -> %s", pod_name)
1586
+ logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1262
1587
  self.k8s_object.exec_pod_command(
1263
1588
  pod_name, ["/bin/sh", "-c", "touch /tmp/keepalive"]
1264
1589
  )
1265
- logger.info("Restarting pod -> %s", pod_name)
1590
+ logger.info("Restarting pod -> '%s'", pod_name)
1266
1591
  self.k8s_object.exec_pod_command(
1267
1592
  pod_name, ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"]
1268
1593
  )
@@ -1274,11 +1599,11 @@ class Customizer:
1274
1599
  for x in range(0, self.otcs_settings.replicas_backend):
1275
1600
  pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1276
1601
 
1277
- logger.info("Deactivate Liveness probe for pod -> %s", pod_name)
1602
+ logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1278
1603
  self.k8s_object.exec_pod_command(
1279
1604
  pod_name, ["/bin/sh", "-c", "touch /tmp/keepalive"]
1280
1605
  )
1281
- logger.info("Restarting pod -> %s", pod_name)
1606
+ logger.info("Restarting pod -> '%s'", pod_name)
1282
1607
  self.k8s_object.exec_pod_command(
1283
1608
  pod_name, ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"]
1284
1609
  )
@@ -1298,7 +1623,7 @@ class Customizer:
1298
1623
  for x in range(0, self.otcs_settings.replicas_frontend):
1299
1624
  pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1300
1625
 
1301
- logger.info("Reactivate Liveness probe for pod -> %s", pod_name)
1626
+ logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1302
1627
  self.k8s_object.exec_pod_command(
1303
1628
  pod_name, ["/bin/sh", "-c", "rm /tmp/keepalive"]
1304
1629
  )
@@ -1306,7 +1631,7 @@ class Customizer:
1306
1631
  for x in range(0, self.otcs_settings.replicas_backend):
1307
1632
  pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1308
1633
 
1309
- logger.info("Reactivate Liveness probe for pod -> %s", pod_name)
1634
+ logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1310
1635
  self.k8s_object.exec_pod_command(
1311
1636
  pod_name, ["/bin/sh", "-c", "rm /tmp/keepalive"]
1312
1637
  )
@@ -1322,7 +1647,7 @@ class Customizer:
1322
1647
  time.sleep(extra_wait_time)
1323
1648
  logger.info("Continue customizing...")
1324
1649
 
1325
- # end function definition
1650
+ # end method definition
1326
1651
 
1327
1652
  def restart_otac_service(self) -> bool:
1328
1653
  """Restart the Archive Center spawner service in OTAC pod
@@ -1337,7 +1662,7 @@ class Customizer:
1337
1662
  return False
1338
1663
 
1339
1664
  logger.info(
1340
- "Restarting spawner service in Archive Center pod -> %s",
1665
+ "Restarting spawner service in Archive Center pod -> '%s'",
1341
1666
  self.otac_settings.k8s_pod_name,
1342
1667
  )
1343
1668
  # The Archive Center Spawner needs to be run in "interactive" mode - otherwise the command will "hang":
@@ -1355,7 +1680,7 @@ class Customizer:
1355
1680
  else:
1356
1681
  return False
1357
1682
 
1358
- # end function definition
1683
+ # end method definition
1359
1684
 
1360
1685
  def restart_otawp_pod(self):
1361
1686
  """Delete the AppWorks Platform Pod to make Kubernetes restart it.
@@ -1367,7 +1692,7 @@ class Customizer:
1367
1692
 
1368
1693
  self.k8s_object.delete_pod(self.otawp_settings.k8s_statefulset + "-0")
1369
1694
 
1370
- # end function definition
1695
+ # end method definition
1371
1696
 
1372
1697
  def consolidate_otds(self):
1373
1698
  """Consolidate OTDS resources
@@ -1380,7 +1705,7 @@ class Customizer:
1380
1705
  if self.otawp_settings.enabled: # is AppWorks Platform deployed?
1381
1706
  self.otds_object.consolidate(self.otawp_settings.resource_name)
1382
1707
 
1383
- # end function definition
1708
+ # end method definition
1384
1709
 
1385
1710
  def import_powerdocs_configuration(self, otpd_object: OTPD):
1386
1711
  """Import a database export (zip file) into the PowerDocs database
@@ -1393,7 +1718,7 @@ class Customizer:
1393
1718
  # Download file from remote location specified by the OTPD_DBIMPORTFILE
1394
1719
  # this must be a public place without authentication:
1395
1720
  logger.info(
1396
- "Download PowerDocs database file from URL -> %s",
1721
+ "Download PowerDocs database file from URL -> '%s'",
1397
1722
  self.otpd_settings.db_importfile,
1398
1723
  )
1399
1724
 
@@ -1401,7 +1726,7 @@ class Customizer:
1401
1726
  package = requests.get(self.otpd_settings.db_importfile, timeout=60)
1402
1727
  package.raise_for_status()
1403
1728
  logger.info(
1404
- "Successfully downloaded PowerDocs database file -> %s; status code -> %s",
1729
+ "Successfully downloaded PowerDocs database file -> '%s'; status code -> %s",
1405
1730
  self.otpd_settings.db_importfile,
1406
1731
  package.status_code,
1407
1732
  )
@@ -1422,7 +1747,7 @@ class Customizer:
1422
1747
  except requests.exceptions.HTTPError as err:
1423
1748
  logger.error("Request error -> %s", err)
1424
1749
 
1425
- # end function definition
1750
+ # end method definition
1426
1751
 
1427
1752
  def set_maintenance_mode(self, enable: bool = True):
1428
1753
  """Enable or Disable Maintenance Mode
@@ -1460,12 +1785,14 @@ class Customizer:
1460
1785
  )
1461
1786
  logger.info("OTCS frontend is now back in Production Mode!")
1462
1787
 
1788
+ # end method definition
1789
+
1463
1790
  def customization_run(self):
1464
1791
  """Central function to initiate the customization"""
1465
1792
  # Set Timer for duration calculation
1466
- self.settings.customizer_start_time = (
1467
- self.settings.customizer_end_time
1468
- ) = datetime.now()
1793
+ self.settings.customizer_start_time = self.settings.customizer_end_time = (
1794
+ datetime.now()
1795
+ )
1469
1796
 
1470
1797
  # Initialize the OTDS, OTCS and OTPD objects and wait for the
1471
1798
  # pods to be ready. If any of this fails we bail out:
@@ -1548,119 +1875,40 @@ class Customizer:
1548
1875
  else:
1549
1876
  self.otpd_object = None
1550
1877
 
1878
+ if self.core_share_settings.enabled: # is Core Share enabled?
1879
+ self.log_header("Initialize Core Share")
1880
+
1881
+ self.core_share_object = self.init_coreshare()
1882
+ if not self.core_share_object:
1883
+ logger.error("Failed to initialize Core Share - exiting...")
1884
+ sys.exit()
1885
+ else:
1886
+ self.core_share_object = None
1887
+
1551
1888
  if (
1552
1889
  self.m365_settings.enabled
1553
1890
  and self.m365_settings.user != ""
1554
1891
  and self.m365_settings.password != ""
1555
1892
  ): # is M365 enabled?
1556
- self.log_header("Initialize MS Graph API")
1893
+ self.log_header("Initialize Microsoft 365")
1557
1894
 
1558
1895
  # Initialize the M365 object and connection to M365 Graph API:
1559
1896
  self.m365_object = self.init_m365()
1560
-
1561
- self.log_header("Upload MS Teams App")
1562
-
1563
- # Download MS Teams App from OTCS (this has with 23.2 a nasty side-effect
1564
- # of unsetting 2 checkboxes on that config page - we reset these checkboxes
1565
- # with the settings file "O365Settings.xml"):
1566
- response = self.otcs_frontend_object.download_config_file(
1567
- "/cs/cs?func=officegroups.DownloadTeamsPackage",
1568
- "/tmp/ot.xecm.teams.zip",
1569
- )
1570
- # this app upload will be done with the user credentials - this is required:
1571
- self.m365_object.authenticate_user(
1572
- self.m365_settings.user, self.m365_settings.password
1573
- )
1574
-
1575
- # Check if the app is already installed in the apps catalog:
1576
- response = self.m365_object.get_teams_apps(
1577
- f"contains(displayName, '{self.m365_settings.teams_app_name}')"
1578
- )
1579
- if self.m365_object.exist_result_item(
1580
- response, "displayName", self.m365_settings.teams_app_name
1581
- ):
1582
- app_catalog_id = self.m365_object.get_result_value(
1583
- response=response, key="id", index=0
1584
- ) # 0 = Index = first item
1585
- app_catalog_version = self.m365_object.get_result_value(
1586
- response=response,
1587
- key="version",
1588
- index=0,
1589
- sub_dict_name="appDefinitions",
1590
- )
1591
- logger.info(
1592
- "Extended ECM Teams App is already in app catalog with app catalog ID -> %s and version -> %s. Check if we have a newer version to upload...",
1593
- app_catalog_id,
1594
- app_catalog_version,
1595
- )
1596
- app_upload_version = self.m365_object.extract_version_from_app_manifest(
1597
- app_path="/tmp/ot.xecm.teams.zip"
1598
- )
1599
- if app_catalog_version < app_upload_version:
1600
- logger.info(
1601
- "Upgrading Extended ECM Teams App in catalog from version -> %s to version -> %s...",
1602
- app_catalog_version,
1603
- app_upload_version,
1604
- )
1605
- response = self.m365_object.upload_teams_app(
1606
- app_path="/tmp/ot.xecm.teams.zip",
1607
- update_existing_app=True,
1608
- app_catalog_id=app_catalog_id,
1609
- )
1610
- else:
1611
- logger.info(
1612
- "No upgrade required. The upload version -> %s is not newer than the version -> %s which is in the M365 app catalog.",
1613
- app_upload_version,
1614
- app_catalog_version,
1615
- )
1616
- else:
1617
- logger.info(
1618
- "Extended Teams ECM App is not yet in app catalog. Installing as new app..."
1619
- )
1620
- response = self.m365_object.upload_teams_app(
1621
- app_path="/tmp/ot.xecm.teams.zip"
1622
- )
1623
-
1624
- # logger.info("======== Upload Outlook Add-In ============")
1625
-
1626
- # # Download MS Outlook Add-In from OTCS:
1627
- # MANIFEST_FILE = "/tmp/BusinessWorkspace.Manifest.xml"
1628
- # if not self.otcs_frontend_object.download_config_file(
1629
- # "/cs/cs?func=outlookaddin.DownloadManifest",
1630
- # MANIFEST_FILE,
1631
- # "DeployedContentServer",
1632
- # self.otcs_settings.public_url,
1633
- # ):
1634
- # logger.error("Failed to download M365 Outlook Add-In from Extended ECM!")
1635
- # else:
1636
- # # THIS IS NOT IMPLEMENTED DUE TO LACK OF M365 GRAPH API SUPPORT!
1637
- # # Do it manually for now: https://admin.microsoft.com/#/Settings/IntegratedApps
1638
- # logger.info("Successfully downloaded M365 Outlook Add-In from Extended ECM to %s", MANIFEST_FILE)
1639
- # self.m365_object.upload_outlook_app(MANIFEST_FILE)
1640
- else:
1641
- self.m365_object = None
1642
-
1643
- # self.log_header("Initialize Browser Automation...")
1644
-
1645
- # We initialize a Selenium based browser automation for
1646
- # those die-hard settings that cannot be automated via REST API
1647
- # nor LLConfig nor Transport:
1648
- # self.browser_automation_object = self.init_browser_automation()
1649
- # if not self.browser_automation_object:
1650
- # logger.error("Failed to initialize Browser Automation - exiting...")
1651
- # sys.exit()
1897
+ if not self.m365_object:
1898
+ logger.error("Failed to initialize Microsoft 365!")
1899
+ sys.exit()
1652
1900
 
1653
1901
  self.log_header("Processing Payload")
1654
1902
 
1655
1903
  cust_payload_list = []
1656
1904
  # Is uncompressed payload provided?
1657
1905
  if os.path.exists(self.settings.cust_payload):
1658
- logger.info("Found payload file -> %s", self.settings.cust_payload)
1906
+ logger.info("Found payload file -> '%s'", self.settings.cust_payload)
1659
1907
  cust_payload_list.append(self.settings.cust_payload)
1660
1908
  # Is compressed payload provided?
1661
1909
  if os.path.exists(self.settings.cust_payload_gz):
1662
1910
  logger.info(
1663
- "Found compressed payload file -> %s", self.settings.cust_payload_gz
1911
+ "Found compressed payload file -> '%s'", self.settings.cust_payload_gz
1664
1912
  )
1665
1913
  cust_payload_list.append(self.settings.cust_payload_gz)
1666
1914
 
@@ -1668,16 +1916,16 @@ class Customizer:
1668
1916
  if os.path.exists(self.settings.cust_payload_external):
1669
1917
  for filename in os.scandir(self.settings.cust_payload_external):
1670
1918
  if filename.is_file() and os.path.getsize(filename) > 0:
1671
- logger.info("Found external payload file -> %s", filename.path)
1919
+ logger.info("Found external payload file -> '%s'", filename.path)
1672
1920
  cust_payload_list.append(filename.path)
1673
1921
  else:
1674
1922
  logger.info(
1675
- "No external payload file -> %s", self.settings.cust_payload_external
1923
+ "No external payload file -> '%s'", self.settings.cust_payload_external
1676
1924
  )
1677
1925
 
1678
1926
  for cust_payload in cust_payload_list:
1679
1927
  # Open the payload file. If this fails we bail out:
1680
- logger.info("Starting processing of payload -> %s", cust_payload)
1928
+ logger.info("Starting processing of payload -> '%s'", cust_payload)
1681
1929
 
1682
1930
  # Set startTime for duration calculation
1683
1931
  start_time = datetime.now()
@@ -1693,11 +1941,13 @@ class Customizer:
1693
1941
  otcs_restart_callback=self.restart_otcs_service,
1694
1942
  otiv_object=self.otiv_object,
1695
1943
  m365_object=self.m365_object,
1944
+ core_share_object=self.core_share_object,
1696
1945
  browser_automation_object=self.browser_automation_object,
1697
1946
  placeholder_values=self.settings.placeholder_values, # this dict includes placeholder replacements for the Ressource IDs of OTAWP and OTCS
1698
1947
  log_header_callback=self.log_header,
1699
1948
  stop_on_error=self.settings.stop_on_error,
1700
1949
  aviator_enabled=self.aviator_settings.enabled,
1950
+ upload_status_files=self.otcs_settings.upload_status_files,
1701
1951
  )
1702
1952
  # Load the payload file and initialize the payload sections:
1703
1953
  if not payload_object.init_payload():
@@ -1713,50 +1963,53 @@ class Customizer:
1713
1963
  self.consolidate_otds()
1714
1964
 
1715
1965
  # Upload payload file for later review to Enterprise Workspace
1716
- self.log_header("Upload Payload file to Extended ECM")
1717
- response = self.otcs_backend_object.get_node_from_nickname(
1718
- self.settings.cust_target_folder_nickname
1719
- )
1720
- target_folder_id = self.otcs_backend_object.get_result_value(response, "id")
1721
- if not target_folder_id:
1722
- target_folder_id = 2000 # use Enterprise Workspace as fallback
1723
- # Write YAML file with upadated payload (including IDs, etc.).
1724
- # We need to write to /tmp as initial location is read-only:
1725
- payload_file = os.path.basename(cust_payload)
1726
- payload_file = (
1727
- payload_file[: -len(".gz.b64")]
1728
- if payload_file.endswith(".gz.b64")
1729
- else payload_file
1730
- )
1731
- cust_payload = "/tmp/" + payload_file
1966
+ if self.otcs_settings.upload_config_files:
1967
+ self.log_header("Upload Payload file to Extended ECM")
1968
+ response = self.otcs_backend_object.get_node_from_nickname(
1969
+ self.settings.cust_target_folder_nickname
1970
+ )
1971
+ target_folder_id = self.otcs_backend_object.get_result_value(
1972
+ response, "id"
1973
+ )
1974
+ if not target_folder_id:
1975
+ target_folder_id = 2000 # use Enterprise Workspace as fallback
1976
+ # Write YAML file with upadated payload (including IDs, etc.).
1977
+ # We need to write to /tmp as initial location is read-only:
1978
+ payload_file = os.path.basename(cust_payload)
1979
+ payload_file = (
1980
+ payload_file[: -len(".gz.b64")]
1981
+ if payload_file.endswith(".gz.b64")
1982
+ else payload_file
1983
+ )
1984
+ cust_payload = "/tmp/" + payload_file
1732
1985
 
1733
- with open(cust_payload, "w", encoding="utf-8") as file:
1734
- yaml.dump(payload_object.get_payload(), file)
1986
+ with open(cust_payload, "w", encoding="utf-8") as file:
1987
+ yaml.dump(payload_object.get_payload(), file)
1735
1988
 
1736
- # Check if the payload file has been uploaded before.
1737
- # This can happen if we re-run the python container.
1738
- # In this case we add a version to the existing document:
1739
- response = self.otcs_backend_object.get_node_by_parent_and_name(
1740
- int(target_folder_id), os.path.basename(cust_payload)
1741
- )
1742
- target_document_id = self.otcs_backend_object.get_result_value(
1743
- response, "id"
1744
- )
1745
- if target_document_id:
1746
- response = self.otcs_backend_object.add_document_version(
1747
- int(target_document_id),
1748
- cust_payload,
1749
- os.path.basename(cust_payload),
1750
- "text/plain",
1751
- "Updated payload file after re-run of customization",
1989
+ # Check if the payload file has been uploaded before.
1990
+ # This can happen if we re-run the python container.
1991
+ # In this case we add a version to the existing document:
1992
+ response = self.otcs_backend_object.get_node_by_parent_and_name(
1993
+ int(target_folder_id), os.path.basename(cust_payload)
1752
1994
  )
1753
- else:
1754
- response = self.otcs_backend_object.upload_file_to_parent(
1755
- cust_payload,
1756
- os.path.basename(cust_payload),
1757
- "text/plain",
1758
- int(target_folder_id),
1995
+ target_document_id = self.otcs_backend_object.get_result_value(
1996
+ response, "id"
1759
1997
  )
1998
+ if target_document_id:
1999
+ response = self.otcs_backend_object.add_document_version(
2000
+ int(target_document_id),
2001
+ cust_payload,
2002
+ os.path.basename(cust_payload),
2003
+ "text/plain",
2004
+ "Updated payload file after re-run of customization",
2005
+ )
2006
+ else:
2007
+ response = self.otcs_backend_object.upload_file_to_parent(
2008
+ cust_payload,
2009
+ os.path.basename(cust_payload),
2010
+ "text/plain",
2011
+ int(target_folder_id),
2012
+ )
1760
2013
 
1761
2014
  duration = datetime.now() - start_time
1762
2015
  self.log_header(
@@ -1804,9 +2057,11 @@ class Customizer:
1804
2057
  self.otds_object.impersonate_resource(self.otawp_settings.resource_name)
1805
2058
 
1806
2059
  # Upload log file for later review to "Deployment" folder in "Administration" folder
1807
- if os.path.exists(self.settings.cust_log_file):
2060
+ if (
2061
+ os.path.exists(self.settings.cust_log_file)
2062
+ and self.otcs_settings.upload_log_file
2063
+ ):
1808
2064
  self.log_header("Upload log file to Extended ECM")
1809
- # logger.info("========== Upload log file to Extended ECM =============")
1810
2065
  response = self.otcs_backend_object.get_node_from_nickname(
1811
2066
  self.settings.cust_target_folder_nickname
1812
2067
  )
@@ -1824,18 +2079,19 @@ class Customizer:
1824
2079
  )
1825
2080
  if target_document_id:
1826
2081
  response = self.otcs_backend_object.add_document_version(
1827
- int(target_document_id),
1828
- self.settings.cust_log_file,
1829
- os.path.basename(self.settings.cust_log_file),
1830
- "text/plain",
1831
- "Updated Python Log after re-run of customization",
2082
+ node_id=int(target_document_id),
2083
+ file_url=self.settings.cust_log_file,
2084
+ file_name=os.path.basename(self.settings.cust_log_file),
2085
+ mime_type="text/plain",
2086
+ description="Updated Python Log after re-run of customization",
1832
2087
  )
1833
2088
  else:
1834
2089
  response = self.otcs_backend_object.upload_file_to_parent(
1835
- self.settings.cust_log_file,
1836
- os.path.basename(self.settings.cust_log_file),
1837
- "text/plain",
1838
- int(target_folder_id),
2090
+ file_url=self.settings.cust_log_file,
2091
+ file_name=os.path.basename(self.settings.cust_log_file),
2092
+ mime_type="text/plain",
2093
+ parent_id=int(target_folder_id),
2094
+ description="Initial Python Log after first run of customization",
1839
2095
  )
1840
2096
 
1841
2097
  self.settings.customizer_end_time = datetime.now()
@@ -1845,29 +2101,4 @@ class Customizer:
1845
2101
  )
1846
2102
  )
1847
2103
 
1848
-
1849
- if __name__ == "__main__":
1850
- logging.basicConfig(
1851
- format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
1852
- datefmt="%d-%b-%Y %H:%M:%S",
1853
- level=logging.INFO,
1854
- handlers=[
1855
- logging.StreamHandler(sys.stdout),
1856
- ],
1857
- )
1858
-
1859
- my_customizer = Customizer(
1860
- otcs=CustomizerSettingsOTCS(
1861
- hostname="otcs.eng.terrarium.cloud",
1862
- hostname_backend="otcs.eng.terrarium.cloud",
1863
- hostname_frontend="otcs.eng.terrarium.cloud",
1864
- protocol="https",
1865
- port_backend=443,
1866
- ),
1867
- otds=CustomizerSettingsOTDS(hostname="otds.eng.terrarium.cloud"),
1868
- otpd=CustomizerSettingsOTPD(enabled=False),
1869
- k8s=CustomizerSettingsK8S(enabled=False),
1870
- otiv=CustomizerSettingsOTIV(enabled=True),
1871
- )
1872
-
1873
- my_customizer.customization_run()
2104
+ # end method definition