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

@@ -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,14 +1511,14 @@ class Customizer:
1201
1511
  )
1202
1512
  if not otawp_license:
1203
1513
  logger.error(
1204
- "Couldn't apply license -> %s for product -> %s to OTDS resource -> %s",
1514
+ "Couldn't apply license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1205
1515
  self.otawp_settings.license_file,
1206
1516
  self.otawp_settings.product_name,
1207
1517
  awp_resource["resourceID"],
1208
1518
  )
1209
1519
  else:
1210
1520
  logger.info(
1211
- "Successfully applied license -> %s for product -> %s to OTDS resource -> %s",
1521
+ "Successfully applied license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1212
1522
  self.otawp_settings.license_file,
1213
1523
  self.otawp_settings.product_name,
1214
1524
  awp_resource["resourceID"],
@@ -1237,20 +1547,20 @@ class Customizer:
1237
1547
  )
1238
1548
  if not assigned_license:
1239
1549
  logger.error(
1240
- "Partition -> %s could not be assigned to license -> %s (%s)",
1550
+ "Partition -> '%s' could not be assigned to license -> '%s' (%s)",
1241
1551
  partition_name,
1242
1552
  self.otawp_settings.product_name,
1243
1553
  "USERS",
1244
1554
  )
1245
1555
  else:
1246
1556
  logger.info(
1247
- "Partition -> %s successfully assigned to license -> %s (%s)",
1557
+ "Partition -> '%s' successfully assigned to license -> '%s' (%s)",
1248
1558
  partition_name,
1249
1559
  self.otawp_settings.product_name,
1250
1560
  "USERS",
1251
1561
  )
1252
1562
 
1253
- # end function definition
1563
+ # end method definition
1254
1564
 
1255
1565
  def restart_otcs_service(self, otcs_object: OTCS, extra_wait_time: int = 60):
1256
1566
  """Restart the Content Server service in all OTCS pods
@@ -1273,11 +1583,11 @@ class Customizer:
1273
1583
  for x in range(0, self.otcs_settings.replicas_frontend):
1274
1584
  pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1275
1585
 
1276
- logger.info("Deactivate Liveness probe for pod -> %s", pod_name)
1586
+ logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1277
1587
  self.k8s_object.exec_pod_command(
1278
1588
  pod_name, ["/bin/sh", "-c", "touch /tmp/keepalive"]
1279
1589
  )
1280
- logger.info("Restarting pod -> %s", pod_name)
1590
+ logger.info("Restarting pod -> '%s'", pod_name)
1281
1591
  self.k8s_object.exec_pod_command(
1282
1592
  pod_name, ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"]
1283
1593
  )
@@ -1289,11 +1599,11 @@ class Customizer:
1289
1599
  for x in range(0, self.otcs_settings.replicas_backend):
1290
1600
  pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1291
1601
 
1292
- logger.info("Deactivate Liveness probe for pod -> %s", pod_name)
1602
+ logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1293
1603
  self.k8s_object.exec_pod_command(
1294
1604
  pod_name, ["/bin/sh", "-c", "touch /tmp/keepalive"]
1295
1605
  )
1296
- logger.info("Restarting pod -> %s", pod_name)
1606
+ logger.info("Restarting pod -> '%s'", pod_name)
1297
1607
  self.k8s_object.exec_pod_command(
1298
1608
  pod_name, ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"]
1299
1609
  )
@@ -1313,7 +1623,7 @@ class Customizer:
1313
1623
  for x in range(0, self.otcs_settings.replicas_frontend):
1314
1624
  pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1315
1625
 
1316
- logger.info("Reactivate Liveness probe for pod -> %s", pod_name)
1626
+ logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1317
1627
  self.k8s_object.exec_pod_command(
1318
1628
  pod_name, ["/bin/sh", "-c", "rm /tmp/keepalive"]
1319
1629
  )
@@ -1321,7 +1631,7 @@ class Customizer:
1321
1631
  for x in range(0, self.otcs_settings.replicas_backend):
1322
1632
  pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1323
1633
 
1324
- logger.info("Reactivate Liveness probe for pod -> %s", pod_name)
1634
+ logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1325
1635
  self.k8s_object.exec_pod_command(
1326
1636
  pod_name, ["/bin/sh", "-c", "rm /tmp/keepalive"]
1327
1637
  )
@@ -1337,7 +1647,7 @@ class Customizer:
1337
1647
  time.sleep(extra_wait_time)
1338
1648
  logger.info("Continue customizing...")
1339
1649
 
1340
- # end function definition
1650
+ # end method definition
1341
1651
 
1342
1652
  def restart_otac_service(self) -> bool:
1343
1653
  """Restart the Archive Center spawner service in OTAC pod
@@ -1352,7 +1662,7 @@ class Customizer:
1352
1662
  return False
1353
1663
 
1354
1664
  logger.info(
1355
- "Restarting spawner service in Archive Center pod -> %s",
1665
+ "Restarting spawner service in Archive Center pod -> '%s'",
1356
1666
  self.otac_settings.k8s_pod_name,
1357
1667
  )
1358
1668
  # The Archive Center Spawner needs to be run in "interactive" mode - otherwise the command will "hang":
@@ -1370,7 +1680,7 @@ class Customizer:
1370
1680
  else:
1371
1681
  return False
1372
1682
 
1373
- # end function definition
1683
+ # end method definition
1374
1684
 
1375
1685
  def restart_otawp_pod(self):
1376
1686
  """Delete the AppWorks Platform Pod to make Kubernetes restart it.
@@ -1382,7 +1692,7 @@ class Customizer:
1382
1692
 
1383
1693
  self.k8s_object.delete_pod(self.otawp_settings.k8s_statefulset + "-0")
1384
1694
 
1385
- # end function definition
1695
+ # end method definition
1386
1696
 
1387
1697
  def consolidate_otds(self):
1388
1698
  """Consolidate OTDS resources
@@ -1395,7 +1705,7 @@ class Customizer:
1395
1705
  if self.otawp_settings.enabled: # is AppWorks Platform deployed?
1396
1706
  self.otds_object.consolidate(self.otawp_settings.resource_name)
1397
1707
 
1398
- # end function definition
1708
+ # end method definition
1399
1709
 
1400
1710
  def import_powerdocs_configuration(self, otpd_object: OTPD):
1401
1711
  """Import a database export (zip file) into the PowerDocs database
@@ -1408,7 +1718,7 @@ class Customizer:
1408
1718
  # Download file from remote location specified by the OTPD_DBIMPORTFILE
1409
1719
  # this must be a public place without authentication:
1410
1720
  logger.info(
1411
- "Download PowerDocs database file from URL -> %s",
1721
+ "Download PowerDocs database file from URL -> '%s'",
1412
1722
  self.otpd_settings.db_importfile,
1413
1723
  )
1414
1724
 
@@ -1416,7 +1726,7 @@ class Customizer:
1416
1726
  package = requests.get(self.otpd_settings.db_importfile, timeout=60)
1417
1727
  package.raise_for_status()
1418
1728
  logger.info(
1419
- "Successfully downloaded PowerDocs database file -> %s; status code -> %s",
1729
+ "Successfully downloaded PowerDocs database file -> '%s'; status code -> %s",
1420
1730
  self.otpd_settings.db_importfile,
1421
1731
  package.status_code,
1422
1732
  )
@@ -1437,7 +1747,7 @@ class Customizer:
1437
1747
  except requests.exceptions.HTTPError as err:
1438
1748
  logger.error("Request error -> %s", err)
1439
1749
 
1440
- # end function definition
1750
+ # end method definition
1441
1751
 
1442
1752
  def set_maintenance_mode(self, enable: bool = True):
1443
1753
  """Enable or Disable Maintenance Mode
@@ -1475,12 +1785,14 @@ class Customizer:
1475
1785
  )
1476
1786
  logger.info("OTCS frontend is now back in Production Mode!")
1477
1787
 
1788
+ # end method definition
1789
+
1478
1790
  def customization_run(self):
1479
1791
  """Central function to initiate the customization"""
1480
1792
  # Set Timer for duration calculation
1481
- self.settings.customizer_start_time = (
1482
- self.settings.customizer_end_time
1483
- ) = datetime.now()
1793
+ self.settings.customizer_start_time = self.settings.customizer_end_time = (
1794
+ datetime.now()
1795
+ )
1484
1796
 
1485
1797
  # Initialize the OTDS, OTCS and OTPD objects and wait for the
1486
1798
  # pods to be ready. If any of this fails we bail out:
@@ -1563,119 +1875,40 @@ class Customizer:
1563
1875
  else:
1564
1876
  self.otpd_object = None
1565
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
+
1566
1888
  if (
1567
1889
  self.m365_settings.enabled
1568
1890
  and self.m365_settings.user != ""
1569
1891
  and self.m365_settings.password != ""
1570
1892
  ): # is M365 enabled?
1571
- self.log_header("Initialize MS Graph API")
1893
+ self.log_header("Initialize Microsoft 365")
1572
1894
 
1573
1895
  # Initialize the M365 object and connection to M365 Graph API:
1574
1896
  self.m365_object = self.init_m365()
1575
-
1576
- self.log_header("Upload MS Teams App")
1577
-
1578
- # Download MS Teams App from OTCS (this has with 23.2 a nasty side-effect
1579
- # of unsetting 2 checkboxes on that config page - we reset these checkboxes
1580
- # with the settings file "O365Settings.xml"):
1581
- response = self.otcs_frontend_object.download_config_file(
1582
- "/cs/cs?func=officegroups.DownloadTeamsPackage",
1583
- "/tmp/ot.xecm.teams.zip",
1584
- )
1585
- # this app upload will be done with the user credentials - this is required:
1586
- self.m365_object.authenticate_user(
1587
- self.m365_settings.user, self.m365_settings.password
1588
- )
1589
-
1590
- # Check if the app is already installed in the apps catalog:
1591
- response = self.m365_object.get_teams_apps(
1592
- f"contains(displayName, '{self.m365_settings.teams_app_name}')"
1593
- )
1594
- if self.m365_object.exist_result_item(
1595
- response, "displayName", self.m365_settings.teams_app_name
1596
- ):
1597
- app_catalog_id = self.m365_object.get_result_value(
1598
- response=response, key="id", index=0
1599
- ) # 0 = Index = first item
1600
- app_catalog_version = self.m365_object.get_result_value(
1601
- response=response,
1602
- key="version",
1603
- index=0,
1604
- sub_dict_name="appDefinitions",
1605
- )
1606
- logger.info(
1607
- "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...",
1608
- app_catalog_id,
1609
- app_catalog_version,
1610
- )
1611
- app_upload_version = self.m365_object.extract_version_from_app_manifest(
1612
- app_path="/tmp/ot.xecm.teams.zip"
1613
- )
1614
- if app_catalog_version < app_upload_version:
1615
- logger.info(
1616
- "Upgrading Extended ECM Teams App in catalog from version -> %s to version -> %s...",
1617
- app_catalog_version,
1618
- app_upload_version,
1619
- )
1620
- response = self.m365_object.upload_teams_app(
1621
- app_path="/tmp/ot.xecm.teams.zip",
1622
- update_existing_app=True,
1623
- app_catalog_id=app_catalog_id,
1624
- )
1625
- else:
1626
- logger.info(
1627
- "No upgrade required. The upload version -> %s is not newer than the version -> %s which is in the M365 app catalog.",
1628
- app_upload_version,
1629
- app_catalog_version,
1630
- )
1631
- else:
1632
- logger.info(
1633
- "Extended Teams ECM App is not yet in app catalog. Installing as new app..."
1634
- )
1635
- response = self.m365_object.upload_teams_app(
1636
- app_path="/tmp/ot.xecm.teams.zip"
1637
- )
1638
-
1639
- # logger.info("======== Upload Outlook Add-In ============")
1640
-
1641
- # # Download MS Outlook Add-In from OTCS:
1642
- # MANIFEST_FILE = "/tmp/BusinessWorkspace.Manifest.xml"
1643
- # if not self.otcs_frontend_object.download_config_file(
1644
- # "/cs/cs?func=outlookaddin.DownloadManifest",
1645
- # MANIFEST_FILE,
1646
- # "DeployedContentServer",
1647
- # self.otcs_settings.public_url,
1648
- # ):
1649
- # logger.error("Failed to download M365 Outlook Add-In from Extended ECM!")
1650
- # else:
1651
- # # THIS IS NOT IMPLEMENTED DUE TO LACK OF M365 GRAPH API SUPPORT!
1652
- # # Do it manually for now: https://admin.microsoft.com/#/Settings/IntegratedApps
1653
- # logger.info("Successfully downloaded M365 Outlook Add-In from Extended ECM to %s", MANIFEST_FILE)
1654
- # self.m365_object.upload_outlook_app(MANIFEST_FILE)
1655
- else:
1656
- self.m365_object = None
1657
-
1658
- # self.log_header("Initialize Browser Automation...")
1659
-
1660
- # We initialize a Selenium based browser automation for
1661
- # those die-hard settings that cannot be automated via REST API
1662
- # nor LLConfig nor Transport:
1663
- # self.browser_automation_object = self.init_browser_automation()
1664
- # if not self.browser_automation_object:
1665
- # logger.error("Failed to initialize Browser Automation - exiting...")
1666
- # sys.exit()
1897
+ if not self.m365_object:
1898
+ logger.error("Failed to initialize Microsoft 365!")
1899
+ sys.exit()
1667
1900
 
1668
1901
  self.log_header("Processing Payload")
1669
1902
 
1670
1903
  cust_payload_list = []
1671
1904
  # Is uncompressed payload provided?
1672
1905
  if os.path.exists(self.settings.cust_payload):
1673
- logger.info("Found payload file -> %s", self.settings.cust_payload)
1906
+ logger.info("Found payload file -> '%s'", self.settings.cust_payload)
1674
1907
  cust_payload_list.append(self.settings.cust_payload)
1675
1908
  # Is compressed payload provided?
1676
1909
  if os.path.exists(self.settings.cust_payload_gz):
1677
1910
  logger.info(
1678
- "Found compressed payload file -> %s", self.settings.cust_payload_gz
1911
+ "Found compressed payload file -> '%s'", self.settings.cust_payload_gz
1679
1912
  )
1680
1913
  cust_payload_list.append(self.settings.cust_payload_gz)
1681
1914
 
@@ -1683,16 +1916,16 @@ class Customizer:
1683
1916
  if os.path.exists(self.settings.cust_payload_external):
1684
1917
  for filename in os.scandir(self.settings.cust_payload_external):
1685
1918
  if filename.is_file() and os.path.getsize(filename) > 0:
1686
- logger.info("Found external payload file -> %s", filename.path)
1919
+ logger.info("Found external payload file -> '%s'", filename.path)
1687
1920
  cust_payload_list.append(filename.path)
1688
1921
  else:
1689
1922
  logger.info(
1690
- "No external payload file -> %s", self.settings.cust_payload_external
1923
+ "No external payload file -> '%s'", self.settings.cust_payload_external
1691
1924
  )
1692
1925
 
1693
1926
  for cust_payload in cust_payload_list:
1694
1927
  # Open the payload file. If this fails we bail out:
1695
- logger.info("Starting processing of payload -> %s", cust_payload)
1928
+ logger.info("Starting processing of payload -> '%s'", cust_payload)
1696
1929
 
1697
1930
  # Set startTime for duration calculation
1698
1931
  start_time = datetime.now()
@@ -1708,11 +1941,13 @@ class Customizer:
1708
1941
  otcs_restart_callback=self.restart_otcs_service,
1709
1942
  otiv_object=self.otiv_object,
1710
1943
  m365_object=self.m365_object,
1944
+ core_share_object=self.core_share_object,
1711
1945
  browser_automation_object=self.browser_automation_object,
1712
1946
  placeholder_values=self.settings.placeholder_values, # this dict includes placeholder replacements for the Ressource IDs of OTAWP and OTCS
1713
1947
  log_header_callback=self.log_header,
1714
1948
  stop_on_error=self.settings.stop_on_error,
1715
1949
  aviator_enabled=self.aviator_settings.enabled,
1950
+ upload_status_files=self.otcs_settings.upload_status_files,
1716
1951
  )
1717
1952
  # Load the payload file and initialize the payload sections:
1718
1953
  if not payload_object.init_payload():
@@ -1728,50 +1963,53 @@ class Customizer:
1728
1963
  self.consolidate_otds()
1729
1964
 
1730
1965
  # Upload payload file for later review to Enterprise Workspace
1731
- self.log_header("Upload Payload file to Extended ECM")
1732
- response = self.otcs_backend_object.get_node_from_nickname(
1733
- self.settings.cust_target_folder_nickname
1734
- )
1735
- target_folder_id = self.otcs_backend_object.get_result_value(response, "id")
1736
- if not target_folder_id:
1737
- target_folder_id = 2000 # use Enterprise Workspace as fallback
1738
- # Write YAML file with upadated payload (including IDs, etc.).
1739
- # We need to write to /tmp as initial location is read-only:
1740
- payload_file = os.path.basename(cust_payload)
1741
- payload_file = (
1742
- payload_file[: -len(".gz.b64")]
1743
- if payload_file.endswith(".gz.b64")
1744
- else payload_file
1745
- )
1746
- 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
1747
1985
 
1748
- with open(cust_payload, "w", encoding="utf-8") as file:
1749
- 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)
1750
1988
 
1751
- # Check if the payload file has been uploaded before.
1752
- # This can happen if we re-run the python container.
1753
- # In this case we add a version to the existing document:
1754
- response = self.otcs_backend_object.get_node_by_parent_and_name(
1755
- int(target_folder_id), os.path.basename(cust_payload)
1756
- )
1757
- target_document_id = self.otcs_backend_object.get_result_value(
1758
- response, "id"
1759
- )
1760
- if target_document_id:
1761
- response = self.otcs_backend_object.add_document_version(
1762
- int(target_document_id),
1763
- cust_payload,
1764
- os.path.basename(cust_payload),
1765
- "text/plain",
1766
- "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)
1767
1994
  )
1768
- else:
1769
- response = self.otcs_backend_object.upload_file_to_parent(
1770
- cust_payload,
1771
- os.path.basename(cust_payload),
1772
- "text/plain",
1773
- int(target_folder_id),
1995
+ target_document_id = self.otcs_backend_object.get_result_value(
1996
+ response, "id"
1774
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
+ )
1775
2013
 
1776
2014
  duration = datetime.now() - start_time
1777
2015
  self.log_header(
@@ -1819,9 +2057,11 @@ class Customizer:
1819
2057
  self.otds_object.impersonate_resource(self.otawp_settings.resource_name)
1820
2058
 
1821
2059
  # Upload log file for later review to "Deployment" folder in "Administration" folder
1822
- 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
+ ):
1823
2064
  self.log_header("Upload log file to Extended ECM")
1824
- # logger.info("========== Upload log file to Extended ECM =============")
1825
2065
  response = self.otcs_backend_object.get_node_from_nickname(
1826
2066
  self.settings.cust_target_folder_nickname
1827
2067
  )
@@ -1839,18 +2079,19 @@ class Customizer:
1839
2079
  )
1840
2080
  if target_document_id:
1841
2081
  response = self.otcs_backend_object.add_document_version(
1842
- int(target_document_id),
1843
- self.settings.cust_log_file,
1844
- os.path.basename(self.settings.cust_log_file),
1845
- "text/plain",
1846
- "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",
1847
2087
  )
1848
2088
  else:
1849
2089
  response = self.otcs_backend_object.upload_file_to_parent(
1850
- self.settings.cust_log_file,
1851
- os.path.basename(self.settings.cust_log_file),
1852
- "text/plain",
1853
- 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",
1854
2095
  )
1855
2096
 
1856
2097
  self.settings.customizer_end_time = datetime.now()
@@ -1860,29 +2101,4 @@ class Customizer:
1860
2101
  )
1861
2102
  )
1862
2103
 
1863
-
1864
- if __name__ == "__main__":
1865
- logging.basicConfig(
1866
- format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
1867
- datefmt="%d-%b-%Y %H:%M:%S",
1868
- level=logging.INFO,
1869
- handlers=[
1870
- logging.StreamHandler(sys.stdout),
1871
- ],
1872
- )
1873
-
1874
- my_customizer = Customizer(
1875
- otcs=CustomizerSettingsOTCS(
1876
- hostname="otcs.eng.terrarium.cloud",
1877
- hostname_backend="otcs.eng.terrarium.cloud",
1878
- hostname_frontend="otcs.eng.terrarium.cloud",
1879
- protocol="https",
1880
- port_backend=443,
1881
- ),
1882
- otds=CustomizerSettingsOTDS(hostname="otds.eng.terrarium.cloud"),
1883
- otpd=CustomizerSettingsOTPD(enabled=False),
1884
- k8s=CustomizerSettingsK8S(enabled=False),
1885
- otiv=CustomizerSettingsOTIV(enabled=True),
1886
- )
1887
-
1888
- my_customizer.customization_run()
2104
+ # end method definition