pyxecm 1.6__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (78) hide show
  1. pyxecm/__init__.py +7 -4
  2. pyxecm/avts.py +727 -254
  3. pyxecm/coreshare.py +686 -467
  4. pyxecm/customizer/__init__.py +16 -4
  5. pyxecm/customizer/__main__.py +58 -0
  6. pyxecm/customizer/api/__init__.py +5 -0
  7. pyxecm/customizer/api/__main__.py +6 -0
  8. pyxecm/customizer/api/app.py +163 -0
  9. pyxecm/customizer/api/auth/__init__.py +1 -0
  10. pyxecm/customizer/api/auth/functions.py +92 -0
  11. pyxecm/customizer/api/auth/models.py +13 -0
  12. pyxecm/customizer/api/auth/router.py +78 -0
  13. pyxecm/customizer/api/common/__init__.py +1 -0
  14. pyxecm/customizer/api/common/functions.py +47 -0
  15. pyxecm/customizer/api/common/metrics.py +92 -0
  16. pyxecm/customizer/api/common/models.py +21 -0
  17. pyxecm/customizer/api/common/payload_list.py +870 -0
  18. pyxecm/customizer/api/common/router.py +72 -0
  19. pyxecm/customizer/api/settings.py +128 -0
  20. pyxecm/customizer/api/terminal/__init__.py +1 -0
  21. pyxecm/customizer/api/terminal/router.py +87 -0
  22. pyxecm/customizer/api/v1_csai/__init__.py +1 -0
  23. pyxecm/customizer/api/v1_csai/router.py +87 -0
  24. pyxecm/customizer/api/v1_maintenance/__init__.py +1 -0
  25. pyxecm/customizer/api/v1_maintenance/functions.py +100 -0
  26. pyxecm/customizer/api/v1_maintenance/models.py +12 -0
  27. pyxecm/customizer/api/v1_maintenance/router.py +76 -0
  28. pyxecm/customizer/api/v1_otcs/__init__.py +1 -0
  29. pyxecm/customizer/api/v1_otcs/functions.py +61 -0
  30. pyxecm/customizer/api/v1_otcs/router.py +179 -0
  31. pyxecm/customizer/api/v1_payload/__init__.py +1 -0
  32. pyxecm/customizer/api/v1_payload/functions.py +179 -0
  33. pyxecm/customizer/api/v1_payload/models.py +51 -0
  34. pyxecm/customizer/api/v1_payload/router.py +499 -0
  35. pyxecm/customizer/browser_automation.py +721 -286
  36. pyxecm/customizer/customizer.py +1076 -1425
  37. pyxecm/customizer/exceptions.py +35 -0
  38. pyxecm/customizer/guidewire.py +1186 -0
  39. pyxecm/customizer/k8s.py +901 -379
  40. pyxecm/customizer/log.py +107 -0
  41. pyxecm/customizer/m365.py +2967 -920
  42. pyxecm/customizer/nhc.py +1169 -0
  43. pyxecm/customizer/openapi.py +258 -0
  44. pyxecm/customizer/payload.py +18228 -7820
  45. pyxecm/customizer/pht.py +717 -286
  46. pyxecm/customizer/salesforce.py +516 -342
  47. pyxecm/customizer/sap.py +58 -41
  48. pyxecm/customizer/servicenow.py +611 -372
  49. pyxecm/customizer/settings.py +445 -0
  50. pyxecm/customizer/successfactors.py +408 -346
  51. pyxecm/customizer/translate.py +83 -48
  52. pyxecm/helper/__init__.py +5 -2
  53. pyxecm/helper/assoc.py +83 -43
  54. pyxecm/helper/data.py +2406 -870
  55. pyxecm/helper/logadapter.py +27 -0
  56. pyxecm/helper/web.py +229 -101
  57. pyxecm/helper/xml.py +596 -171
  58. pyxecm/maintenance_page/__init__.py +5 -0
  59. pyxecm/maintenance_page/__main__.py +6 -0
  60. pyxecm/maintenance_page/app.py +51 -0
  61. pyxecm/maintenance_page/settings.py +28 -0
  62. pyxecm/maintenance_page/static/favicon.avif +0 -0
  63. pyxecm/maintenance_page/templates/maintenance.html +165 -0
  64. pyxecm/otac.py +235 -141
  65. pyxecm/otawp.py +2668 -1220
  66. pyxecm/otca.py +569 -0
  67. pyxecm/otcs.py +7956 -3237
  68. pyxecm/otds.py +2178 -925
  69. pyxecm/otiv.py +36 -21
  70. pyxecm/otmm.py +1272 -325
  71. pyxecm/otpd.py +231 -127
  72. pyxecm-2.0.1.dist-info/METADATA +122 -0
  73. pyxecm-2.0.1.dist-info/RECORD +76 -0
  74. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/WHEEL +1 -1
  75. pyxecm-1.6.dist-info/METADATA +0 -53
  76. pyxecm-1.6.dist-info/RECORD +0 -32
  77. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info/licenses}/LICENSE +0 -0
  78. {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/top_level.txt +0 -0
@@ -1,47 +1,7 @@
1
- """[Automate OpenText Directory Services (OTDS) and Extended ECM (OTCS) configurations]
2
-
3
- Data classes to handle settings read from environment variables
4
- * CustomizerSettings: Class to manage settings
5
- * CustomizerSettingsOTDS: Class for OTDS related settings
6
- * CustomizerSettingsOTCS: Class for OTCS related settings
7
- * CustomizerSettingsOTAC: Class for OTAC related settings
8
- * CustomizerSettingsOTPD: Class for OTPD related settings
9
- * CustomizerSettingsOTIV: Class for OTIV related settings
10
- * CustomizerSettingsK8S: Class for K8s related settings
11
- * CustomizerSettingsOTAWP: Class for OTAWP related settings
12
- * CustomizerSettingsM365: Class for O365 related settings
13
- * CustomizerSettingsAviator: Class for Aviator related settings
14
-
15
- Methods of class Customizer:
16
-
17
- __init__: object initializer for class Customizer
18
- log_header: Helper method to output a section header in the log file
19
- init_browser_automation: initialize browser automation for Content Aviator
20
- init_m365: initialize the Microsoft 365 object
21
- init_k8s: initialize the Kubernetes object we use to talk to the Kubernetes API
22
- init_otds: initialize the OTDS object
23
- init_otac: initialize the OTAC object
24
- init_otcs: initialize the OTCS (Extended ECM) object
25
- init_otiv: initialize the OTIV (Intelligent Viewing) object and its OTDS settings
26
- init_otpd: initialize the PowerDocs object
27
- init_otawp: initialize OTDS settings for AppWorks Platform
28
-
29
- restart_otcs_service: restart the OTCS backend and frontend pods -
30
- required to make certain configurations effective
31
- restart_otac_service: restart spawner process in Archive Center
32
- restart_otawp_pod: restart the AppWorks Platform Pod to make settings effective
33
- consolidate_otds: consolidate OTDS users / groups (to get to a fully synchronized state)
34
-
35
- import_powerdocs_configuration: import PowerDocs database
36
-
37
- set_maintenance_mode: Enable or Disable Maintenance Mode
38
-
39
- customization_run: Central function to initiate the customization
40
-
41
- """
1
+ """Module to automate Directory Services (OTDS) and Content Server (OTCS) configurations."""
42
2
 
43
3
  __author__ = "Dr. Marc Diefenbruch"
44
- __copyright__ = "Copyright 2024, OpenText"
4
+ __copyright__ = "Copyright (C) 2024-2025, OpenText"
45
5
  __credits__ = ["Kai-Philip Gatzweiler"]
46
6
  __maintainer__ = "Dr. Marc Diefenbruch"
47
7
  __email__ = "mdiefenb@opentext.com"
@@ -49,298 +9,58 @@ __email__ = "mdiefenb@opentext.com"
49
9
  import logging
50
10
  import os
51
11
  import sys
12
+ import tempfile
52
13
  import time
53
- from dataclasses import dataclass, field
54
- from datetime import datetime
55
- import uuid
56
- import xml.etree.ElementTree as ET
57
- import json
58
- import re
59
-
60
- # from packaging.version import Version
14
+ from datetime import datetime, timezone
15
+ from typing import TYPE_CHECKING
61
16
 
62
17
  import requests
63
18
 
64
19
  # OpenText specific modules:
65
20
  import yaml
66
- from pyxecm import OTAC, OTCS, OTDS, OTIV, OTPD, OTMM, CoreShare, OTAWP
67
- from pyxecm.avts import AVTS
21
+ from pydantic import HttpUrl
22
+
23
+ from pyxecm import AVTS, OTAC, OTAWP, OTCS, OTDS, OTIV, OTPD, CoreShare
68
24
  from pyxecm.customizer.k8s import K8s
69
25
  from pyxecm.customizer.m365 import M365
70
26
  from pyxecm.customizer.payload import Payload
27
+ from pyxecm.customizer.settings import Settings
28
+
29
+ if TYPE_CHECKING:
30
+ from pyxecm.customizer.browser_automation import BrowserAutomation
71
31
 
72
- from pyxecm.customizer.browser_automation import BrowserAutomation
73
-
74
- logger = logging.getLogger("pyxecm.customizer")
75
-
76
-
77
- @dataclass
78
- class CustomizerSettings:
79
- """Class to manage settings"""
80
-
81
- placeholder_values: dict = field(default_factory=dict)
82
- stop_on_error: bool = os.environ.get("STOP_ON_ERROR", "false").lower() == "true"
83
- cust_log_file: str = "/tmp/customizing.log"
84
- customizer_start_time = customizer_end_time = datetime.now()
85
-
86
- # The following CUST artifacts are created by the main.tf in the python module:
87
- cust_settings_dir: str = "/settings/"
88
- cust_payload_dir: str = "/payload/"
89
- cust_payload: str = cust_payload_dir + "payload.yaml"
90
- cust_payload_gz: str = cust_payload_dir + "payload.yml.gz.b64"
91
- cust_payload_external: str = "/payload-external/"
92
-
93
- cust_target_folder_nickname: str = (
94
- "deployment" # nickname of folder to upload payload and log files
95
- )
96
- # CUST_RM_SETTINGS_DIR = "/opt/opentext/cs/appData/supportasset/Settings/"
97
- cust_rm_settings_dir = cust_settings_dir
98
-
99
-
100
- @dataclass
101
- class CustomizerSettingsOTDS:
102
- """Class for OTDS related settings"""
103
-
104
- protocol: str = os.environ.get("OTDS_PROTOCOL", "http")
105
- public_protocol: str = os.environ.get("OTDS_PUBLIC_PROTOCOL", "https")
106
- hostname: str = os.environ.get("OTDS_HOSTNAME", "otds")
107
- port: int = os.environ.get("OTDS_SERVICE_PORT_OTDS", 80)
108
- username: str = os.environ.get("OTDS_ADMIN", "admin")
109
- otds_ticket: str | None = None
110
- admin_partition: str = "otds.admin"
111
- public_url: str = os.environ.get("OTDS_PUBLIC_URL")
112
- password: str = os.environ.get("OTDS_PASSWORD")
113
- bindPassword: str = os.environ.get("BINB_PASSWORD")
114
- disable_password_policy: bool = True
115
- enable_audit: bool = True
116
-
117
-
118
- @dataclass
119
- class CustomizerSettingsOTCS:
120
- """Class for OTCS related settings"""
121
-
122
- # Content Server Constants:
123
- protocol: str = os.environ.get("OTCS_PROTOCOL", "http")
124
- public_protocol: str = os.environ.get("OTCS_PUBLIC_PROTOCOL", "https")
125
- hostname: str = os.environ.get("OTCS_HOSTNAME", "otcs-admin-0")
126
- hostname_backend: str = os.environ.get("OTCS_HOSTNAME", "otcs-admin-0")
127
- hostname_frontend: str = os.environ.get("OTCS_HOSTNAME_FRONTEND", "otcs-frontend")
128
- public_url: str = os.environ.get("OTCS_PUBLIC_URL", "otcs.public-url.undefined")
129
- port: int = os.environ.get("OTCS_SERVICE_PORT_OTCS", 8080)
130
- port_backend: int = os.environ.get("OTCS_SERVICE_PORT_OTCS", 8080)
131
- port_frontend: int = 80
132
- base_path: str = "/cs/cs"
133
- feme_uri: str = os.environ.get("FEME_URI", "ws://feme:4242")
134
- admin: str = os.environ.get("OTCS_ADMIN", "admin")
135
- password: str = os.environ.get("OTCS_PASSWORD")
136
- partition: str = os.environ.get("OTCS_PARTITION", "Content Server Members")
137
- resource_name: str = "cs"
138
- k8s_statefulset_frontend: str = "otcs-frontend"
139
- k8s_statefulset_backend: str = "otcs-admin"
140
- k8s_ingress: str = "otxecm-ingress"
141
- maintenance_mode: bool = (
142
- os.environ.get("OTCS_MAINTENANCE_MODE", "true").lower() == "true"
143
- )
144
- license_feature: str = "X3"
145
-
146
- # K8s service name and port for maintenance pod
147
- maintenance_service_name: str = "otxecm-customizer"
148
- mainteance_service_port: int = 5555
149
-
150
- replicas_frontend = 0
151
- replicas_backend = 0
152
-
153
- # Add configuration options for Customizer behaviour
154
- update_admin_user: bool = True
155
- upload_config_files: bool = True
156
- upload_status_files: bool = True
157
- upload_log_file: bool = True
158
-
159
-
160
- @dataclass
161
- class CustomizerSettingsOTAC:
162
- """Class for OTAC related settings"""
163
-
164
- enabled: bool = os.environ.get("OTAC_ENABLED", "false").lower() == "true"
165
- hostname: str = os.environ.get("OTAC_SERVICE_HOST", "otac-0")
166
- port: int = os.environ.get("OTAC_SERVICE_PORT", 8080)
167
- protocol: str = os.environ.get("OTAC_PROTOCOL", "http")
168
- public_url: str = os.environ.get("OTAC_PUBLIC_URL")
169
- admin: str = os.environ.get("OTAC_ADMIN", "dsadmin")
170
- password: str = os.environ.get("OTAC_PASSWORD", "")
171
- known_server: str = os.environ.get("OTAC_KNOWN_SERVER", "")
172
- k8s_pod_name: str = "otac-0"
173
-
174
-
175
- @dataclass
176
- class CustomizerSettingsOTPD:
177
- """Class for OTPD related settings"""
178
-
179
- enabled: bool = os.environ.get("OTPD_ENABLED", "false").lower() == "true"
180
- hostname: str = os.environ.get("OTPD_SERVICE_HOST", "otpd")
181
- port: int = os.environ.get("OTPD_SERVICE_PORT", 8080)
182
- protocol: str = os.environ.get("OTPD_PROTOCOL", "http")
183
- db_importfile: str = os.environ.get(
184
- "OTPD_DBIMPORTFILE", "URL://url.download.location/file.zip"
185
- )
186
- tenant: str = os.environ.get("OTPD_TENANT", "Successfactors")
187
- user: str = os.environ.get("OTPD_USER", "powerdocsapiuser")
188
- password: str = os.environ.get(
189
- "OTPD_PASSWORD",
190
- )
191
- k8s_pod_name: str = "otpd-0"
192
-
193
-
194
- @dataclass
195
- class CustomizerSettingsOTIV:
196
- """Class for OTIV related settings"""
197
-
198
- enabled: bool = os.environ.get("OTIV_ENABLED", "false").lower() == "true"
199
- license_file: str = "/payload/otiv-license.lic"
200
- license_feature: str = "FULLTIME_USERS_REGULAR"
201
- product_name: str = "Viewing"
202
- product_description: str = "OpenText Intelligent Viewing"
203
- resource_name: str = "iv"
204
-
205
-
206
- @dataclass
207
- class CustomizerSettingsK8S:
208
- """Class for K8s related settings"""
209
-
210
- enabled: bool = os.environ.get("K8S_ENABLED", "true").lower() == "true"
211
- in_cluster: bool = True
212
- kubeconfig_file: str = "~/.kube/config"
213
- namespace: str = "default"
214
-
215
-
216
- @dataclass
217
- class CustomizerSettingsOTAWP:
218
- """Class for OTAWP related settings"""
219
-
220
- enabled: bool = os.environ.get("OTAWP_ENABLED", "false").lower() == "true"
221
- license_file: str = "/payload/otawp-license.lic"
222
- product_name: str = "APPWORKS_PLATFORM"
223
- product_description: str = "OpenText Appworks Platform"
224
- resource_name: str = "awp"
225
- access_role_name: str = "Access to " + resource_name
226
- admin: str = os.environ.get("OTAWP_ADMIN", "sysadmin")
227
- password: str = os.environ.get("OTCS_PASSWORD")
228
- public_protocol: str = os.environ.get("OTAWP_PROTOCOL", "https")
229
- public_url: str = os.environ.get("OTAWP_PUBLIC_URL")
230
- k8s_statefulset: str = "appworks"
231
- k8s_configmap: str = "appworks-config-ymls"
232
- port: int = os.environ.get("OTAWP_SERVICE_PORT", 8080)
233
- protocol: str = os.environ.get("OTPD_PROTOCOL", "http")
234
-
235
-
236
- @dataclass
237
- class CustomizerSettingsM365:
238
- """Class for O365 related settings"""
239
-
240
- enabled: bool = os.environ.get("O365_ENABLED", "false").lower() == "true"
241
- tenant_id: str = os.environ.get("O365_TENANT_ID", "")
242
- client_id: str = os.environ.get("O365_CLIENT_ID", "")
243
- client_secret: str = os.environ.get("O365_CLIENT_SECRET", "")
244
- user: str = os.environ.get("O365_USER", "")
245
- password: str = os.environ.get("O365_PASSWORD", "")
246
- domain: str = os.environ.get("O365_DOMAIN", "")
247
- sku_id: str = os.environ.get("O365_SKU_ID", "c7df2760-2c81-4ef7-b578-5b5392b571df")
248
- teams_app_name: str = os.environ.get("O365_TEAMS_APP_NAME", "OpenText Extended ECM")
249
- teams_app_external_id: str = os.environ.get(
250
- "O365_TEAMS_APP_ID", "dd4af790-d8ff-47a0-87ad-486318272c7a"
251
- )
252
-
253
-
254
- @dataclass
255
- class CustomizerSettingsCoreShare:
256
- """Class for Core Share related settings"""
257
-
258
- enabled: bool = os.environ.get("CORE_SHARE_ENABLED", "false").lower() == "true"
259
- base_url: str = os.environ.get("CORE_SHARE_BASE_URL", "https://core.opentext.com")
260
- sso_url: str = os.environ.get("CORE_SHARE_SSO_URL", "https://sso.core.opentext.com")
261
- client_id: str = os.environ.get("CORE_SHARE_CLIENT_ID", "")
262
- client_secret = os.environ.get("CORE_SHARE_CLIENT_SECRET", "")
263
- username: str = os.environ.get("CORE_SHARE_USERNAME", "")
264
- password: str = os.environ.get("CORE_SHARE_PASSWORD", "")
265
-
266
-
267
- @dataclass
268
- class CustomizerSettingsAviator:
269
- """Class for Aviator related settings"""
270
-
271
- enabled: bool = os.environ.get("AVIATOR_ENABLED", "false").lower() == "true"
272
-
273
-
274
- @dataclass
275
- class CustomizerSettingsAVTS:
276
- """Class for Aviator Search (AVTS) related settings"""
277
-
278
- enabled: bool = os.environ.get("AVTS_ENABLED", "false").lower() == "true"
279
- otds_url = os.environ.get("AVTS_OTDS_URL", "")
280
- client_id = os.environ.get("AVTS_CLIENT_ID", "")
281
- client_secret = os.environ.get("AVTS_CLIENT_SECRET", "")
282
- base_url = os.environ.get("AVTS_BASE_URL", "")
283
- username = os.environ.get("AVTS_USERNAME", "")
284
- password = os.environ.get("AVTS_PASSWORD", "")
32
+ default_logger = logging.getLogger("pyxecm.customizer")
285
33
 
286
34
 
287
35
  class Customizer:
288
- """Customizer Class to control the cusomization automation
36
+ """Customizer Class to control the cusomization automation."""
289
37
 
290
- Args: None
291
- """
38
+ logger: logging.Logger = default_logger
39
+ customizer_start_time: datetime | None
40
+ customizer_stop_time: datetime | None
292
41
 
293
42
  def __init__(
294
43
  self,
295
- settings: CustomizerSettings = CustomizerSettings(),
296
- otds: CustomizerSettingsOTDS = CustomizerSettingsOTDS(),
297
- otcs: CustomizerSettingsOTCS = CustomizerSettingsOTCS(),
298
- otac: CustomizerSettingsOTAC = CustomizerSettingsOTAC(),
299
- otpd: CustomizerSettingsOTPD = CustomizerSettingsOTPD(),
300
- otiv: CustomizerSettingsOTIV = CustomizerSettingsOTIV(),
301
- k8s: CustomizerSettingsK8S = CustomizerSettingsK8S(),
302
- otawp: CustomizerSettingsOTAWP = CustomizerSettingsOTAWP(),
303
- m365: CustomizerSettingsM365 = CustomizerSettingsM365(),
304
- core_share: CustomizerSettingsCoreShare = CustomizerSettingsCoreShare(),
305
- aviator: CustomizerSettingsAviator = CustomizerSettingsAviator(),
306
- avts: CustomizerSettingsAVTS = CustomizerSettingsAVTS(),
307
- ):
308
- self.settings = settings
309
-
310
- # OTDS Constants:
311
- self.otds_settings = otds
312
-
313
- # Content Server Constants:
314
- self.otcs_settings = otcs
315
-
316
- # Archive Center constants:
317
- self.otac_settings = otac
318
-
319
- # PowerDocs constants:
320
- self.otpd_settings = otpd
321
-
322
- # Intelligent Viewing constants:
323
- self.otiv_settings = otiv
324
-
325
- # AppWorks Platform constants:
326
- self.otawp_settings = otawp
44
+ settings: dict | None = None,
45
+ logger: logging.Logger = default_logger,
46
+ ) -> None:
47
+ """Initialize Customzer object.
327
48
 
328
- # K8s Mode
329
- self.k8s_settings = k8s
330
-
331
- # Microsoft 365 Environment variables:
332
- self.m365_settings = m365
49
+ Args:
50
+ settings (dict | None, optional):
51
+ Customizer settings. Defaults to None.
52
+ logger (logging.Logger, optional):
53
+ The loggoing object to be used for all log messages.
54
+ Defaults to default_logger.
333
55
 
334
- # Core Share Environment variables:
335
- self.core_share_settings = core_share
56
+ """
336
57
 
337
- # Aviator variables:
338
- self.aviator_settings = aviator
58
+ self.logger = logger
339
59
 
340
- # Aviator Search variables:
341
- self.avts_settings = avts
60
+ # Create Settings class, raise ValidationError if settings are invalid
61
+ self.settings = Settings(**settings) if settings is not None else Settings()
342
62
 
343
- # Initialize Objects for later assignment
63
+ # Initialize Objects:
344
64
  self.otds_object: OTDS | None = None
345
65
  self.otcs_object: OTCS | None = None
346
66
  self.otcs_backend_object: OTCS | None = None
@@ -353,23 +73,30 @@ class Customizer:
353
73
  self.core_share_object: CoreShare | None = None
354
74
  self.browser_automation_object: BrowserAutomation | None = None
355
75
  self.otawp_object: OTAWP | None = None
76
+ self.avts_object: AVTS | None = None
356
77
 
357
78
  # end initializer
358
79
 
359
- def log_header(self, text: str, char: str = "=", length: int = 80):
360
- """Helper method to output a section header in the log file
80
+ def log_header(self, text: str, char: str = "=", length: int = 120) -> None:
81
+ """Output a section header in the log file.
361
82
 
362
83
  Args:
363
- text (str): Headline text to output into the log file.
364
- char (str, optional): header line character. Defaults to "=".
365
- length (int, optional): maxium length. Defaults to 80.
84
+ text (str):
85
+ Headline text to output into the log file.
86
+ char (str, optional):
87
+ The header line character. Defaults to "=".
88
+ length (int, optional):
89
+ The maximum line length. Defaults to 120.
90
+
366
91
  Returns:
367
92
  None
93
+
368
94
  """
369
95
 
370
96
  # Calculate the remaining space for the text after adding spaces
371
97
  available_space = max(
372
- 0, length - len(text) - 2
98
+ 0,
99
+ length - len(text) - 2,
373
100
  ) # 2 accounts for the spaces each side of the text
374
101
 
375
102
  # Calculate the number of characters needed on each side
@@ -380,8 +107,11 @@ class Customizer:
380
107
  char_count = max(3, char_count)
381
108
 
382
109
  # Build the header string, extra_char is either 0 or 1
383
- logger.info(
384
- "%s %s %s", char * char_count, text, char * (char_count + extra_char)
110
+ self.logger.info(
111
+ "%s %s %s",
112
+ char * char_count,
113
+ text,
114
+ char * (char_count + extra_char),
385
115
  )
386
116
 
387
117
  # end method definition
@@ -391,235 +121,248 @@ class Customizer:
391
121
 
392
122
  Args:
393
123
  None
124
+
394
125
  Returns:
395
- object: M365 object or None if the object couldn't be created or
396
- the authentication fails.
126
+ M365 object:
127
+ M365 object or None if the object couldn't be created or
128
+ the authentication fails.
129
+
397
130
  """
398
131
 
399
- logger.info(
400
- "Microsoft 365 Tenant ID = %s", self.m365_settings.tenant_id
132
+ self.logger.info(
133
+ "Microsoft 365 Tenant ID = %s",
134
+ self.settings.m365.tenant_id,
401
135
  )
402
- logger.info(
403
- "Microsoft 365 Client ID = %s", self.m365_settings.client_id
136
+ self.logger.debug(
137
+ "Microsoft 365 Client ID = %s",
138
+ self.settings.m365.client_id,
404
139
  )
405
- logger.debug(
406
- "Microsoft 365 Client Secret = %s", self.m365_settings.client_secret
140
+ self.logger.debug(
141
+ "Microsoft 365 Client Secret = <sensitive>",
142
+ # self.settings.m365.client_secret,
407
143
  )
408
- logger.info(
409
- "Microsoft 365 User = %s",
410
- (
411
- self.m365_settings.user
412
- if self.m365_settings.user != ""
413
- else "<not configured>"
414
- ),
144
+ self.logger.info(
145
+ "Microsoft 365 Domain = %s",
146
+ self.settings.m365.domain,
415
147
  )
416
- logger.debug(
417
- "Microsoft 365 Password = %s",
418
- (
419
- self.m365_settings.password
420
- if self.m365_settings.password != ""
421
- else "<not configured>"
422
- ),
423
- )
424
- logger.info(
425
- "Microsoft 365 Domain = %s", self.m365_settings.domain
148
+ self.logger.info(
149
+ "Microsoft 365 Default License SKU = %s",
150
+ self.settings.m365.sku_id,
426
151
  )
427
- logger.info(
428
- "Microsoft 365 Default License SKU = %s", self.m365_settings.sku_id
429
- )
430
- logger.info(
152
+ self.logger.info(
431
153
  "Microsoft 365 Teams App Name = %s",
432
- self.m365_settings.teams_app_name,
154
+ self.settings.m365.teams_app_name,
433
155
  )
434
- logger.info(
156
+ self.logger.info(
435
157
  "Microsoft 365 Teams App External ID = %s",
436
- self.m365_settings.teams_app_external_id,
158
+ self.settings.m365.teams_app_external_id,
159
+ )
160
+ self.logger.info(
161
+ "Microsoft 365 SharePoint App Root Site = %s",
162
+ self.settings.m365.sharepoint_app_root_site,
163
+ )
164
+ self.logger.info(
165
+ "Microsoft 365 SharePoint App Client ID = %s",
166
+ self.settings.m365.sharepoint_app_client_id,
167
+ )
168
+ self.logger.debug(
169
+ "Microsoft 365 SharePoint App Client Secret = <sensitive>",
170
+ # self.settings.m365.sharepoint_app_client_secret,
437
171
  )
438
172
 
439
173
  m365_object = M365(
440
- tenant_id=self.m365_settings.tenant_id,
441
- client_id=self.m365_settings.client_id,
442
- client_secret=self.m365_settings.client_secret,
443
- domain=self.m365_settings.domain,
444
- sku_id=self.m365_settings.sku_id,
445
- teams_app_name=self.m365_settings.teams_app_name,
446
- teams_app_external_id=self.m365_settings.teams_app_external_id,
174
+ tenant_id=self.settings.m365.tenant_id,
175
+ client_id=self.settings.m365.client_id,
176
+ client_secret=self.settings.m365.client_secret,
177
+ domain=self.settings.m365.domain,
178
+ sku_id=self.settings.m365.sku_id,
179
+ teams_app_name=self.settings.m365.teams_app_name,
180
+ teams_app_external_id=self.settings.m365.teams_app_external_id,
181
+ sharepoint_app_root_site=self.settings.m365.sharepoint_app_root_site,
182
+ sharepoint_app_client_id=self.settings.m365.sharepoint_app_client_id,
183
+ sharepoint_app_client_secret=self.settings.m365.sharepoint_app_client_secret,
184
+ logger=self.logger,
447
185
  )
448
186
 
449
187
  if m365_object and m365_object.authenticate():
450
- logger.info("Connected to Microsoft Graph API.")
188
+ self.logger.info("Connected to Microsoft Graph API.")
451
189
  else:
452
- logger.error("Failed to connect to Microsoft Graph API.")
190
+ self.logger.error("Failed to connect to Microsoft Graph API.")
453
191
  return m365_object
454
192
 
455
- logger.info(
456
- "Download M365 Teams App -> '%s' (external ID = %s) from Extended ECM (OTCS)...",
457
- self.m365_settings.teams_app_name,
458
- self.m365_settings.teams_app_external_id,
459
- )
460
-
461
- # Download MS Teams App from OTCS (this has with 23.2 a nasty side-effect
462
- # of unsetting 2 checkboxes on that config page - we reset these checkboxes
463
- # with the settings file "O365Settings.xml"):
464
- response = self.otcs_frontend_object.download_config_file(
465
- "/cs/cs?func=officegroups.DownloadTeamsPackage",
466
- "/tmp/ot.xecm.teams.zip",
467
- )
468
- # this app upload will be done with the user credentials - this is required:
469
- m365_object.authenticate_user(
470
- self.m365_settings.user, self.m365_settings.password
471
- )
472
-
473
- # Check if the app is already installed in the apps catalog
474
- # ideally we want to use the
475
- app_exist = False
476
-
477
- # If the App External ID is provided via Env variable then we
478
- # prefer to use it instead of the App name:
479
- if self.m365_settings.teams_app_external_id:
480
- logger.info(
481
- "Check if M365 Teams App -> '%s' (%s) is already installed in catalog using external app ID...",
482
- self.m365_settings.teams_app_name,
483
- self.m365_settings.teams_app_external_id,
484
- )
485
- response = m365_object.get_teams_apps(
486
- filter_expression="externalId eq '{}'".format(
487
- self.m365_settings.teams_app_external_id
193
+ # Check if the Teams App should be updated, we don't do this always due to the bug described below
194
+ if self.settings.m365.update_teams_app:
195
+ self.logger.info(
196
+ "Download M365 Teams App -> '%s' (external ID = %s) from Extended ECM (OTCS)...",
197
+ self.settings.m365.teams_app_name,
198
+ self.settings.m365.teams_app_external_id,
199
+ )
200
+
201
+ # Download MS Teams App from OTCS (this has with 23.2 a nasty side-effect
202
+ # of unsetting 2 checkboxes on that config page - we reset these checkboxes
203
+ # with the settings file "O365Settings.xml"):
204
+ file_path = os.path.join(tempfile.gettempdir(), "ot.xecm.teams.zip")
205
+ response = self.otcs_frontend_object.download_config_file(
206
+ otcs_url_suffix="/cs/cs?func=officegroups.DownloadTeamsPackage",
207
+ file_path=file_path,
208
+ )
209
+
210
+ # Check if the app is already installed in the apps catalog
211
+ # ideally we want to use the
212
+ app_exist = False
213
+
214
+ # If the App External ID is provided via Env variable then we
215
+ # prefer to use it instead of the App name:
216
+ if self.settings.m365.teams_app_external_id:
217
+ self.logger.info(
218
+ "Check if M365 Teams App -> '%s' (%s) is already installed in catalog using external app ID...",
219
+ self.settings.m365.teams_app_name,
220
+ self.settings.m365.teams_app_external_id,
488
221
  )
489
- )
490
- # this should always be True as ID is unique:
491
- app_exist = m365_object.exist_result_item(
492
- response=response,
493
- key="externalId",
494
- value=self.m365_settings.teams_app_external_id,
495
- )
496
- # If the app could not be found via the external ID we fall back to
497
- # search for the app by name:
498
- if not app_exist:
499
- if self.m365_settings.teams_app_external_id:
500
- logger.info(
501
- "Could not find M365 Teams App using the external ID -> %s. Try to lookup the app by name -> '%s' instead...",
502
- self.m365_settings.teams_app_external_id,
503
- self.m365_settings.teams_app_name,
222
+ response = m365_object.get_teams_apps(
223
+ filter_expression="externalId eq '{}'".format(
224
+ self.settings.m365.teams_app_external_id,
225
+ ),
504
226
  )
505
- logger.info(
506
- "Check if M365 Teams App -> '%s' is already installed in catalog (using app name)...",
507
- self.m365_settings.teams_app_name,
508
- )
509
- response = m365_object.get_teams_apps(
510
- filter_expression="contains(displayName, '{}')".format(
511
- self.m365_settings.teams_app_name
227
+ # this should always be True as ID is unique:
228
+ app_exist = m365_object.exist_result_item(
229
+ response=response,
230
+ key="externalId",
231
+ value=self.settings.m365.teams_app_external_id,
512
232
  )
513
- )
514
- app_exist = m365_object.exist_result_item(
515
- response=response,
516
- key="displayName",
517
- value=self.m365_settings.teams_app_name,
518
- )
519
- if app_exist:
520
- # We double check that we have the effective name of the app
521
- # in the catalog to avoid errors when the app is looked up
522
- # by its wrong name in the customizer automation. This can
523
- # happen if the app is installed manually or the environment
524
- # variable is set to a wrong name.
525
- app_catalog_name = m365_object.get_result_value(response, "displayName")
526
- if app_catalog_name != self.m365_settings.teams_app_name:
527
- logger.warning(
528
- "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!",
529
- app_catalog_name,
530
- self.m365_settings.teams_app_name,
233
+ # If the app could not be found via the external ID we fall back to
234
+ # search for the app by name:
235
+ if not app_exist:
236
+ if self.settings.m365.teams_app_external_id:
237
+ self.logger.info(
238
+ "Could not find M365 Teams App by external ID -> %s. Try to lookup the app by name -> '%s' instead...",
239
+ self.settings.m365.teams_app_external_id,
240
+ self.settings.m365.teams_app_name,
241
+ )
242
+ self.logger.info(
243
+ "Check if M365 Teams App -> '%s' is already installed in catalog (using app name)...",
244
+ self.settings.m365.teams_app_name,
531
245
  )
532
- # Align the name in the settings dict with the existing name in the catalog.
533
- self.m365_settings.teams_app_name = app_catalog_name
534
- # Align the name in the M365 object config dict with the existing name in the catalog.
535
- m365_object.config()["teamsAppName"] = app_catalog_name
536
- app_internal_id = m365_object.get_result_value(
537
- response=response, key="id", index=0
538
- ) # 0 = Index = first item
539
- # Store the internal ID for later use
540
- m365_object.config()["teamsAppInternalId"] = app_internal_id
541
- app_catalog_version = m365_object.get_result_value(
542
- response=response,
543
- key="version",
544
- index=0,
545
- sub_dict_name="appDefinitions",
546
- )
547
- logger.info(
548
- "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...",
549
- self.m365_settings.teams_app_name,
550
- self.m365_settings.teams_app_external_id,
551
- app_internal_id,
552
- app_catalog_version,
553
- )
554
- app_download_version = m365_object.extract_version_from_app_manifest(
555
- app_path="/tmp/ot.xecm.teams.zip"
556
- )
557
- if app_catalog_version < app_download_version:
558
- logger.info(
559
- "Upgrading Extended ECM Teams App in catalog from version -> %s to version -> %s...",
246
+ response = m365_object.get_teams_apps(
247
+ filter_expression="contains(displayName, '{}')".format(
248
+ self.settings.m365.teams_app_name,
249
+ ),
250
+ )
251
+ app_exist = m365_object.exist_result_item(
252
+ response=response,
253
+ key="displayName",
254
+ value=self.settings.m365.teams_app_name,
255
+ )
256
+ if app_exist:
257
+ # We double check that we have the effective name of the app
258
+ # in the catalog to avoid errors when the app is looked up
259
+ # by its wrong name in the customizer automation. This can
260
+ # happen if the app is installed manually or the environment
261
+ # variable is set to a wrong name.
262
+ app_catalog_name = m365_object.get_result_value(response, "displayName")
263
+ if app_catalog_name != self.settings.m365.teams_app_name:
264
+ self.logger.warning(
265
+ "The Extended ECM app name -> '%s' in the M365 Teams catalog does not match the defined app name -> '%s'!",
266
+ app_catalog_name,
267
+ self.settings.m365.teams_app_name,
268
+ )
269
+ # Align the name in the settings dict with the existing name in the catalog.
270
+ self.settings.m365.teams_app_name = app_catalog_name
271
+ # Align the name in the M365 object config dict with the existing name in the catalog.
272
+ m365_object.config()["teamsAppName"] = app_catalog_name
273
+ app_internal_id = m365_object.get_result_value(
274
+ response=response,
275
+ key="id",
276
+ index=0,
277
+ ) # 0 = Index = first item
278
+ # Store the internal ID for later use
279
+ m365_object.config()["teamsAppInternalId"] = app_internal_id
280
+ app_catalog_version = m365_object.get_result_value(
281
+ response=response,
282
+ key="version",
283
+ index=0,
284
+ sub_dict_name="appDefinitions",
285
+ )
286
+ self.logger.info(
287
+ "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...",
288
+ self.settings.m365.teams_app_name,
289
+ self.settings.m365.teams_app_external_id,
290
+ app_internal_id,
560
291
  app_catalog_version,
561
- app_download_version,
562
292
  )
293
+ app_path = os.path.join(tempfile.gettempdir(), "ot.xecm.teams.zip")
294
+ app_download_version = m365_object.extract_version_from_app_manifest(
295
+ app_path=app_path,
296
+ )
297
+ if app_catalog_version < app_download_version:
298
+ self.logger.info(
299
+ "Upgrading Extended ECM Teams App in catalog from version -> %s to version -> %s...",
300
+ app_catalog_version,
301
+ app_download_version,
302
+ )
303
+ app_path = os.path.join(tempfile.gettempdir(), "ot.xecm.teams.zip")
304
+ response = m365_object.upload_teams_app(
305
+ app_path=app_path,
306
+ update_existing_app=True,
307
+ app_catalog_id=app_internal_id,
308
+ )
309
+ app_internal_id = m365_object.get_result_value(
310
+ response=response,
311
+ key="teamsAppId",
312
+ )
313
+ if app_internal_id:
314
+ self.logger.info(
315
+ "Successfully upgraded Extended ECM Teams App -> '%s' (external ID = %s). Internal App ID -> %s",
316
+ self.settings.m365.teams_app_name,
317
+ self.settings.m365.teams_app_external_id,
318
+ app_internal_id,
319
+ )
320
+ # Store the internal ID for later use
321
+ m365_object.config()["teamsAppInternalId"] = app_internal_id
322
+ else:
323
+ self.logger.error(
324
+ "Failed to upgrade Extended ECM Teams App -> '%s' (external ID = %s).",
325
+ self.settings.m365.teams_app_name,
326
+ self.settings.m365.teams_app_external_id,
327
+ )
328
+ else:
329
+ self.logger.info(
330
+ "No upgrade required. The downloaded version -> %s is not newer than the version -> %s which is already in the M365 app catalog.",
331
+ app_download_version,
332
+ app_catalog_version,
333
+ )
334
+ else: # Extended ECM M365 Teams app is not yet installed...
335
+ self.logger.info(
336
+ "Extended Teams ECM App -> '%s' (external ID = %s) is not yet in app catalog. Installing as new app...",
337
+ self.settings.m365.teams_app_name,
338
+ self.settings.m365.teams_app_external_id,
339
+ )
340
+ app_path = os.path.join(tempfile.gettempdir(), "ot.xecm.teams.zip")
563
341
  response = m365_object.upload_teams_app(
564
- app_path="/tmp/ot.xecm.teams.zip",
565
- update_existing_app=True,
566
- app_catalog_id=app_internal_id,
342
+ app_path=app_path,
343
+ update_existing_app=False,
567
344
  )
568
345
  app_internal_id = m365_object.get_result_value(
569
346
  response=response,
570
- key="teamsAppId",
347
+ key="id", # for new installs it is NOT "teamsAppId" but "id" as we use a different M365 Graph API endpoint !!!
571
348
  )
572
349
  if app_internal_id:
573
- logger.info(
574
- "Successfully upgraded Extended ECM Teams App -> %s (external ID = %s). Internal App ID -> %s",
575
- self.m365_settings.teams_app_name,
576
- self.m365_settings.teams_app_external_id,
350
+ self.logger.info(
351
+ "Successfully installed Extended ECM Teams App -> '%s' (external ID = %s). Internal App ID -> %s",
352
+ self.settings.m365.teams_app_name,
353
+ self.settings.m365.teams_app_external_id,
577
354
  app_internal_id,
578
355
  )
579
356
  # Store the internal ID for later use
580
357
  m365_object.config()["teamsAppInternalId"] = app_internal_id
581
358
  else:
582
- logger.error(
583
- "Failed to upgrade Extended ECM Teams App -> %s (external ID = %s).",
584
- self.m365_settings.teams_app_name,
585
- self.m365_settings.teams_app_external_id,
359
+ self.logger.error(
360
+ "Failed to install Extended ECM Teams App -> '%s' (external ID = %s).",
361
+ self.settings.m365.teams_app_name,
362
+ self.settings.m365.teams_app_external_id,
586
363
  )
587
- else:
588
- logger.info(
589
- "No upgrade required. The downloaded version -> %s is not newer than the version -> %s which is already in the M365 app catalog.",
590
- app_download_version,
591
- app_catalog_version,
592
- )
593
- else: # Extended ECM M365 Teams app is not yet installed...
594
- logger.info(
595
- "Extended Teams ECM App -> '%s' (external ID = %s) is not yet in app catalog. Installing as new app...",
596
- self.m365_settings.teams_app_name,
597
- self.m365_settings.teams_app_external_id,
598
- )
599
- response = m365_object.upload_teams_app(
600
- app_path="/tmp/ot.xecm.teams.zip", update_existing_app=False
601
- )
602
- app_internal_id = m365_object.get_result_value(
603
- response=response,
604
- key="id", # for new installs it is NOT "teamsAppId" but "id" as we use a different M365 Graph API endpoint !!!
605
- )
606
- if app_internal_id:
607
- logger.info(
608
- "Successfully installed Extended ECM Teams App -> '%s' (external ID = %s). Internal App ID -> %s",
609
- self.m365_settings.teams_app_name,
610
- self.m365_settings.teams_app_external_id,
611
- app_internal_id,
612
- )
613
- # Store the internal ID for later use
614
- m365_object.config()["teamsAppInternalId"] = app_internal_id
615
- else:
616
- logger.error(
617
- "Failed to install Extended ECM Teams App -> '%s' (external ID = %s).",
618
- self.m365_settings.teams_app_name,
619
- self.m365_settings.teams_app_external_id,
620
- )
621
364
 
622
- # logger.info("======== Upload Outlook Add-In ============")
365
+ # self.logger.info("======== Upload Outlook Add-In ============")
623
366
 
624
367
  # # Download MS Outlook Add-In from OTCS:
625
368
  # MANIFEST_FILE = "/tmp/BusinessWorkspace.Manifest.xml"
@@ -627,13 +370,13 @@ class Customizer:
627
370
  # "/cs/cs?func=outlookaddin.DownloadManifest",
628
371
  # MANIFEST_FILE,
629
372
  # "DeployedContentServer",
630
- # self.otcs_settings.public_url,
373
+ # self.settings.otcs.public_url,
631
374
  # ):
632
- # logger.error("Failed to download M365 Outlook Add-In from Extended ECM!")
375
+ # self.logger.error("Failed to download M365 Outlook Add-In from Extended ECM!")
633
376
  # else:
634
377
  # # THIS IS NOT IMPLEMENTED DUE TO LACK OF M365 GRAPH API SUPPORT!
635
378
  # # Do it manually for now: https://admin.microsoft.com/#/Settings/IntegratedApps
636
- # logger.info("Successfully downloaded M365 Outlook Add-In from Extended ECM to %s", MANIFEST_FILE)
379
+ # self.logger.info("Successfully downloaded M365 Outlook Add-In from Extended ECM to %s", MANIFEST_FILE)
637
380
  # m365_object.upload_outlook_app(MANIFEST_FILE)
638
381
 
639
382
  return m365_object
@@ -645,43 +388,49 @@ class Customizer:
645
388
 
646
389
  Args:
647
390
  None
391
+
648
392
  Returns:
649
- object: CoreShare object or None if the object couldn't be created or
650
- the authentication fails.
393
+ AVTS object:
394
+ Aviator Search object or None if the object couldn't be created or
395
+ the authentication fails.
396
+
651
397
  """
652
398
 
653
- logger.info(
654
- "Aviator Search Base URL = %s", self.avts_settings.base_url
399
+ self.logger.info(
400
+ "Aviator Search Base URL = %s",
401
+ self.settings.avts.base_url,
655
402
  )
656
- logger.info(
657
- "Aviator Search OTDS URL = %s", self.avts_settings.otds_url
403
+ self.logger.info(
404
+ "Aviator Search OTDS URL = %s",
405
+ str(self.settings.otds.url),
658
406
  )
659
- logger.info(
660
- "Aviator Search Client ID = %s", self.avts_settings.client_id
407
+ self.logger.info(
408
+ "Aviator Search Client ID = %s",
409
+ self.settings.avts.client_id,
661
410
  )
662
- logger.debug(
663
- "Aviator Search Client Secret = %s",
664
- self.avts_settings.client_secret,
411
+ self.logger.debug(
412
+ "Aviator Search Client Secret = %s",
413
+ self.settings.avts.client_secret,
665
414
  )
666
- logger.info(
667
- "Aviator Search User ID = %s", self.avts_settings.username
415
+ self.logger.info(
416
+ "Aviator Search User ID = %s",
417
+ self.settings.avts.username,
668
418
  )
669
- logger.debug(
670
- "Aviator Search User Password = %s",
671
- self.avts_settings.password,
419
+ self.logger.debug(
420
+ "Aviator Search User Password = %s",
421
+ self.settings.avts.password,
672
422
  )
673
423
 
674
- avts_object = AVTS(
675
- otds_url=self.avts_settings.otds_url,
676
- base_url=self.avts_settings.base_url,
677
- client_id=self.avts_settings.client_id,
678
- client_secret=self.avts_settings.client_secret,
679
- username=self.avts_settings.username,
680
- password=self.avts_settings.password,
424
+ return AVTS(
425
+ base_url=str(self.settings.avts.base_url),
426
+ client_id=self.settings.avts.client_id,
427
+ client_secret=self.settings.avts.client_secret,
428
+ username=self.settings.avts.username,
429
+ password=self.settings.avts.password,
430
+ otds_url=str(self.settings.otds.url),
431
+ logger=self.logger,
681
432
  )
682
433
 
683
- return avts_object
684
-
685
434
  # end method definition
686
435
 
687
436
  def init_coreshare(self) -> CoreShare:
@@ -690,58 +439,56 @@ class Customizer:
690
439
  Args:
691
440
  None
692
441
  Returns:
693
- object: CoreShare object or None if the object couldn't be created or
694
- the authentication fails.
442
+ CoreShare object:
443
+ Core Share object or None if the object couldn't be created or
444
+ the authentication fails.
445
+
695
446
  """
696
447
 
697
- logger.info(
698
- "Core Share Base URL = %s", self.core_share_settings.base_url
448
+ self.logger.info(
449
+ "Core Share Base URL = %s",
450
+ self.settings.coreshare.base_url,
699
451
  )
700
- logger.info(
701
- "Core Share SSO URL = %s", self.core_share_settings.sso_url
452
+ self.logger.info(
453
+ "Core Share SSO URL = %s",
454
+ self.settings.coreshare.sso_url,
702
455
  )
703
- logger.info(
704
- "Core Share Client ID = %s", self.core_share_settings.client_id
456
+ self.logger.info(
457
+ "Core Share Client ID = %s",
458
+ self.settings.coreshare.client_id,
705
459
  )
706
- logger.debug(
460
+ self.logger.debug(
707
461
  "Core Share Client Secret = %s",
708
- self.core_share_settings.client_secret,
462
+ self.settings.coreshare.client_secret,
709
463
  )
710
- logger.info(
464
+ self.logger.info(
711
465
  "Core Share User = %s",
712
- (
713
- self.core_share_settings.username
714
- if self.core_share_settings.username != ""
715
- else "<not configured>"
716
- ),
466
+ (self.settings.coreshare.username if self.settings.coreshare.username != "" else "<not configured>"),
717
467
  )
718
- logger.debug(
468
+ self.logger.debug(
719
469
  "Core Share Password = %s",
720
- (
721
- self.core_share_settings.password
722
- if self.core_share_settings.password != ""
723
- else "<not configured>"
724
- ),
470
+ (self.settings.coreshare.password if self.settings.coreshare.password != "" else "<not configured>"),
725
471
  )
726
472
 
727
473
  core_share_object = CoreShare(
728
- base_url=self.core_share_settings.base_url,
729
- sso_url=self.core_share_settings.sso_url,
730
- client_id=self.core_share_settings.client_id,
731
- client_secret=self.core_share_settings.client_secret,
732
- username=self.core_share_settings.username,
733
- password=self.core_share_settings.password,
474
+ base_url=self.settings.coreshare.base_url,
475
+ sso_url=self.settings.coreshare.sso_url,
476
+ client_id=self.settings.coreshare.client_id,
477
+ client_secret=self.settings.coreshare.client_secret,
478
+ username=self.settings.coreshare.username,
479
+ password=self.settings.coreshare.password.get_secret_value(),
480
+ logger=self.logger,
734
481
  )
735
482
 
736
483
  if core_share_object and core_share_object.authenticate_admin():
737
- logger.info("Connected to Core Share as Tenant Admin.")
484
+ self.logger.info("Connected to Core Share as Tenant Admin.")
738
485
  else:
739
- logger.error("Failed to connect to Core Share as Tenant Admin.")
486
+ self.logger.error("Failed to connect to Core Share as Tenant Admin.")
740
487
 
741
488
  if core_share_object and core_share_object.authenticate_user():
742
- logger.info("Connected to Core Share as Tenant Service User.")
489
+ self.logger.info("Connected to Core Share as Tenant Service User.")
743
490
  else:
744
- logger.error("Failed to connect to Core Share as Tenant Service User.")
491
+ self.logger.error("Failed to connect to Core Share as Tenant Service User.")
745
492
 
746
493
  return core_share_object
747
494
 
@@ -752,64 +499,66 @@ class Customizer:
752
499
 
753
500
  Args:
754
501
  None
502
+
755
503
  Returns:
756
504
  K8s: K8s object
505
+
757
506
  Side effects:
758
507
  The global variables otcs_replicas_frontend and otcs_replicas_backend are initialized
508
+
759
509
  """
760
510
 
761
- logger.info("Connection parameters Kubernetes (K8s):")
762
- logger.info("K8s inCluster = %s", self.k8s_settings.in_cluster)
763
- logger.info("K8s namespace = %s", self.k8s_settings.namespace)
764
- logger.info(
511
+ self.logger.info("Connection parameters Kubernetes (K8s):")
512
+ self.logger.info("K8s namespace = %s", self.settings.k8s.namespace)
513
+ self.logger.info(
765
514
  "K8s kubeconfig file = %s",
766
- self.k8s_settings.kubeconfig_file,
515
+ self.settings.k8s.kubeconfig_file,
767
516
  )
768
517
 
769
518
  k8s_object = K8s(
770
- in_cluster=self.k8s_settings.in_cluster,
771
- kubeconfig_file=self.k8s_settings.kubeconfig_file,
772
- namespace=self.k8s_settings.namespace,
519
+ kubeconfig_file=self.settings.k8s.kubeconfig_file,
520
+ namespace=self.settings.k8s.namespace,
521
+ logger=self.logger,
773
522
  )
774
523
  if k8s_object:
775
- logger.info("Kubernetes API is ready now.")
524
+ self.logger.info("Kubernetes API is ready now.")
776
525
  else:
777
- logger.error("Cannot establish connection to Kubernetes.")
526
+ self.logger.error("Cannot establish connection to Kubernetes.")
778
527
 
779
528
  # Get number of replicas for frontend:
780
529
  otcs_frontend_scale = k8s_object.get_stateful_set_scale(
781
- self.otcs_settings.k8s_statefulset_frontend
530
+ sts_name=self.settings.k8s.sts_otcs_frontend,
782
531
  )
783
532
  if not otcs_frontend_scale:
784
- logger.error(
533
+ self.logger.error(
785
534
  "Cannot find Kubernetes Stateful Set -> '%s' for OTCS Frontends!",
786
- self.otcs_settings.k8s_statefulset_frontend,
535
+ self.settings.k8s.sts_otcs_frontend,
787
536
  )
788
537
  sys.exit()
789
538
 
790
- self.otcs_settings.replicas_frontend = otcs_frontend_scale.spec.replicas # type: ignore
791
- logger.info(
539
+ self.settings.k8s.sts_otcs_frontend_replicas = otcs_frontend_scale.spec.replicas
540
+ self.logger.info(
792
541
  "Stateful Set -> '%s' has -> %s replicas",
793
- self.otcs_settings.k8s_statefulset_frontend,
794
- self.otcs_settings.replicas_frontend,
542
+ self.settings.k8s.sts_otcs_frontend,
543
+ self.settings.k8s.sts_otcs_frontend_replicas,
795
544
  )
796
545
 
797
546
  # Get number of replicas for backend:
798
547
  otcs_backend_scale = k8s_object.get_stateful_set_scale(
799
- self.otcs_settings.k8s_statefulset_backend
548
+ sts_name=self.settings.k8s.sts_otcs_admin,
800
549
  )
801
550
  if not otcs_backend_scale:
802
- logger.error(
551
+ self.logger.error(
803
552
  "Cannot find Kubernetes Stateful Set -> '%s' for OTCS Backends!",
804
- self.otcs_settings.k8s_statefulset_backend,
553
+ self.settings.k8s.sts_otcs_admin,
805
554
  )
806
555
  sys.exit()
807
556
 
808
- self.otcs_settings.replicas_backend = otcs_backend_scale.spec.replicas # type: ignore
809
- logger.info(
557
+ self.settings.k8s.sts_otcs_admin_replicas = otcs_backend_scale.spec.replicas
558
+ self.logger.info(
810
559
  "Stateful Set -> '%s' has -> %s replicas",
811
- self.otcs_settings.k8s_statefulset_backend,
812
- self.otcs_settings.replicas_backend,
560
+ self.settings.k8s.sts_otcs_admin,
561
+ self.settings.k8s.sts_otcs_admin_replicas,
813
562
  )
814
563
 
815
564
  return k8s_object
@@ -821,53 +570,68 @@ class Customizer:
821
570
 
822
571
  Args:
823
572
  None
573
+
824
574
  Returns:
825
- object: OTDS object
826
- """
575
+ OTDS:
576
+ The OTDS object
827
577
 
828
- logger.info("Connection parameters OTDS:")
829
- logger.info("OTDS Protocol = %s", self.otds_settings.protocol)
830
- logger.info("OTDS Public Protocol = %s", self.otds_settings.public_protocol)
831
- logger.info("OTDS Hostname = %s", self.otds_settings.hostname)
832
- logger.info("OTDS Public URL = %s", self.otds_settings.public_url)
833
- logger.info("OTDS Port = %s", str(self.otds_settings.port))
834
- logger.info("OTDS Admin User = %s", self.otds_settings.username)
835
- logger.debug("OTDS Admin Password = %s", self.otds_settings.password)
836
- logger.debug("OTDS Ticket = %s", self.otds_settings.otds_ticket)
837
- logger.info("OTDS Admin Partition = %s", self.otds_settings.admin_partition)
578
+ """
838
579
 
839
- otds_object = OTDS(
840
- protocol=self.otds_settings.protocol,
841
- hostname=self.otds_settings.hostname,
842
- port=self.otds_settings.port,
843
- username=self.otds_settings.username,
844
- password=self.otds_settings.password,
845
- otds_ticket=self.otds_settings.otds_ticket,
846
- bindPassword=self.otds_settings.bindPassword
580
+ self.logger.info("Connection parameters OTDS:")
581
+ self.logger.info("OTDS Protocol = %s", self.settings.otds.url.scheme)
582
+ self.logger.info(
583
+ "OTDS Hostname = %s",
584
+ self.settings.otds.url_internal.host,
585
+ )
586
+ self.logger.info(
587
+ "OTDS Port = %s",
588
+ str(self.settings.otds.url.port),
589
+ )
590
+ self.logger.info("OTDS Public Protocol = %s", self.settings.otds.url.scheme)
591
+ self.logger.info("OTDS Public URL = %s", self.settings.otds.url.host)
592
+ self.logger.info("OTDS Public Port = %s", self.settings.otds.url.port)
593
+ self.logger.info("OTDS Admin User = %s", self.settings.otds.username)
594
+ self.logger.debug("OTDS Admin Password = %s", self.settings.otds.password)
595
+ self.logger.debug("OTDS Ticket = %s", self.settings.otds.ticket)
596
+ self.logger.info(
597
+ "OTDS Admin Partition = %s",
598
+ self.settings.otds.admin_partition,
847
599
  )
848
600
 
849
- logger.info("Authenticating to OTDS...")
601
+ otds_object = OTDS(
602
+ protocol=self.settings.otds.url_internal.scheme,
603
+ hostname=self.settings.otds.url_internal.host,
604
+ port=self.settings.otds.url_internal.port,
605
+ username=self.settings.otds.username,
606
+ password=self.settings.otds.password.get_secret_value(),
607
+ otds_ticket=self.settings.otds.ticket,
608
+ bind_password=self.settings.otds.bind_password.get_secret_value(),
609
+ admin_partition=self.settings.otds.admin_partition,
610
+ logger=self.logger,
611
+ )
612
+
613
+ self.logger.info("Authenticating to OTDS...")
850
614
  otds_cookie = otds_object.authenticate()
851
615
  while otds_cookie is None:
852
- logger.warning("Waiting 30 seconds for OTDS to become ready...")
616
+ self.logger.info("Waiting 30 seconds for OTDS to become ready...")
853
617
  time.sleep(30)
854
618
  otds_cookie = otds_object.authenticate()
855
- logger.info("OTDS is ready now.")
619
+ self.logger.info("OTDS is ready now.")
856
620
 
857
- logger.info("Enable OTDS audit...")
621
+ self.logger.info("Enable OTDS audit...")
858
622
 
859
- if self.otds_settings.enable_audit:
623
+ if self.settings.otds.enable_audit:
860
624
  otds_object.enable_audit()
861
625
 
862
- if self.otds_settings.disable_password_policy:
863
- logger.info("Disable OTDS password expiry...")
626
+ if self.settings.otds.disable_password_policy:
627
+ self.logger.info("Disable OTDS password expiry...")
864
628
  # Setting the value to 0 disables password expiry.
865
629
  # The default is 90 days and we may have Terrarium
866
630
  # instances that are running longer than that. This
867
631
  # avoids problems with customerizer re-runs of
868
632
  # instances that are > 90 days old.
869
633
  otds_object.update_password_policy(
870
- update_values={"passwordMaximumDuration": 0}
634
+ update_values={"passwordMaximumDuration": 0},
871
635
  )
872
636
 
873
637
  return otds_object
@@ -876,76 +640,83 @@ class Customizer:
876
640
 
877
641
  def init_otac(self) -> OTAC:
878
642
  """Initialize the OTAC object and parameters.
879
- Configure the Archive Server as a known server
880
- if environment variable OTAC_KNOWN_SERVER is set.
643
+
644
+ Configure the Archive Server as a known server
645
+ if environment variable OTAC_KNOWN_SERVER is set.
881
646
 
882
647
  Args: None
883
- Return:
884
- OTAC object
648
+
649
+ Returns:
650
+ The OTAC object.
651
+
885
652
  """
886
653
 
887
- logger.info("Connection parameters OTAC:")
888
- logger.info("OTAC Protocol = %s", self.otac_settings.protocol)
889
- logger.info("OTAC Hostname = %s", self.otac_settings.hostname)
890
- logger.info("OTAC Public URL = %s", self.otac_settings.public_url)
891
- logger.info("OTAC Port = %s", str(self.otac_settings.port))
892
- logger.info("OTAC Admin User = %s", self.otac_settings.admin)
893
- logger.debug("OTAC Admin Password = %s", self.otac_settings.password)
894
- logger.info(
895
- "OTAC Known Server = %s",
896
- (
897
- self.otac_settings.known_server
898
- if self.otac_settings.known_server != ""
899
- else "<not configured>"
900
- ),
654
+ self.logger.info("Connection parameters OTAC:")
655
+ self.logger.info("OTAC URL = %s", str(self.settings.otac.url))
656
+ self.logger.info("OTAC URL internal = %s", str(self.settings.otac.url_internal))
657
+ self.logger.info("OTAC Admin User = %s", self.settings.otac.username)
658
+ self.logger.debug("OTAC Admin Password = %s", self.settings.otac.password)
659
+ self.logger.info(
660
+ "OTAC Known Server = %s",
661
+ (self.settings.otac.known_server if self.settings.otac.known_server != "" else "<not configured>"),
901
662
  )
902
663
 
903
664
  otac_object = OTAC(
904
- self.otac_settings.protocol,
905
- self.otac_settings.hostname,
906
- int(self.otac_settings.port),
907
- self.otac_settings.admin,
908
- self.otac_settings.password,
909
- self.otds_settings.username,
910
- self.otds_settings.password,
911
- )
665
+ self.settings.otac.url_internal.scheme,
666
+ self.settings.otac.url_internal.host,
667
+ int(self.settings.otac.url_internal.port),
668
+ self.settings.otac.username,
669
+ self.settings.otac.password.get_secret_value(),
670
+ self.settings.otds.username,
671
+ self.settings.otds.password.get_secret_value(),
672
+ logger=self.logger,
673
+ )
674
+
675
+ self.logger.info("Authenticating to OTAC...")
676
+ otac_cookie = otac_object.authenticate()
677
+ while otac_cookie is None:
678
+ self.logger.info("Waiting 30 seconds for OTAC to become ready...")
679
+ time.sleep(30)
680
+ otac_cookie = otac_object.authenticate()
681
+ self.logger.info("OTAC is ready now.")
912
682
 
913
683
  # This is a work-around as OTCS container automation is not
914
684
  # enabling the certificate reliable.
915
685
  response = otac_object.enable_certificate(
916
- cert_name="SP_otcs-admin-0", cert_type="ARC"
686
+ cert_name="SP_otcs-admin-0",
687
+ cert_type="ARC",
917
688
  )
918
689
  if not response:
919
- logger.error("Failed to enable OTAC certificate for Extended ECM!")
690
+ self.logger.error("Failed to enable OTAC certificate for OTCS!")
920
691
  else:
921
- logger.info("Successfully enabled OTAC certificate for Extended ECM!")
692
+ self.logger.info("Successfully enabled OTAC certificate for OTCS!")
922
693
 
923
694
  # is there a known server configured for Archive Center (to sync content with)
924
- if otac_object and self.otac_settings.known_server != "":
695
+ if otac_object and self.settings.otac.known_server != "":
925
696
  # wait until the OTAC pod is in ready state
926
- logger.info("Waiting for Archive Center to become ready...")
927
- self.k8s_object.wait_pod_condition(self.otac_settings.k8s_pod_name, "Ready")
697
+ self.logger.info("Waiting for Archive Center to become ready...")
698
+ self.k8s_object.wait_pod_condition(self.settings.k8s.pod_otac, "Ready")
928
699
 
929
- logger.info("Configure known host for Archive Center...")
700
+ self.logger.info("Configure known host for Archive Center...")
930
701
  response = otac_object.exec_command(
931
- f"cf_create_host {self.otac_settings.known_server} 0 /archive 8080 8090"
702
+ f"cf_create_host {self.settings.otac.known_server} 0 /archive 8080 8090",
932
703
  )
933
704
  if not response or not response.ok:
934
- logger.error("Failed to configure known host for Archive Center!")
705
+ self.logger.error("Failed to configure known host for Archive Center!")
935
706
 
936
- logger.info("Configure host alias for Archive Center...")
707
+ self.logger.info("Configure host alias for Archive Center...")
937
708
  response = otac_object.exec_command(
938
- f"cf_set_variable MY_HOST_ALIASES {self.otac_settings.k8s_pod_name},{self.otac_settings.public_url},otac DS"
709
+ f"cf_set_variable MY_HOST_ALIASES {self.settings.k8s.pod_otac},{self.settings.otac.url.host},otac DS",
939
710
  )
940
711
  if not response or not response.ok:
941
- logger.error("Failed to configure host alias for Archive Center!")
712
+ self.logger.error("Failed to configure host alias for Archive Center!")
942
713
 
943
714
  # Restart the spawner in Archive Center:
944
- logger.info("Restart Archive Center Spawner...")
715
+ self.logger.info("Restart Archive Center Spawner...")
945
716
  self.restart_otac_service()
946
717
  else:
947
- logger.info(
948
- "Skip configuration of known host for Archive Center (OTAC_KNOWN_SERVER is not set)."
718
+ self.logger.info(
719
+ "Skip configuration of known host for Archive Center (OTAC_KNOWN_SERVER is not set).",
949
720
  )
950
721
 
951
722
  return otac_object
@@ -954,116 +725,126 @@ class Customizer:
954
725
 
955
726
  def init_otcs(
956
727
  self,
957
- hostname: str,
958
- port: int,
959
- partition_name: str,
960
- resource_name: str,
728
+ url: HttpUrl,
961
729
  ) -> OTCS:
962
730
  """Initialize the OTCS class and parameters and authenticate at OTCS once it is ready.
963
731
 
964
732
  Args:
965
- hostname (str): OTCS hostname
966
- port (int): port number of OTCS
967
- partition_name (str): name of OTDS Partition for Extended ECM users
968
- resource_name (str): name of OTDS resource for Extended ECM
733
+ url (HttpURL):
734
+ The OTCS URL.
735
+
969
736
  Returns:
970
- OTCS: OTCS object
737
+ OTCS:
738
+ The OTCS object
739
+
971
740
  """
972
741
 
973
- logger.info("Connection parameters OTCS (Extended ECM):")
974
- logger.info("OTCS Protocol = %s", self.otcs_settings.protocol)
975
- logger.info(
976
- "OTCS Public Protocol = %s", self.otcs_settings.public_protocol
977
- )
978
- logger.info("OTCS Hostname = %s", hostname)
979
- logger.info("OTCS Public URL = %s", self.otcs_settings.public_url)
980
- logger.info("OTCS Port = %s", str(port))
981
- logger.info("OTCS Admin User = %s", self.otcs_settings.admin)
982
- logger.debug("OTCS Admin Password = %s", self.otcs_settings.password)
983
- logger.info("OTCS User Partition = %s", partition_name)
984
- logger.info("OTCS Resource Name = %s", resource_name)
985
- logger.info(
986
- "OTCS User Default License = %s", self.otcs_settings.license_feature
987
- )
988
- logger.info(
742
+ self.logger.info("Connection parameters OTCS (Extended ECM):")
743
+ self.logger.info("OTCS URL = %s", str(self.settings.otcs.url))
744
+ self.logger.info(
745
+ "OTCS Frontend URL = %s",
746
+ str(self.settings.otcs.url_frontend),
747
+ )
748
+ self.logger.info(
749
+ "OTCS Backend URL = %s",
750
+ str(self.settings.otcs.url_backend),
751
+ )
752
+ self.logger.info("OTCS Admin User = %s", self.settings.otcs.username)
753
+ self.logger.debug(
754
+ "OTCS Admin Password = %s",
755
+ self.settings.otcs.password,
756
+ )
757
+ self.logger.info(
758
+ "OTCS User Partition = %s",
759
+ self.settings.otcs.partition,
760
+ )
761
+ self.logger.info(
762
+ "OTCS Resource Name = %s",
763
+ self.settings.otcs.resource_name,
764
+ )
765
+ self.logger.info(
766
+ "OTCS User Default License = %s",
767
+ self.settings.otcs.license_feature,
768
+ )
769
+ self.logger.info(
989
770
  "OTCS K8s Frontend Pods = %s",
990
- self.otcs_settings.k8s_statefulset_frontend,
771
+ self.settings.k8s.sts_otcs_frontend,
991
772
  )
992
- logger.info(
773
+ self.logger.info(
993
774
  "OTCS K8s Backend Pods = %s",
994
- self.otcs_settings.k8s_statefulset_backend,
775
+ self.settings.k8s.sts_otcs_admin,
995
776
  )
996
- logger.info(
777
+ self.logger.info(
997
778
  "FEME URI = %s",
998
- self.otcs_settings.feme_uri,
779
+ self.settings.otcs.feme_uri,
999
780
  )
1000
781
 
1001
- logger.debug("Checking if OTCS object has already been initialized")
1002
-
1003
- otds_ticket = (
1004
- self.otds_object.cookie()["OTDSTicket"] if self.otds_object else None
1005
- )
782
+ otds_ticket = self.otds_object.cookie()["OTDSTicket"] if self.otds_object else None
1006
783
  otcs_object = OTCS(
1007
- self.otcs_settings.protocol,
1008
- hostname,
1009
- int(port),
1010
- self.otcs_settings.public_protocol + "://" + self.otcs_settings.public_url,
1011
- self.otcs_settings.admin,
1012
- self.otcs_settings.password,
1013
- partition_name,
1014
- resource_name,
784
+ protocol=url.scheme,
785
+ hostname=url.host,
786
+ port=url.port,
787
+ public_url=self.settings.otcs.url.scheme + "://" + self.settings.otcs.url.host,
788
+ username=self.settings.otcs.username,
789
+ password=self.settings.otcs.password.get_secret_value(),
790
+ user_partition=self.settings.otcs.partition,
791
+ resource_name=self.settings.otcs.resource_name,
1015
792
  otds_ticket=otds_ticket,
1016
- base_path=self.otcs_settings.base_path,
1017
- feme_uri=self.otcs_settings.feme_uri,
793
+ base_path=self.settings.otcs.base_path,
794
+ feme_uri=self.settings.otcs.feme_uri,
795
+ logger=self.logger,
1018
796
  )
1019
797
 
1020
798
  # It is important to wait for OTCS to be configured - otherwise we
1021
799
  # may interfere with the OTCS container automation and run into errors
1022
- logger.info("Wait for OTCS to be configured...")
800
+ self.logger.info("Wait for OTCS to be configured...")
1023
801
  otcs_configured = otcs_object.is_configured()
1024
802
  while not otcs_configured:
1025
- logger.warning("OTCS is not configured yet. Waiting 30 seconds...")
803
+ self.logger.warning("OTCS is not configured yet. Waiting 30 seconds...")
1026
804
  time.sleep(30)
1027
805
  otcs_configured = otcs_object.is_configured()
1028
- logger.info("OTCS is configured now.")
806
+ self.logger.info("OTCS is configured now.")
1029
807
 
1030
- logger.info("Authenticating to OTCS...")
808
+ self.logger.info("Authenticating to OTCS...")
1031
809
  otcs_cookie = otcs_object.authenticate()
1032
810
  while otcs_cookie is None:
1033
- logger.warning("Waiting 30 seconds for OTCS to become ready...")
811
+ self.logger.info("Waiting 30 seconds for OTCS to become ready...")
1034
812
  time.sleep(30)
1035
813
  otcs_cookie = otcs_object.authenticate()
1036
- logger.info("OTCS is ready now.")
814
+ self.logger.info("OTCS is ready now.")
1037
815
 
1038
- # if self.otcs_settings.update_admin_user:
1039
- # Set first name and last name of Admin user (ID = 1000):
1040
- # otcs_object.update_user(1000, field="first_name", value="Terrarium")
1041
- # otcs_object.update_user(1000, field="last_name", value="Admin")
816
+ # Now we should be able to get the OTCS resource ID from OTDS:
817
+ otcs_resource = self.otds_object.get_resource(
818
+ self.settings.otcs.resource_name,
819
+ )
820
+ if not otcs_resource or "resourceID" not in otcs_resource:
821
+ self.logger.error(
822
+ "Cannot get OTCS resource ID from OTDS for resource name -> '%s'!", self.settings.otcs.resource_name
823
+ )
824
+ return otcs_object
825
+ otcs_resource_id = otcs_resource["resourceID"]
826
+ otcs_object.set_resource_id(resource_id=otcs_resource_id)
827
+ self.logger.info(
828
+ "OTCS has resource ID -> '%s' for resource name -> '%s'", otcs_resource_id, self.settings.otcs.resource_name
829
+ )
1042
830
 
1043
831
  if "OTCS_RESSOURCE_ID" not in self.settings.placeholder_values:
1044
- self.settings.placeholder_values["OTCS_RESSOURCE_ID"] = (
1045
- self.otds_object.get_resource(self.otcs_settings.resource_name)[
1046
- "resourceID"
1047
- ]
1048
- )
1049
- logger.debug(
1050
- "Placeholder values after OTCS init = %s",
832
+ self.settings.placeholder_values["OTCS_RESSOURCE_ID"] = otcs_resource_id
833
+ self.logger.debug(
834
+ "Placeholder values after OTCS init -> %s",
1051
835
  self.settings.placeholder_values,
1052
836
  )
1053
837
 
1054
- if self.otawp_settings.enabled:
1055
- otcs_resource = self.otds_object.get_resource(
1056
- self.otcs_settings.resource_name
1057
- )
1058
- otcs_resource["logoutURL"] = (
1059
- f"{self.otawp_settings.public_protocol}://{self.otawp_settings.public_url}/home/system/wcp/sso/sso_logout.htm"
838
+ if self.settings.otawp.enabled:
839
+ otcs_resource["logoutURL"] = "{}://{}/home/system/wcp/sso/sso_logout.htm".format(
840
+ self.settings.otawp.public_protocol, self.settings.otawp.public_url
1060
841
  )
1061
842
  otcs_resource["logoutMethod"] = "GET"
1062
843
 
1063
844
  self.otds_object.update_resource(name="cs", resource=otcs_resource)
1064
845
 
1065
846
  # Allow impersonation of the resource for all users:
1066
- self.otds_object.impersonate_resource(resource_name)
847
+ self.otds_object.impersonate_resource(self.settings.otcs.resource_name)
1067
848
 
1068
849
  return otcs_object
1069
850
 
@@ -1073,49 +854,67 @@ class Customizer:
1073
854
  """Initialize the OTIV (Intelligent Viewing) object and its OTDS settings.
1074
855
 
1075
856
  Args:
857
+ None
858
+
1076
859
  Returns:
1077
- objects: OTIV object
860
+ OTIV:
861
+ The OTIV object.
862
+
1078
863
  """
1079
864
 
1080
- logger.info("Parameters for OTIV (Intelligent Viewing):")
1081
- logger.info("OTDS Resource Name = %s", self.otiv_settings.resource_name)
1082
- logger.info("OTIV License File = %s", self.otiv_settings.license_file)
1083
- logger.info("OTIV Product Name = %s", self.otiv_settings.product_name)
1084
- logger.info(
1085
- "OTIV Product Description = %s", self.otiv_settings.product_description
865
+ self.logger.info("Parameters for OTIV (Intelligent Viewing):")
866
+ self.logger.info(
867
+ "OTDS Resource Name = %s",
868
+ self.settings.otiv.resource_name,
869
+ )
870
+ self.logger.info(
871
+ "OTIV License File = %s",
872
+ self.settings.otiv.license_file,
873
+ )
874
+ self.logger.info(
875
+ "OTIV Product Name = %s",
876
+ self.settings.otiv.product_name,
877
+ )
878
+ self.logger.info(
879
+ "OTIV Product Description = %s",
880
+ self.settings.otiv.product_description,
881
+ )
882
+ self.logger.info(
883
+ "OTIV License Feature = %s",
884
+ self.settings.otiv.license_feature,
1086
885
  )
1087
- logger.info("OTIV License Feature = %s", self.otiv_settings.license_feature)
1088
886
 
1089
887
  otiv_object = OTIV(
1090
- resource_name=self.otiv_settings.resource_name,
1091
- product_name=self.otiv_settings.product_name,
1092
- product_description=self.otiv_settings.product_description,
1093
- license_file=self.otiv_settings.license_file,
1094
- default_license=self.otiv_settings.license_feature,
888
+ resource_name=self.settings.otiv.resource_name,
889
+ product_name=self.settings.otiv.product_name,
890
+ product_description=self.settings.otiv.product_description,
891
+ license_file=self.settings.otiv.license_file,
892
+ default_license=self.settings.otiv.license_feature,
893
+ logger=self.logger,
1095
894
  )
1096
895
 
1097
- otiv_resource = self.otds_object.get_resource(self.otiv_settings.resource_name)
896
+ otiv_resource = self.otds_object.get_resource(self.settings.otiv.resource_name)
1098
897
  while otiv_resource is None:
1099
- logger.warning(
898
+ self.logger.info(
1100
899
  "OTDS Resource -> %s for Intelligent Viewing not found. OTIV may not be ready. Wait 30 sec...",
1101
- self.otiv_settings.resource_name,
900
+ self.settings.otiv.resource_name,
1102
901
  )
1103
902
  time.sleep(30)
1104
903
  otiv_resource = self.otds_object.get_resource(
1105
- self.otiv_settings.resource_name
904
+ self.settings.otiv.resource_name,
1106
905
  )
1107
906
 
1108
907
  otiv_license = self.otds_object.add_license_to_resource(
1109
- self.otiv_settings.license_file,
1110
- self.otiv_settings.product_name,
1111
- self.otiv_settings.product_description,
1112
- otiv_resource["resourceID"],
908
+ path_to_license_file=self.settings.otiv.license_file,
909
+ product_name=self.settings.otiv.product_name,
910
+ product_description=self.settings.otiv.product_description,
911
+ resource_id=otiv_resource["resourceID"],
1113
912
  )
1114
913
  if not otiv_license:
1115
- logger.info(
1116
- "Couldn't apply license -> %s for product -> %s. Intelligent Viewing may not be deployed!",
1117
- self.otiv_settings.license_file,
1118
- self.otiv_settings.product_name,
914
+ self.logger.info(
915
+ "Couldn't apply license -> %s for product -> '%s'. Intelligent Viewing may not be deployed!",
916
+ self.settings.otiv.license_file,
917
+ self.settings.otiv.product_name,
1119
918
  )
1120
919
  return None
1121
920
 
@@ -1135,7 +934,7 @@ class Customizer:
1135
934
  )
1136
935
  time.sleep(30)
1137
936
 
1138
- logger.info("OTDS user iv-publisher -> updating oTType=ServiceUser")
937
+ self.logger.info("OTDS user iv-publisher -> updating oTType=ServiceUser")
1139
938
 
1140
939
  return otiv_object
1141
940
 
@@ -1146,536 +945,335 @@ class Customizer:
1146
945
 
1147
946
  Args:
1148
947
  None
948
+
1149
949
  Returns:
1150
- object: OTPD (PowerDocs) object
950
+ OTPD:
951
+ The OTPD (PowerDocs) object.
952
+
1151
953
  """
1152
954
 
1153
- logger.info("Connection parameters OTPD (PowerDocs):")
1154
- logger.info("OTPD Protocol = %s", self.otpd_settings.protocol)
1155
- logger.info("OTPD Hostname = %s", self.otpd_settings.hostname)
1156
- logger.info("OTPD Port = %s", str(self.otpd_settings.port))
1157
- logger.info("OTPD API User = %s", self.otpd_settings.user)
1158
- logger.info("OTPD Tenant = %s", self.otpd_settings.tenant)
1159
- logger.info(
955
+ self.logger.info("Connection parameters OTPD (PowerDocs):")
956
+ self.logger.info(
957
+ "OTPD Protocol = %s",
958
+ self.settings.otpd.url.scheme,
959
+ )
960
+ self.logger.info("OTPD Hostname = %s", self.settings.otpd.url.host)
961
+ self.logger.info("OTPD Port = %s", self.settings.otpd.url.port)
962
+ self.logger.info("OTPD API User = %s", self.settings.otpd.username)
963
+ self.logger.info("OTPD Tenant = %s", self.settings.otpd.tenant)
964
+ self.logger.info(
1160
965
  "OTPD Database Import File = %s",
1161
- (
1162
- self.otpd_settings.db_importfile
1163
- if self.otpd_settings.db_importfile != ""
1164
- else "<not configured>"
1165
- ),
966
+ (self.settings.otpd.db_importfile if self.settings.otpd.db_importfile != "" else "<not configured>"),
1166
967
  )
1167
- logger.info("OTPD K8s Pod Name = %s", self.otpd_settings.k8s_pod_name)
968
+ self.logger.info("OTPD K8s Pod Name = %s", self.settings.k8s.pod_otpd)
1168
969
 
1169
970
  otpd_object = OTPD(
1170
- self.otpd_settings.protocol,
1171
- self.otpd_settings.hostname,
1172
- int(self.otpd_settings.port),
1173
- self.otpd_settings.user,
1174
- self.otpd_settings.password,
971
+ self.settings.otpd.url.scheme,
972
+ self.settings.otpd.url.host,
973
+ self.settings.otpd.url.port,
974
+ self.settings.otpd.username,
975
+ self.settings.otpd.password,
976
+ logger=self.logger,
1175
977
  )
1176
978
 
1177
979
  # wait until the OTPD pod is in ready state
1178
- self.k8s_object.wait_pod_condition(self.otpd_settings.k8s_pod_name, "Ready")
980
+ self.k8s_object.wait_pod_condition(self.settings.k8s.pod_otpd, "Ready")
1179
981
 
1180
982
  # We have a race condition here. Even if the pod is ready
1181
983
  # it may not yet have fully initialized its database.
1182
984
  # Then the "apply_setting()" calls below may fail with
1183
985
  # an error. This should be improved in the future. For now
1184
986
  # we just wait a minute hoping that the DB is initialized then.
1185
- logger.info("Wait some time for PowerDocs database to be initialized...")
1186
- time.sleep(60)
1187
- logger.info("Configure some basic PowerDocs settings...")
987
+ # self.logger.info("Wait some time for PowerDocs database to be initialized...")
988
+ # time.sleep(60)
989
+ # self.logger.info("Configure some basic PowerDocs settings...")
1188
990
 
1189
991
  # Fix settings for local Kubernetes deployments.
1190
992
  # Unclear why this is not the default.
1191
- if otpd_object:
1192
- otpd_object.apply_setting("LocalOtdsUrl", "http://otds/otdsws")
1193
- otpd_object.apply_setting(
1194
- "LocalApplicationServerUrlForContentManager",
1195
- "http://localhost:8080/c4ApplicationServer",
1196
- self.otpd_settings.tenant,
1197
- )
993
+ # if otpd_object:
994
+ # otpd_object.apply_setting("LocalOtdsUrl", "http://otds/otdsws")
995
+ # otpd_object.apply_setting(
996
+ # "LocalApplicationServerUrlForContentManager",
997
+ # "http://localhost:8080/c4ApplicationServer",
998
+ # self.settings.otpd.tenant,
999
+ # )
1198
1000
 
1199
1001
  return otpd_object
1200
1002
 
1201
1003
  # end function definition
1202
1004
 
1203
- def init_otawp(self):
1204
- """Initialize OTDS for Appworks Platform
1205
- Args:
1206
- Return: None
1005
+ def init_otawp(self) -> OTAWP:
1006
+ """Initialize OTDS for Appworks Platform.
1007
+
1008
+ Returns:
1009
+ OTAWP:
1010
+ The AppWorks Platform object.
1011
+
1207
1012
  """
1208
1013
 
1209
- logger.info("Connection parameters OTAWP:")
1210
- logger.info("OTAWP Enabled = %s", str(self.otawp_settings.enabled))
1211
- logger.info("OTAWP Resource = %s", self.otawp_settings.resource_name)
1212
- logger.info("OTAWP Access Role = %s", self.otawp_settings.access_role_name)
1213
- logger.info("OTAWP Admin User = %s", self.otawp_settings.admin)
1214
- logger.debug("OTAWP Password = %s", self.otawp_settings.password)
1215
- logger.info("OTAWP K8s Stateful Set = %s", self.otawp_settings.k8s_statefulset)
1216
- logger.info("OTAWP K8s Config Map = %s", self.otawp_settings.k8s_configmap)
1014
+ self.logger.info("Connection parameters OTAWP:")
1015
+ self.logger.info(
1016
+ "OTAWP Enabled = %s",
1017
+ str(self.settings.otawp.enabled),
1018
+ )
1019
+ self.logger.info("OTAWP URL = %s", self.settings.otawp.public_url)
1020
+ self.logger.info(
1021
+ "OTAWP Resource = %s",
1022
+ self.settings.otawp.resource_name,
1023
+ )
1024
+ self.logger.info(
1025
+ "OTAWP Access Role = %s",
1026
+ self.settings.otawp.access_role_name,
1027
+ )
1028
+ self.logger.info("OTAWP Admin User = %s", self.settings.otawp.username)
1029
+ self.logger.debug("OTAWP Password = %s", self.settings.otawp.password)
1030
+ self.logger.info("OTAWP Organization = %s", self.settings.otawp.organization)
1031
+ self.logger.info("OTAWP K8s Stateful Set = %s", self.settings.k8s.sts_otawp)
1032
+ self.logger.info("OTAWP K8s Config Map = %s", self.settings.k8s.cm_otawp)
1217
1033
 
1218
- logger.info(
1034
+ self.logger.info(
1219
1035
  "Wait for OTCS to create its OTDS resource with name -> '%s'...",
1220
- self.otcs_settings.resource_name,
1036
+ self.settings.otcs.resource_name,
1221
1037
  )
1222
1038
 
1039
+ organization = "system"
1040
+
1223
1041
  # Loop to wait for OTCS to create its OTDS resource
1224
1042
  # (we need it to update the AppWorks K8s Config Map):
1225
- otcs_resource = self.otds_object.get_resource(self.otcs_settings.resource_name)
1043
+ otcs_resource = self.otds_object.get_resource(self.settings.otcs.resource_name)
1226
1044
  while otcs_resource is None:
1227
- logger.warning(
1045
+ self.logger.warning(
1228
1046
  "OTDS resource for Content Server with name -> '%s' does not exist yet. Waiting...",
1229
- self.otcs_settings.resource_name,
1047
+ self.settings.otcs.resource_name,
1230
1048
  )
1231
1049
  time.sleep(30)
1232
1050
  otcs_resource = self.otds_object.get_resource(
1233
- self.otcs_settings.resource_name
1051
+ self.settings.otcs.resource_name,
1234
1052
  )
1235
1053
 
1236
1054
  otcs_resource_id = otcs_resource["resourceID"]
1237
1055
 
1238
- logger.info("OTDS resource ID for Content Server -> %s", otcs_resource_id)
1056
+ self.logger.info("Found Content Server OTDS resource ID -> %s", otcs_resource_id)
1239
1057
 
1240
1058
  # make sure code is idempotent and only try to add ressource if it doesn't exist already:
1241
- awp_resource = self.otds_object.get_resource(self.otawp_settings.resource_name)
1059
+ awp_resource = self.otds_object.get_resource(self.settings.otawp.resource_name)
1242
1060
  if not awp_resource:
1243
- logger.info(
1061
+ self.logger.info(
1244
1062
  "OTDS resource -> '%s' for AppWorks Platform does not yet exist. Creating...",
1245
- self.otawp_settings.resource_name,
1063
+ self.settings.otawp.resource_name,
1246
1064
  )
1247
- # Create a Python dict with the special payload we need for AppWorks:
1248
- additional_payload = {}
1249
- additional_payload["connectorid"] = "rest"
1250
- additional_payload["resourceType"] = "rest"
1251
- user_attribute_mapping = [
1252
- {
1253
- "sourceAttr": ["oTExternalID1"],
1254
- "destAttr": "__NAME__",
1255
- "mappingFormat": "%s",
1256
- },
1257
- {
1258
- "sourceAttr": ["displayname"],
1259
- "destAttr": "DisplayName",
1260
- "mappingFormat": "%s",
1261
- },
1262
- {"sourceAttr": ["mail"], "destAttr": "Email", "mappingFormat": "%s"},
1263
- {
1264
- "sourceAttr": ["oTTelephoneNumber"],
1265
- "destAttr": "Telephone",
1266
- "mappingFormat": "%s",
1267
- },
1268
- {
1269
- "sourceAttr": ["oTMobile"],
1270
- "destAttr": "Mobile",
1271
- "mappingFormat": "%s",
1272
- },
1273
- {
1274
- "sourceAttr": ["oTFacsimileTelephoneNumber"],
1275
- "destAttr": "Fax",
1276
- "mappingFormat": "%s",
1277
- },
1278
- {
1279
- "sourceAttr": ["oTStreetAddress,l,st,postalCode,c"],
1280
- "destAttr": "Address",
1281
- "mappingFormat": "%s%n%s %s %s%n%s",
1282
- },
1283
- {
1284
- "sourceAttr": ["oTCompany"],
1285
- "destAttr": "Company",
1286
- "mappingFormat": "%s",
1287
- },
1288
- {
1289
- "sourceAttr": ["ds-pwp-account-disabled"],
1290
- "destAttr": "AccountDisabled",
1291
- "mappingFormat": "%s",
1292
- },
1293
- {
1294
- "sourceAttr": ["oTExtraAttr9"],
1295
- "destAttr": "IsServiceAccount",
1296
- "mappingFormat": "%s",
1297
- },
1298
- {
1299
- "sourceAttr": ["custom:proxyConfiguration"],
1300
- "destAttr": "ProxyConfiguration",
1301
- "mappingFormat": "%s",
1302
- },
1303
- {
1304
- "sourceAttr": ["c"],
1305
- "destAttr": "Identity-CountryOrRegion",
1306
- "mappingFormat": "%s",
1307
- },
1308
- {
1309
- "sourceAttr": ["gender"],
1310
- "destAttr": "Identity-Gender",
1311
- "mappingFormat": "%s",
1312
- },
1313
- {
1314
- "sourceAttr": ["displayName"],
1315
- "destAttr": "Identity-DisplayName",
1316
- "mappingFormat": "%s",
1317
- },
1318
- {
1319
- "sourceAttr": ["oTStreetAddress"],
1320
- "destAttr": "Identity-Address",
1321
- "mappingFormat": "%s",
1322
- },
1323
- {
1324
- "sourceAttr": ["l"],
1325
- "destAttr": "Identity-City",
1326
- "mappingFormat": "%s",
1327
- },
1328
- {
1329
- "sourceAttr": ["mail"],
1330
- "destAttr": "Identity-Email",
1331
- "mappingFormat": "%s",
1332
- },
1333
- {
1334
- "sourceAttr": ["givenName"],
1335
- "destAttr": "Identity-FirstName",
1336
- "mappingFormat": "%s",
1337
- },
1338
- {
1339
- "sourceAttr": ["sn"],
1340
- "destAttr": "Identity-LastName",
1341
- "mappingFormat": "%s",
1342
- },
1343
- {
1344
- "sourceAttr": ["initials"],
1345
- "destAttr": "Identity-MiddleNames",
1346
- "mappingFormat": "%s",
1347
- },
1348
- {
1349
- "sourceAttr": ["oTMobile"],
1350
- "destAttr": "Identity-Mobile",
1351
- "mappingFormat": "%s",
1352
- },
1353
- {
1354
- "sourceAttr": ["postalCode"],
1355
- "destAttr": "Identity-PostalCode",
1356
- "mappingFormat": "%s",
1357
- },
1358
- {
1359
- "sourceAttr": ["st"],
1360
- "destAttr": "Identity-StateOrProvince",
1361
- "mappingFormat": "%s",
1362
- },
1363
- {
1364
- "sourceAttr": ["title"],
1365
- "destAttr": "Identity-title",
1366
- "mappingFormat": "%s",
1367
- },
1368
- {
1369
- "sourceAttr": ["physicalDeliveryOfficeName"],
1370
- "destAttr": "Identity-physicalDeliveryOfficeName",
1371
- "mappingFormat": "%s",
1372
- },
1373
- {
1374
- "sourceAttr": ["oTFacsimileTelephoneNumber"],
1375
- "destAttr": "Identity-oTFacsimileTelephoneNumber",
1376
- "mappingFormat": "%s",
1377
- },
1378
- {
1379
- "sourceAttr": ["notes"],
1380
- "destAttr": "Identity-notes",
1381
- "mappingFormat": "%s",
1382
- },
1383
- {
1384
- "sourceAttr": ["oTCompany"],
1385
- "destAttr": "Identity-oTCompany",
1386
- "mappingFormat": "%s",
1387
- },
1388
- {
1389
- "sourceAttr": ["oTDepartment"],
1390
- "destAttr": "Identity-oTDepartment",
1391
- "mappingFormat": "%s",
1392
- },
1393
- {
1394
- "sourceAttr": ["birthDate"],
1395
- "destAttr": "Identity-Birthday",
1396
- "mappingFormat": "%s",
1397
- },
1398
- {
1399
- "sourceAttr": ["cn"],
1400
- "destAttr": "Identity-UserName",
1401
- "mappingFormat": "%s",
1402
- },
1403
- {
1404
- "sourceAttr": ["Description"],
1405
- "destAttr": "Identity-UserDescription",
1406
- "mappingFormat": "%s",
1407
- },
1408
- {
1409
- "sourceAttr": ["oTTelephoneNumber"],
1410
- "destAttr": "Identity-Phone",
1411
- "mappingFormat": "%s",
1412
- },
1413
- {
1414
- "sourceAttr": ["displayName"],
1415
- "destAttr": "Identity-IdentityDisplayName",
1416
- "mappingFormat": "%s",
1417
- },
1418
- ]
1419
- additional_payload["userAttributeMapping"] = user_attribute_mapping
1420
- group_attribute_mapping = [
1421
- {
1422
- "sourceAttr": ["cn"],
1423
- "destAttr": "__NAME__",
1424
- "mappingFormat": '%js:function format(name) { return name.replace(/&/g,"-and-"); }',
1425
- },
1426
- {
1427
- "sourceAttr": ["description"],
1428
- "destAttr": "Description",
1429
- "mappingFormat": "%s",
1430
- },
1431
- {
1432
- "sourceAttr": ["description"],
1433
- "destAttr": "Identity-Description",
1434
- "mappingFormat": "%s",
1435
- },
1436
- {
1437
- "sourceAttr": ["displayName"],
1438
- "destAttr": "Identity-DisplayName",
1439
- "mappingFormat": "%s",
1440
- },
1441
- ]
1442
- additional_payload["groupAttributeMapping"] = group_attribute_mapping
1443
- additional_payload["connectorName"] = "REST (Generic)"
1444
- additional_payload["pcCreatePermissionAllowed"] = "true"
1445
- additional_payload["pcModifyPermissionAllowed"] = "true"
1446
- additional_payload["pcDeletePermissionAllowed"] = "false"
1447
- additional_payload["connectionParamInfo"] = [
1448
- {
1449
- "name": "fBaseURL",
1450
- "value": "http://appworks:8080/home/system/app/otdspush",
1451
- },
1452
- {"name": "fUsername", "value": self.otawp_settings.admin},
1453
- {"name": "fPassword", "value": self.otawp_settings.password},
1454
- ]
1455
-
1456
1065
  awp_resource = self.otds_object.add_resource(
1457
- name=self.otawp_settings.resource_name,
1066
+ name=self.settings.otawp.resource_name,
1458
1067
  description="AppWorks Platform",
1459
1068
  display_name="AppWorks Platform",
1460
- additional_payload=additional_payload,
1069
+ additional_payload=OTAWP.resource_payload(
1070
+ org_name=organization,
1071
+ username=self.settings.otawp.username,
1072
+ password=self.settings.otawp.password.get_secret_value(),
1073
+ ),
1461
1074
  )
1462
1075
  else:
1463
- logger.info(
1464
- "OTDS resource -> %s for AppWorks Platform does already exist.",
1465
- self.otawp_settings.resource_name,
1076
+ self.logger.info(
1077
+ "OTDS resource -> '%s' for AppWorks Platform does already exist.",
1078
+ self.settings.otawp.resource_name,
1466
1079
  )
1467
1080
 
1468
1081
  awp_resource_id = awp_resource["resourceID"]
1469
1082
 
1470
- logger.info("OTDS resource ID for AppWorks Platform -> %s", awp_resource_id)
1083
+ self.logger.info(
1084
+ "OTDS resource ID for AppWorks Platform -> %s",
1085
+ awp_resource_id,
1086
+ )
1471
1087
 
1472
1088
  self.settings.placeholder_values["OTAWP_RESOURCE_ID"] = str(awp_resource_id)
1473
1089
 
1474
- logger.debug(
1475
- "Placeholder values after OTAWP init = %s", self.settings.placeholder_values
1090
+ self.logger.debug(
1091
+ "Placeholder values after OTAWP init = %s",
1092
+ self.settings.placeholder_values,
1476
1093
  )
1477
1094
 
1478
- logger.info("Update AppWorks Kubernetes Config Map with OTDS resource IDs...")
1479
-
1480
- config_map = self.k8s_object.get_config_map(self.otawp_settings.k8s_configmap)
1481
- if not config_map:
1482
- logger.error(
1483
- "Failed to retrieve AppWorks Kubernetes Config Map -> %s",
1484
- self.otawp_settings.k8s_configmap,
1485
- )
1486
- else:
1487
- solution = yaml.safe_load(config_map.data["solution.yaml"]) # type: ignore
1488
-
1489
- # Change values as required
1490
- solution["platform"]["organizations"]["system"]["otds"][
1491
- "resourceId"
1492
- ] = awp_resource_id
1493
- solution["platform"]["content"]["ContentServer"][
1494
- "contentServerUrl"
1495
- ] = f"{self.otcs_settings.public_protocol}://{self.otcs_settings.public_url}/cs/cs"
1496
- solution["platform"]["content"]["ContentServer"][
1497
- "contentServerSupportDirectoryUrl"
1498
- ] = f"{self.otcs_settings.public_protocol}://{self.otcs_settings.public_url}/cssupport"
1499
- solution["platform"]["content"]["ContentServer"][
1500
- "otdsResourceId"
1501
- ] = otcs_resource_id
1502
- solution["platform"]["authenticators"]["OTDS_auth"]["publicLoginUrl"] = (
1503
- self.otds_settings.public_protocol
1504
- + "://"
1505
- + self.otds_settings.public_url
1506
- + "/otdsws/login"
1507
- )
1508
- solution["platform"]["security"]["contentSecurityPolicy"] = (
1509
- "frame-ancestors 'self' "
1510
- + self.otcs_settings.public_protocol
1511
- + "://"
1512
- + self.otcs_settings.public_url
1095
+ # Check if Kubernetes is available. Actually that should always be the case...
1096
+ if self.k8s_object:
1097
+ self.logger.info(
1098
+ "Update AppWorks Kubernetes Config Map with OTDS resource IDs...",
1513
1099
  )
1514
- data = {"solution.yaml": yaml.dump(solution)}
1515
- result = self.k8s_object.replace_config_map(
1516
- self.otawp_settings.k8s_configmap, data
1517
- )
1518
- if result:
1519
- logger.info("Successfully updated AppWorks Solution YAML.")
1100
+
1101
+ config_map = self.k8s_object.get_config_map(config_map_name=self.settings.k8s.cm_otawp)
1102
+ if not config_map:
1103
+ self.logger.error(
1104
+ "Failed to retrieve AppWorks Kubernetes config map -> '%s'",
1105
+ self.settings.k8s.cm_otawp,
1106
+ )
1520
1107
  else:
1521
- logger.error("Failed to update AppWorks Solution YAML.")
1522
- logger.debug("Solution YAML for AppWorks -> %s", solution)
1108
+ self.logger.info(
1109
+ "Update Kubernetes config map for AppWorks organization -> '%s' with OTDS resource IDs...",
1110
+ organization,
1111
+ )
1112
+ solution = yaml.safe_load(config_map.data["solution.yaml"])
1523
1113
 
1524
- logger.info("Scale AppWorks Kubernetes Stateful Set to 1...")
1525
- self.k8s_object.scale_stateful_set(
1526
- sts_name=self.otawp_settings.k8s_statefulset, scale=1
1527
- )
1114
+ # Change values as required
1115
+ solution["platform"]["organizations"][organization]["otds"]["resourceId"] = awp_resource_id
1116
+ solution["platform"]["content"]["ContentServer"]["contentServerUrl"] = (
1117
+ f"{self.settings.otcs.url!s}{self.settings.otcs.base_path}"
1118
+ )
1119
+ solution["platform"]["content"]["ContentServer"]["contentServerSupportDirectoryUrl"] = (
1120
+ f"{self.settings.otcs.url!s}/cssupport"
1121
+ )
1122
+ solution["platform"]["content"]["ContentServer"]["otdsResourceId"] = otcs_resource_id
1123
+ solution["platform"]["authenticators"]["OTDS_auth"]["publicLoginUrl"] = (
1124
+ str(self.settings.otds.url) + "/otdsws/login"
1125
+ )
1126
+ solution["platform"]["security"]["contentSecurityPolicy"] = "frame-ancestors 'self' " + str(
1127
+ self.settings.otcs.url,
1128
+ )
1129
+ config_map.data["solution.yaml"] = yaml.dump(solution)
1130
+ result = self.k8s_object.replace_config_map(
1131
+ config_map_name=self.settings.k8s.cm_otawp,
1132
+ config_map_data=config_map.data,
1133
+ )
1134
+ if result:
1135
+ self.logger.info(
1136
+ "Successfully updated AppWorks solution YAML for organization -> '%s'.",
1137
+ organization,
1138
+ )
1139
+ else:
1140
+ self.logger.error(
1141
+ "Failed to update AppWorks solution YAML for organization -> '%s'!",
1142
+ organization,
1143
+ )
1144
+ self.logger.debug("Solution YAML for AppWorks organization -> '%s': %s", organization, solution)
1145
+
1146
+ self.logger.info("Scale AppWorks Kubernetes stateful set -> '%s' to 1...", self.settings.k8s.sts_otawp)
1147
+ self.k8s_object.scale_stateful_set(
1148
+ sts_name=self.settings.k8s.sts_otawp,
1149
+ scale=1,
1150
+ )
1151
+ else:
1152
+ self.logger.warning("Kubernetes not initialized. Cannot configure AppWorks Kubernetes Config Map!")
1528
1153
 
1529
1154
  # Add the OTCS Admin user to the AppWorks Access Role in OTDS
1530
1155
  self.otds_object.add_user_to_access_role(
1531
- "Access to " + self.otawp_settings.resource_name, "otadmin@otds.admin"
1156
+ access_role="Access to " + self.settings.otawp.resource_name,
1157
+ user_id="otadmin@otds.admin",
1532
1158
  )
1533
1159
 
1534
1160
  # Loop to wait for OTCS to create its OTDS user partition:
1535
1161
  otcs_partition = self.otds_object.get_partition(
1536
- self.otcs_settings.partition, show_error=False
1162
+ name=self.settings.otcs.partition,
1163
+ show_error=False,
1537
1164
  )
1538
1165
  while otcs_partition is None:
1539
- logger.warning(
1166
+ self.logger.warning(
1540
1167
  "OTDS user partition for Content Server with name -> '%s' does not exist yet. Waiting...",
1541
- self.otcs_settings.partition,
1168
+ self.settings.otcs.partition,
1542
1169
  )
1543
1170
 
1544
1171
  time.sleep(30)
1545
1172
  otcs_partition = self.otds_object.get_partition(
1546
- self.otcs_settings.partition, show_error=False
1173
+ name=self.settings.otcs.partition,
1174
+ show_error=False,
1547
1175
  )
1548
1176
 
1549
1177
  # Add the OTDS user partition for OTCS to the AppWorks Platform Access Role in OTDS.
1550
1178
  # This will effectvely sync all OTCS users with AppWorks Platform:
1551
1179
  self.otds_object.add_partition_to_access_role(
1552
- self.otawp_settings.access_role_name, self.otcs_settings.partition
1180
+ access_role=self.settings.otawp.access_role_name,
1181
+ partition=self.settings.otcs.partition,
1553
1182
  )
1554
1183
 
1555
1184
  # Add the OTDS admin partition to the AppWorks Platform Access Role in OTDS.
1556
1185
  self.otds_object.add_partition_to_access_role(
1557
- self.otawp_settings.access_role_name, self.otds_settings.admin_partition
1186
+ access_role=self.settings.otawp.access_role_name,
1187
+ partition=self.settings.otds.admin_partition,
1558
1188
  )
1559
1189
 
1560
1190
  # Set Group inclusion for Access Role for OTAWP to "True":
1561
1191
  self.otds_object.update_access_role_attributes(
1562
- self.otawp_settings.access_role_name,
1563
- [{"name": "pushAllGroups", "values": ["True"]}],
1192
+ name=self.settings.otawp.access_role_name,
1193
+ attribute_list=[{"name": "pushAllGroups", "values": ["True"]}],
1564
1194
  )
1565
1195
 
1566
1196
  # Add ResourceID User to OTDSAdmin to allow push
1567
1197
  self.otds_object.add_user_to_group(
1568
- user=str(awp_resource_id) + "@otds.admin", group="otdsadmins@otds.admin"
1198
+ user=str(awp_resource_id) + "@otds.admin",
1199
+ group="otdsadmins@otds.admin",
1569
1200
  )
1570
1201
 
1571
1202
  # Allow impersonation for all users:
1572
- self.otds_object.impersonate_resource(self.otawp_settings.resource_name)
1573
-
1574
- # Add SPS license for OTAWP
1575
- # check if the license file exists, otherwise skip for versions pre 24.1
1576
- if os.path.isfile(self.otawp_settings.license_file):
1577
- logger.info(
1578
- "Found OTAWP license file -> '%s', assiging it to ressource '%s'...",
1579
- self.otawp_settings.license_file,
1580
- self.otawp_settings.resource_name,
1581
- )
1203
+ self.otds_object.impersonate_resource(resource_name=self.settings.otawp.resource_name)
1582
1204
 
1583
- otawp_license = self.otds_object.add_license_to_resource(
1584
- self.otawp_settings.license_file,
1585
- self.otawp_settings.product_name,
1586
- self.otawp_settings.product_description,
1587
- awp_resource["resourceID"],
1588
- )
1589
- if not otawp_license:
1590
- logger.error(
1591
- "Couldn't apply license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1592
- self.otawp_settings.license_file,
1593
- self.otawp_settings.product_name,
1594
- awp_resource["resourceID"],
1595
- )
1596
- else:
1597
- logger.info(
1598
- "Successfully applied license -> '%s' for product -> '%s' to OTDS resource -> '%s'",
1599
- self.otawp_settings.license_file,
1600
- self.otawp_settings.product_name,
1601
- awp_resource["resourceID"],
1602
- )
1603
-
1604
- # Assign AppWorks license to Content Server Members Partiton and otds.admin:
1605
- for partition_name in ["otds.admin", self.otcs_settings.partition]:
1606
- if self.otds_object.is_partition_licensed(
1607
- partition_name=partition_name,
1608
- resource_id=awp_resource["resourceID"],
1609
- license_feature="USERS",
1610
- license_name=self.otawp_settings.product_name,
1611
- ):
1612
- logger.info(
1613
- "Partition -> %s is already licensed for -> %s (%s)",
1614
- partition_name,
1615
- self.otawp_settings.product_name,
1616
- "USERS",
1617
- )
1618
- else:
1619
- assigned_license = self.otds_object.assign_partition_to_license(
1620
- partition_name,
1621
- awp_resource["resourceID"],
1622
- "USERS",
1623
- self.otawp_settings.product_name,
1624
- )
1625
- if not assigned_license:
1626
- logger.error(
1627
- "Partition -> '%s' could not be assigned to license -> '%s' (%s)",
1628
- partition_name,
1629
- self.otawp_settings.product_name,
1630
- "USERS",
1631
- )
1632
- else:
1633
- logger.info(
1634
- "Partition -> '%s' successfully assigned to license -> '%s' (%s)",
1635
- partition_name,
1636
- self.otawp_settings.product_name,
1637
- "USERS",
1638
- )
1639
1205
  otawp_object = OTAWP(
1640
- self.otawp_settings.protocol,
1641
- self.otawp_settings.k8s_statefulset,
1642
- str(self.otawp_settings.port),
1643
- "sysadmin",
1644
- self.otawp_settings.password,
1645
- "",
1206
+ protocol=self.settings.otawp.protocol,
1207
+ hostname=self.settings.k8s.sts_otawp,
1208
+ port=str(self.settings.otawp.port),
1209
+ username="sysadmin",
1210
+ password=self.settings.otawp.password.get_secret_value(),
1211
+ organization=self.settings.otawp.organization,
1212
+ otawp_ticket="",
1213
+ config_map_name=self.settings.k8s.cm_otawp,
1214
+ license_file=self.settings.otawp.license_file,
1215
+ product_name=self.settings.otawp.product_name,
1216
+ product_description=self.settings.otawp.product_description,
1217
+ logger=self.logger,
1646
1218
  )
1219
+
1647
1220
  return otawp_object
1648
1221
 
1649
1222
  # end method definition
1650
1223
 
1651
- def restart_otcs_service(self, otcs_object: OTCS, extra_wait_time: int = 60):
1652
- """Restart the Content Server service in all OTCS pods
1224
+ def restart_otcs_service(
1225
+ self,
1226
+ backend: OTCS,
1227
+ frontend: OTCS,
1228
+ extra_wait_time: int = 60,
1229
+ ) -> None:
1230
+ """Restart the Content Server service in all OTCS pods.
1653
1231
 
1654
1232
  Args:
1655
- otcs_object: OTCS class instance (object)
1233
+ backend:
1234
+ OTCS object of the backend.
1235
+ frontend:
1236
+ OTCS object of the frontend.
1237
+ extra_wait_time (int, optional):
1238
+ Extra wait time after the restart to make sure pods are responsive again.
1239
+ Default is 60.
1240
+
1656
1241
  Returns:
1657
1242
  None
1243
+
1658
1244
  """
1659
1245
 
1660
1246
  if not self.k8s_object:
1661
- logger.warning(
1662
- "Kubernetes integration not available, skipping restart of services"
1247
+ self.logger.warning(
1248
+ "Kubernetes integration not available, skipping restart of services",
1663
1249
  )
1664
1250
  return
1665
1251
 
1666
- logger.info("Restart OTCS frontend and backend pods...")
1252
+ self.logger.info("Restart OTCS frontend and backend pods...")
1253
+
1254
+ # Get number of replicas or update it for frontends as it might change with dynamic scaling:
1255
+ otcs_frontend_scale = self.k8s_object.get_stateful_set_scale(
1256
+ sts_name=self.settings.k8s.sts_otcs_frontend,
1257
+ )
1258
+ if not otcs_frontend_scale:
1259
+ self.logger.error(
1260
+ "Cannot find Kubernetes Stateful Set -> '%s' for OTCS Frontends!",
1261
+ self.settings.k8s.sts_otcs_frontend,
1262
+ )
1263
+
1264
+ self.settings.k8s.sts_otcs_frontend_replicas = otcs_frontend_scale.spec.replicas
1667
1265
 
1668
1266
  # Restart all frontends:
1669
- for x in range(0, self.otcs_settings.replicas_frontend):
1670
- pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1267
+ for x in range(self.settings.k8s.sts_otcs_frontend_replicas):
1268
+ pod_name = self.settings.k8s.sts_otcs_frontend + "-" + str(x)
1671
1269
 
1672
- logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1270
+ self.logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1673
1271
  self.k8s_object.exec_pod_command(
1674
1272
  pod_name,
1675
1273
  ["/bin/sh", "-c", "touch /tmp/keepalive"],
1676
1274
  container="otcs-frontend-container",
1677
1275
  )
1678
- logger.info("Restarting pod -> '%s'", pod_name)
1276
+ self.logger.info("Restarting OTCS in pod -> '%s'", pod_name)
1679
1277
  self.k8s_object.exec_pod_command(
1680
1278
  pod_name,
1681
1279
  ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"],
@@ -1688,16 +1286,16 @@ class Customizer:
1688
1286
  )
1689
1287
 
1690
1288
  # Restart all backends:
1691
- for x in range(0, self.otcs_settings.replicas_backend):
1692
- pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1289
+ for x in range(self.settings.k8s.sts_otcs_admin_replicas):
1290
+ pod_name = self.settings.k8s.sts_otcs_admin + "-" + str(x)
1693
1291
 
1694
- logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1292
+ self.logger.info("Deactivate Liveness probe for pod -> '%s'", pod_name)
1695
1293
  self.k8s_object.exec_pod_command(
1696
1294
  pod_name,
1697
1295
  ["/bin/sh", "-c", "touch /tmp/keepalive"],
1698
1296
  container="otcs-admin-container",
1699
1297
  )
1700
- logger.info("Restarting pod -> '%s'", pod_name)
1298
+ self.logger.info("Restarting OTCS in pod -> '%s'", pod_name)
1701
1299
  self.k8s_object.exec_pod_command(
1702
1300
  pod_name,
1703
1301
  ["/bin/sh", "-c", "/opt/opentext/cs/stop_csserver"],
@@ -1709,218 +1307,241 @@ class Customizer:
1709
1307
  container="otcs-admin-container",
1710
1308
  )
1711
1309
 
1712
- logger.info("Re-Authenticating to OTCS after restart of pods...")
1713
- otcs_cookie = otcs_object.authenticate(revalidate=True)
1310
+ # Reauthenticate at frontend:
1311
+ self.logger.info(
1312
+ "Re-Authenticating to OTCS frontend after restart of frontend pods...",
1313
+ )
1314
+ otcs_cookie = frontend.authenticate(revalidate=True)
1315
+ while otcs_cookie is None:
1316
+ self.logger.info("Waiting 30 seconds for OTCS frontend to become ready...")
1317
+ time.sleep(30)
1318
+ otcs_cookie = frontend.authenticate(revalidate=True)
1319
+ self.logger.info("OTCS frontend is ready again.")
1320
+
1321
+ # Reauthenticate at backend:
1322
+ self.logger.info(
1323
+ "Re-Authenticating to OTCS backend after restart of backend pods...",
1324
+ )
1325
+ otcs_cookie = backend.authenticate(revalidate=True)
1714
1326
  while otcs_cookie is None:
1715
- logger.warning("Waiting 30 seconds for OTCS to become ready...")
1327
+ self.logger.info("Waiting 30 seconds for OTCS backend to become ready...")
1716
1328
  time.sleep(30)
1717
- otcs_cookie = otcs_object.authenticate(revalidate=True)
1718
- logger.info("OTCS is ready again.")
1329
+ otcs_cookie = backend.authenticate(revalidate=True)
1330
+ self.logger.info("OTCS backend is ready again.")
1719
1331
 
1720
1332
  # Reactivate Liveness probes in all pods:
1721
- for x in range(0, self.otcs_settings.replicas_frontend):
1722
- pod_name = self.otcs_settings.k8s_statefulset_frontend + "-" + str(x)
1333
+ for x in range(self.settings.k8s.sts_otcs_frontend_replicas):
1334
+ pod_name = self.settings.k8s.sts_otcs_frontend + "-" + str(x)
1723
1335
 
1724
- logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1336
+ self.logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1725
1337
  self.k8s_object.exec_pod_command(
1726
1338
  pod_name,
1727
1339
  ["/bin/sh", "-c", "rm /tmp/keepalive"],
1728
1340
  container="otcs-frontend-container",
1729
1341
  )
1730
1342
 
1731
- for x in range(0, self.otcs_settings.replicas_backend):
1732
- pod_name = self.otcs_settings.k8s_statefulset_backend + "-" + str(x)
1343
+ for x in range(self.settings.k8s.sts_otcs_admin_replicas):
1344
+ pod_name = self.settings.k8s.sts_otcs_admin + "-" + str(x)
1733
1345
 
1734
- logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1346
+ self.logger.info("Reactivate Liveness probe for pod -> '%s'", pod_name)
1735
1347
  self.k8s_object.exec_pod_command(
1736
1348
  pod_name,
1737
1349
  ["/bin/sh", "-c", "rm /tmp/keepalive"],
1738
1350
  container="otcs-admin-container",
1739
1351
  )
1740
1352
 
1741
- logger.info("Restart OTCS frontend and backend pods has been completed.")
1353
+ self.logger.info("Restart OTCS frontend and backend pods has been completed.")
1742
1354
 
1743
1355
  # optional, give some additional time to make sure service is responsive
1744
1356
  if extra_wait_time > 0:
1745
- logger.info(
1357
+ self.logger.info(
1746
1358
  "Wait %s seconds to make sure OTCS is responsive again...",
1747
1359
  str(extra_wait_time),
1748
1360
  )
1749
1361
  time.sleep(extra_wait_time)
1750
- logger.info("Continue customizing...")
1362
+ self.logger.info("Continue customizing...")
1751
1363
 
1752
1364
  # end method definition
1753
1365
 
1754
1366
  def restart_otac_service(self) -> bool:
1755
- """Restart the Archive Center spawner service in OTAC pod
1367
+ """Restart the Archive Center spawner service in OTAC pod.
1756
1368
 
1757
- Args:
1758
- None
1759
1369
  Returns:
1760
- bool: True if restart was done, False if error occured
1370
+ bool: True if restart was done, False if error occured.
1371
+
1761
1372
  """
1762
1373
 
1763
- if not self.otac_settings.enabled:
1374
+ if not self.settings.otac.enabled:
1764
1375
  return False
1765
1376
 
1766
- logger.info(
1377
+ self.logger.info(
1767
1378
  "Restarting spawner service in Archive Center pod -> '%s'",
1768
- self.otac_settings.k8s_pod_name,
1379
+ self.settings.k8s.pod_otac,
1769
1380
  )
1770
1381
  # The Archive Center Spawner needs to be run in "interactive" mode - otherwise the command will "hang":
1771
1382
  # The "-c" parameter is not required in this case
1772
1383
  # False is given as parameter as OTAC writes non-errors to stderr
1773
1384
  response = self.k8s_object.exec_pod_command_interactive(
1774
- self.otac_settings.k8s_pod_name,
1775
- ["/bin/sh", "/etc/init.d/spawner restart"],
1776
- 60,
1777
- False,
1385
+ pod_name=self.settings.k8s.pod_otac,
1386
+ commands=["/bin/sh", "/etc/init.d/spawner restart"],
1387
+ timeout=60,
1388
+ write_stderr_to_error_log=False,
1778
1389
  )
1779
1390
 
1780
- if response:
1781
- return True
1782
- else:
1783
- return False
1391
+ return bool(response)
1784
1392
 
1785
1393
  # end method definition
1786
1394
 
1787
- def restart_otawp_pod(self):
1788
- """Delete the AppWorks Platform Pod to make Kubernetes restart it.
1789
-
1790
- Args:
1791
- Returns:
1792
- None
1793
- """
1395
+ def restart_otawp_pod(self) -> None:
1396
+ """Delete the AppWorks Platform Pod to make Kubernetes restart it."""
1794
1397
 
1795
- self.k8s_object.delete_pod(self.otawp_settings.k8s_statefulset + "-0")
1398
+ self.k8s_object.delete_pod(self.settings.k8s.sts_otawp + "-0")
1796
1399
 
1797
1400
  # end method definition
1798
1401
 
1799
- def consolidate_otds(self):
1800
- """Consolidate OTDS resources
1801
- Args:
1802
- Return: None
1803
- """
1402
+ def consolidate_otds(self) -> None:
1403
+ """Consolidate OTDS resources."""
1804
1404
 
1805
- self.otds_object.consolidate(self.otcs_settings.resource_name)
1405
+ self.otds_object.consolidate(self.settings.otcs.resource_name)
1806
1406
 
1807
- if self.otawp_settings.enabled: # is AppWorks Platform deployed?
1808
- self.otds_object.consolidate(self.otawp_settings.resource_name)
1407
+ if self.settings.otawp.enabled: # is AppWorks Platform deployed?
1408
+ self.otds_object.consolidate(self.settings.otawp.resource_name)
1809
1409
 
1810
1410
  # end method definition
1811
1411
 
1812
- def import_powerdocs_configuration(self, otpd_object: OTPD):
1813
- """Import a database export (zip file) into the PowerDocs database
1412
+ def import_powerdocs_configuration(self, otpd_object: OTPD) -> None:
1413
+ """Import a database export (zip file) into the PowerDocs database.
1814
1414
 
1815
1415
  Args:
1816
- otpd_object (object): PowerDocs object
1416
+ otpd_object (OTPD):
1417
+ The PowerDocs object.
1418
+
1817
1419
  """
1818
1420
 
1819
- if self.otpd_settings.db_importfile.startswith("http"):
1421
+ if self.settings.otpd.db_importfile.startswith("http"):
1820
1422
  # Download file from remote location specified by the OTPD_DBIMPORTFILE
1821
1423
  # this must be a public place without authentication:
1822
- logger.info(
1424
+ self.logger.info(
1823
1425
  "Download PowerDocs database file from URL -> '%s'",
1824
- self.otpd_settings.db_importfile,
1426
+ self.settings.otpd.db_importfile,
1825
1427
  )
1826
1428
 
1827
1429
  try:
1828
- package = requests.get(self.otpd_settings.db_importfile, timeout=60)
1430
+ package = requests.get(self.settings.otpd.db_importfile, timeout=60)
1829
1431
  package.raise_for_status()
1830
- logger.info(
1432
+ self.logger.info(
1831
1433
  "Successfully downloaded PowerDocs database file -> '%s'; status code -> %s",
1832
- self.otpd_settings.db_importfile,
1434
+ self.settings.otpd.db_importfile,
1833
1435
  package.status_code,
1834
1436
  )
1835
- filename = "/tmp/otpd_db_import.zip"
1437
+ filename = os.path.join(tempfile.gettempdir(), "otpd_db_import.zip")
1836
1438
  with open(filename, mode="wb") as localfile:
1837
1439
  localfile.write(package.content)
1838
1440
 
1839
- logger.info(
1441
+ self.logger.info(
1840
1442
  "Starting import on %s://%s:%s of %s",
1841
- self.otpd_settings.protocol,
1842
- self.otpd_settings.hostname,
1843
- self.otpd_settings.port,
1844
- self.otpd_settings.db_importfile,
1443
+ self.settings.otpd.url.scheme,
1444
+ self.settings.otpd.url.host,
1445
+ self.settings.otpd.url.port,
1446
+ self.settings.otpd.db_importfile,
1845
1447
  )
1846
- response = otpd_object.import_database(filename=filename)
1847
- logger.info("Response -> %s", response)
1448
+ response = otpd_object.import_database(file_path=filename)
1449
+ self.logger.info("Response -> %s", response)
1848
1450
 
1849
- except requests.exceptions.HTTPError as err:
1850
- logger.error("Request error -> %s", err)
1451
+ except requests.exceptions.HTTPError:
1452
+ self.logger.error("HTTP request error!")
1851
1453
 
1852
1454
  # end method definition
1853
1455
 
1854
- def set_maintenance_mode(self, enable: bool = True):
1855
- """Enable or Disable Maintenance Mode
1456
+ def set_maintenance_mode(self, enable: bool = True) -> None:
1457
+ """Enable or Disable Maintenance Mode.
1458
+
1459
+ This redirects the Kubernetes Ingress to a maintenace web page.
1856
1460
 
1857
1461
  Args:
1858
- enable (bool, optional): _description_. Defaults to True.
1462
+ enable (bool, optional):
1463
+ Whether or not to activate the maintenance mode web page.
1464
+ Defaults to True.
1465
+
1859
1466
  """
1860
- if enable and self.k8s_settings.enabled:
1467
+
1468
+ if enable and self.settings.k8s.enabled:
1861
1469
  self.log_header("Enable Maintenance Mode")
1862
- logger.info(
1863
- "Put OTCS frontends in Maitenance Mode by changing the Kubernetes Ingress backend service..."
1470
+ self.logger.info(
1471
+ "Put OTCS frontends in Maitenance Mode by changing the Kubernetes Ingress backend service...",
1864
1472
  )
1865
1473
  self.k8s_object.update_ingress_backend_services(
1866
- self.otcs_settings.k8s_ingress,
1474
+ self.settings.k8s.ingress_otxecm,
1867
1475
  "otcs",
1868
- self.otcs_settings.maintenance_service_name,
1869
- self.otcs_settings.mainteance_service_port,
1476
+ self.settings.k8s.maintenance_service_name,
1477
+ self.settings.k8s.maintenance_service_port,
1870
1478
  )
1871
- logger.info("OTCS frontend is now in Maintenance Mode!")
1872
- elif not self.k8s_settings.enabled:
1873
- logger.warning(
1874
- "Kubernetes Integration disabled - Cannot Enable/Disable Maintenance Mode"
1479
+ self.logger.info("OTCS frontend is now in Maintenance Mode!")
1480
+ elif not self.settings.k8s.enabled:
1481
+ self.logger.warning(
1482
+ "Kubernetes Integration disabled - Cannot Enable/Disable Maintenance Mode",
1875
1483
  )
1876
1484
  self.k8s_object = None
1877
1485
  else:
1878
1486
  # Changing the Ingress backend service to OTCS frontend service:
1879
- logger.info(
1880
- "Put OTCS frontend back in Production Mode by changing the Kubernetes Ingress backend service..."
1487
+ self.logger.info(
1488
+ "Put OTCS frontend back in Production Mode by changing the Kubernetes Ingress backend service...",
1881
1489
  )
1882
1490
  self.k8s_object.update_ingress_backend_services(
1883
- self.otcs_settings.k8s_ingress,
1491
+ self.settings.k8s.ingress_otxecm,
1884
1492
  "otcs",
1885
- self.otcs_settings.hostname_frontend,
1886
- self.otcs_settings.port_frontend,
1493
+ self.settings.otcs.url_frontend.host,
1494
+ self.settings.otcs.url_frontend.port,
1887
1495
  )
1888
- logger.info("OTCS frontend is now back in Production Mode!")
1496
+ self.logger.info("OTCS frontend is now back in Production Mode!")
1889
1497
 
1890
1498
  # end method definition
1891
1499
 
1892
- def customization_run(self):
1893
- """Central function to initiate the customization"""
1894
- # Set Timer for duration calculation
1895
- self.settings.customizer_start_time = self.settings.customizer_end_time = (
1896
- datetime.now()
1897
- )
1500
+ def init_customizer(self) -> bool:
1501
+ """Initialize all objects used by the customizer.
1502
+
1503
+ This includes:
1504
+ * OTDS
1505
+ * Kubernetes (K8S)
1506
+ * AppWorks Platform
1507
+ * OTCS (frontend + backend)
1508
+ * OTAC (Archive Center)
1509
+ * OTIV (Intelligent Viewing)
1510
+ * OTPD (PowerDocs)
1511
+ * Core Share
1512
+ * Microsoft 365
1513
+ * Aviator Search
1898
1514
 
1899
- # Initialize the OTDS, OTCS and OTPD objects and wait for the
1900
- # pods to be ready. If any of this fails we bail out:
1515
+ Returns:
1516
+ bool:
1517
+ True = success. False = error.
1518
+
1519
+ """
1901
1520
 
1902
1521
  self.log_header("Initialize OTDS")
1903
1522
 
1904
1523
  self.otds_object = self.init_otds()
1905
1524
  if not self.otds_object:
1906
- logger.error("Failed to initialize OTDS - exiting...")
1907
- sys.exit()
1525
+ self.logger.error("Failed to initialize OTDS - exiting...")
1526
+ return False
1908
1527
 
1909
1528
  # Establish in-cluster Kubernetes connection
1910
- self.log_header("Initialize Kubernetes")
1911
- if self.k8s_settings.enabled:
1912
- self.k8s_object = self.init_k8s()
1913
-
1914
- if not self.k8s_object:
1915
- logger.error("Failed to initialize Kubernetes - exiting...")
1916
- sys.exit()
1917
-
1918
- # Put Frontend in Maintenance mode to make sure nobody interferes
1919
- # during customization:
1920
- if self.otcs_settings.maintenance_mode:
1921
- self.set_maintenance_mode(True)
1529
+ if self.settings.k8s.enabled:
1530
+ self.log_header("Initialize Kubernetes")
1531
+ try:
1532
+ self.k8s_object = self.init_k8s()
1533
+
1534
+ if not self.k8s_object:
1535
+ self.logger.error("Failed to initialize Kubernetes - exiting...")
1536
+ return False
1537
+ except Exception as err:
1538
+ self.logger.error(
1539
+ "Failed to initialize Kubernetes, disabling Kubernetes integration...",
1540
+ )
1541
+ self.logger.debug(err)
1542
+ self.settings.k8s.enabled = False
1922
1543
 
1923
- if self.otawp_settings.enabled: # is AppWorks Platform deployed?
1544
+ if self.settings.otawp.enabled: # is AppWorks Platform deployed?
1924
1545
  self.log_header("Initialize OTAWP")
1925
1546
 
1926
1547
  # Configure required OTDS resources as AppWorks doesn't do this on its own:
@@ -1930,119 +1551,150 @@ class Customizer:
1930
1551
 
1931
1552
  self.log_header("Initialize OTCS backend")
1932
1553
  self.otcs_backend_object = self.init_otcs(
1933
- self.otcs_settings.hostname_backend,
1934
- int(self.otcs_settings.port_backend),
1935
- self.otcs_settings.partition,
1936
- self.otcs_settings.resource_name,
1554
+ url=self.settings.otcs.url_backend,
1937
1555
  )
1938
1556
  if not self.otcs_backend_object:
1939
- logger.error("Failed to initialize OTCS backend - exiting...")
1557
+ self.logger.error("Failed to initialize OTCS backend - exiting...")
1940
1558
  sys.exit()
1941
1559
 
1942
1560
  self.log_header("Initialize OTCS frontend")
1943
1561
  self.otcs_frontend_object = self.init_otcs(
1944
- self.otcs_settings.hostname_frontend,
1945
- int(self.otcs_settings.port_frontend),
1946
- self.otcs_settings.partition,
1947
- self.otcs_settings.resource_name,
1562
+ url=self.settings.otcs.url_frontend,
1948
1563
  )
1949
1564
  if not self.otcs_frontend_object:
1950
- logger.error("Failed to initialize OTCS frontend - exiting...")
1951
- sys.exit()
1565
+ self.logger.error("Failed to initialize OTCS frontend - exiting...")
1566
+ return False
1952
1567
 
1953
- if self.otac_settings.enabled: # is Archive Center deployed?
1568
+ if self.settings.otac.enabled: # is Archive Center deployed?
1954
1569
  self.log_header("Initialize OTAC")
1955
1570
 
1956
1571
  self.otac_object = self.init_otac()
1957
1572
  if not self.otac_object:
1958
- logger.error("Failed to initialize OTAC - exiting...")
1959
- sys.exit()
1573
+ self.logger.error("Failed to initialize OTAC - exiting...")
1574
+ return False
1960
1575
  else:
1576
+ self.logger.info("Archive Center is disabled.")
1961
1577
  self.otac_object = None
1962
1578
 
1963
- if self.otiv_settings.enabled: # is Intelligent Viewing deployed?
1579
+ if self.settings.otiv.enabled: # is Intelligent Viewing deployed?
1964
1580
  self.log_header("Initialize OTIV")
1965
1581
 
1966
1582
  self.otiv_object = self.init_otiv()
1967
1583
  else:
1584
+ self.logger.info("Intelligent Viewing is disabled.")
1968
1585
  self.otiv_object = None
1969
1586
 
1970
- if self.otpd_settings.enabled: # is PowerDocs deployed?
1971
- self.log_header("Initialize OTPD")
1587
+ if self.settings.otpd.enabled: # is PowerDocs deployed?
1588
+ self.log_header("Initialize PowerDocs")
1972
1589
 
1973
1590
  self.otpd_object = self.init_otpd()
1974
1591
  if not self.otpd_object:
1975
- logger.error("Failed to initialize OTPD - exiting...")
1976
- sys.exit()
1592
+ self.logger.error("Failed to initialize OTPD - exiting...")
1593
+ return False
1977
1594
  else:
1595
+ self.logger.info("PowerDocs is disabled.")
1978
1596
  self.otpd_object = None
1979
1597
 
1980
- if self.core_share_settings.enabled: # is Core Share enabled?
1598
+ if self.settings.coreshare.enabled: # is Core Share enabled?
1981
1599
  self.log_header("Initialize Core Share")
1982
1600
 
1983
1601
  self.core_share_object = self.init_coreshare()
1984
1602
  if not self.core_share_object:
1985
- logger.error("Failed to initialize Core Share - exiting...")
1986
- sys.exit()
1603
+ self.logger.error("Failed to initialize Core Share - exiting...")
1604
+ return False
1987
1605
  else:
1606
+ self.logger.info("Core Share is disabled.")
1988
1607
  self.core_share_object = None
1989
1608
 
1990
1609
  if (
1991
- self.m365_settings.enabled
1992
- and self.m365_settings.user != ""
1993
- and self.m365_settings.password != ""
1610
+ self.settings.m365.enabled and self.settings.m365.client_id != "" and self.settings.m365.client_secret != ""
1994
1611
  ): # is M365 enabled?
1995
1612
  self.log_header("Initialize Microsoft 365")
1996
1613
 
1997
1614
  # Initialize the M365 object and connection to M365 Graph API:
1998
1615
  self.m365_object = self.init_m365()
1999
1616
  if not self.m365_object:
2000
- logger.error("Failed to initialize Microsoft 365!")
2001
- sys.exit()
1617
+ self.logger.error("Failed to initialize Microsoft 365!")
1618
+ return False
1619
+ else:
1620
+ self.logger.info("Microsoft 365 is disabled or credentials are missing.")
1621
+ self.m365_object = None
2002
1622
 
2003
- if self.avts_settings.enabled:
1623
+ if self.settings.avts.enabled:
2004
1624
  self.log_header("Initialize Aviator Search")
2005
1625
  self.avts_object = self.init_avts()
2006
1626
  if not self.avts_object:
2007
- logger.error("Failed to initialize Aviator Search")
2008
- sys.exit()
1627
+ self.logger.error("Failed to initialize Aviator Search")
1628
+ return False
2009
1629
  else:
1630
+ self.logger.info("Aviator Search is disabled.")
2010
1631
  self.avts_object = None
2011
1632
 
2012
- self.log_header("Processing Payload")
1633
+ return True
1634
+
1635
+ # end method definition
1636
+
1637
+ def customization_run(self) -> bool:
1638
+ """Central method to initiate the customization."""
1639
+
1640
+ success = True
1641
+
1642
+ # Set Timer for duration calculation
1643
+ self.customizer_start_time = datetime.now(timezone.utc)
1644
+
1645
+ if not self.init_customizer():
1646
+ self.logger.error("Initialization of customizer failed!")
1647
+ return False
1648
+
1649
+ # Put Frontend in Maintenance mode to make sure nobody interferes
1650
+ # during customization:
1651
+ if self.settings.otcs.maintenance_mode:
1652
+ self.set_maintenance_mode(enable=True)
1653
+
1654
+ self.log_header("Collect payload files to process")
2013
1655
 
2014
1656
  cust_payload_list = []
2015
1657
  # Is uncompressed payload provided?
2016
- if os.path.exists(self.settings.cust_payload):
2017
- logger.info("Found payload file -> '%s'", self.settings.cust_payload)
1658
+ if self.settings.cust_payload and os.path.exists(self.settings.cust_payload):
1659
+ self.logger.info("Found payload file -> '%s'", self.settings.cust_payload)
2018
1660
  cust_payload_list.append(self.settings.cust_payload)
2019
1661
  # Is compressed payload provided?
2020
- if os.path.exists(self.settings.cust_payload_gz):
2021
- logger.info(
2022
- "Found compressed payload file -> '%s'", self.settings.cust_payload_gz
1662
+ if self.settings.cust_payload_gz and os.path.exists(
1663
+ self.settings.cust_payload_gz,
1664
+ ):
1665
+ self.logger.info(
1666
+ "Found compressed payload file -> '%s'",
1667
+ self.settings.cust_payload_gz,
2023
1668
  )
2024
1669
  cust_payload_list.append(self.settings.cust_payload_gz)
2025
1670
 
2026
1671
  # do we have additional payload as an external file?
2027
- if os.path.exists(self.settings.cust_payload_external):
1672
+ if self.settings.cust_payload_external and os.path.exists(
1673
+ self.settings.cust_payload_external,
1674
+ ):
2028
1675
  for filename in sorted(
2029
- os.scandir(self.settings.cust_payload_external), key=lambda e: e.name
1676
+ os.scandir(self.settings.cust_payload_external),
1677
+ key=lambda e: e.name,
2030
1678
  ):
2031
1679
  if filename.is_file() and os.path.getsize(filename) > 0:
2032
- logger.info("Found external payload file -> '%s'", filename.path)
1680
+ self.logger.info(
1681
+ "Found external payload file -> '%s'",
1682
+ filename.path,
1683
+ )
2033
1684
  cust_payload_list.append(filename.path)
2034
- else:
2035
- logger.info(
2036
- "No external payload file -> '%s'", self.settings.cust_payload_external
1685
+ elif self.settings.cust_payload_external:
1686
+ self.logger.warning(
1687
+ "External payload file -> '%s' does not exist!",
1688
+ self.settings.cust_payload_external,
2037
1689
  )
2038
1690
 
2039
1691
  for cust_payload in cust_payload_list:
2040
- # Open the payload file. If this fails we bail out:
2041
- logger.info("Starting processing of payload -> '%s'", cust_payload)
1692
+ self.log_header("Start processing of payload -> '{}'".format(cust_payload))
2042
1693
 
2043
1694
  # Set startTime for duration calculation
2044
- start_time = datetime.now()
1695
+ start_time = datetime.now(timezone.utc)
2045
1696
 
1697
+ # Create payload object:
2046
1698
  payload_object = Payload(
2047
1699
  payload_source=cust_payload,
2048
1700
  custom_settings_dir=self.settings.cust_settings_dir,
@@ -2053,22 +1705,27 @@ class Customizer:
2053
1705
  otcs_frontend_object=self.otcs_frontend_object,
2054
1706
  otcs_restart_callback=self.restart_otcs_service,
2055
1707
  otiv_object=self.otiv_object,
1708
+ otpd_object=self.otpd_object,
2056
1709
  m365_object=self.m365_object,
2057
1710
  core_share_object=self.core_share_object,
2058
1711
  browser_automation_object=self.browser_automation_object,
1712
+ browser_headless=self.settings.headless_browser,
2059
1713
  placeholder_values=self.settings.placeholder_values, # this dict includes placeholder replacements for the Ressource IDs of OTAWP and OTCS
2060
1714
  log_header_callback=self.log_header,
2061
1715
  stop_on_error=self.settings.stop_on_error,
2062
- aviator_enabled=self.aviator_settings.enabled,
2063
- upload_status_files=self.otcs_settings.upload_status_files,
1716
+ aviator_enabled=self.settings.aviator.enabled,
1717
+ upload_status_files=self.settings.otcs.upload_status_files,
2064
1718
  otawp_object=self.otawp_object,
2065
1719
  avts_object=self.avts_object,
1720
+ logger=self.logger,
2066
1721
  )
2067
1722
  # Load the payload file and initialize the payload sections:
2068
1723
  if not payload_object.init_payload():
2069
- logger.error(
2070
- "Failed to initialize payload -> %s - skipping...", cust_payload
1724
+ self.logger.error(
1725
+ "Failed to initialize payload -> '%s' - skipping payload file...",
1726
+ cust_payload,
2071
1727
  )
1728
+ success = False
2072
1729
  continue
2073
1730
 
2074
1731
  # Now process the payload in the defined ordering:
@@ -2078,119 +1735,136 @@ class Customizer:
2078
1735
  self.consolidate_otds()
2079
1736
 
2080
1737
  # Upload payload file for later review to Enterprise Workspace
2081
- if self.otcs_settings.upload_config_files:
1738
+ if self.settings.otcs.upload_config_files:
2082
1739
  self.log_header("Upload Payload file to Extended ECM")
2083
1740
  response = self.otcs_backend_object.get_node_from_nickname(
2084
- self.settings.cust_target_folder_nickname
1741
+ nickname=self.settings.cust_target_folder_nickname,
2085
1742
  )
2086
1743
  target_folder_id = self.otcs_backend_object.get_result_value(
2087
- response, "id"
1744
+ response=response,
1745
+ key="id",
2088
1746
  )
2089
1747
  if not target_folder_id:
2090
1748
  target_folder_id = 2000 # use Enterprise Workspace as fallback
2091
1749
  # Write YAML file with upadated payload (including IDs, etc.).
2092
- # We need to write to /tmp as initial location is read-only:
1750
+ # We need to write to a temporary location as initial location is read-only:
2093
1751
  payload_file = os.path.basename(cust_payload)
2094
- payload_file = (
2095
- payload_file[: -len(".gz.b64")]
2096
- if payload_file.endswith(".gz.b64")
2097
- else payload_file
1752
+ payload_file = payload_file.removesuffix(".gz.b64")
1753
+ payload_file = payload_file.replace(".tfvars", ".yaml").replace(
1754
+ ".tf",
1755
+ ".yaml",
2098
1756
  )
2099
- cust_payload = "/tmp/" + payload_file
1757
+ cust_payload = os.path.join(tempfile.gettempdir(), payload_file)
2100
1758
 
2101
1759
  with open(cust_payload, "w", encoding="utf-8") as file:
2102
- yaml.dump(payload_object.get_payload(), file)
1760
+ yaml.dump(
1761
+ data=payload_object.get_payload(
1762
+ drop_bulk_datasources_data=True,
1763
+ ),
1764
+ stream=file,
1765
+ )
2103
1766
 
2104
1767
  # Check if the payload file has been uploaded before.
2105
1768
  # This can happen if we re-run the python container.
2106
1769
  # In this case we add a version to the existing document:
2107
1770
  response = self.otcs_backend_object.get_node_by_parent_and_name(
2108
- int(target_folder_id), os.path.basename(cust_payload)
1771
+ parent_id=int(target_folder_id),
1772
+ name=os.path.basename(cust_payload),
2109
1773
  )
2110
1774
  target_document_id = self.otcs_backend_object.get_result_value(
2111
- response, "id"
1775
+ response=response,
1776
+ key="id",
2112
1777
  )
2113
1778
  if target_document_id:
2114
1779
  response = self.otcs_backend_object.add_document_version(
2115
- int(target_document_id),
2116
- cust_payload,
2117
- os.path.basename(cust_payload),
2118
- "text/plain",
2119
- "Updated payload file after re-run of customization",
1780
+ node_id=int(target_document_id),
1781
+ file_url=cust_payload,
1782
+ file_name=os.path.basename(cust_payload),
1783
+ mime_type="text/plain",
1784
+ description="Updated payload file after re-run of customization",
2120
1785
  )
2121
1786
  else:
2122
1787
  response = self.otcs_backend_object.upload_file_to_parent(
2123
- cust_payload,
2124
- os.path.basename(cust_payload),
2125
- "text/plain",
2126
- int(target_folder_id),
1788
+ file_url=cust_payload,
1789
+ file_name=os.path.basename(cust_payload),
1790
+ mime_type="text/plain",
1791
+ parent_id=int(target_folder_id),
2127
1792
  )
2128
1793
 
2129
- duration = datetime.now() - start_time
1794
+ duration = datetime.now(timezone.utc) - start_time
2130
1795
  self.log_header(
2131
1796
  "Customizer completed processing of payload -> {} in {}".format(
2132
1797
  cust_payload,
2133
1798
  duration,
2134
- )
2135
- )
2136
-
2137
- if self.otcs_settings.maintenance_mode:
2138
- self.set_maintenance_mode(False)
2139
-
2140
- # Restart AppWorksPlatform pod if it is deployed (to make settings effective):
2141
- if self.otawp_settings.enabled: # is AppWorks Platform deployed?
2142
- otawp_resource = self.otds_object.get_resource(
2143
- self.otawp_settings.resource_name
2144
- )
2145
- if (
2146
- not "allowImpersonation" in otawp_resource
2147
- or not otawp_resource["allowImpersonation"]
2148
- ):
2149
- # Allow impersonation for all users:
2150
- logger.warning(
2151
- "OTAWP impersonation is not correct in OTDS before OTAWP pod restart!"
2152
- )
2153
- else:
2154
- logger.info(
2155
- "OTAWP impersonation is correct in OTDS before OTAWP pod restart!"
2156
- )
2157
- logger.info("Restart OTAWP pod...")
2158
- self.restart_otawp_pod()
2159
- # For some reason we need to double-check that the impersonation for OTAWP has been set correctly
2160
- # and if not set it again:
2161
- otawp_resource = self.otds_object.get_resource(
2162
- self.otawp_settings.resource_name
2163
- )
2164
- if (
2165
- not "allowImpersonation" in otawp_resource
2166
- or not otawp_resource["allowImpersonation"]
2167
- ):
2168
- # Allow impersonation for all users:
2169
- logger.warning(
2170
- "OTAWP impersonation is not correct in OTDS - set it once more..."
2171
- )
2172
- self.otds_object.impersonate_resource(self.otawp_settings.resource_name)
2173
-
2174
- # Upload log file for later review to "Deployment" folder in "Administration" folder
2175
- if (
2176
- os.path.exists(self.settings.cust_log_file)
2177
- and self.otcs_settings.upload_log_file
2178
- ):
1799
+ ),
1800
+ )
1801
+ # end for cust_payload in cust_payload_list
1802
+
1803
+ if self.settings.otcs.maintenance_mode:
1804
+ self.set_maintenance_mode(enable=False)
1805
+
1806
+ # Code below disbaled -> not needed anymore, will be done via "kubernetes" payload section
1807
+ #
1808
+ # # Restart AppWorksPlatform pod if it is deployed (to make settings effective):
1809
+ # if self.settings.otawp.enabled: # is AppWorks Platform deployed?
1810
+ # otawp_resource = self.otds_object.get_resource(
1811
+ # name=self.settings.otawp.resource_name,
1812
+ # )
1813
+ # if "allowImpersonation" not in otawp_resource or not otawp_resource["allowImpersonation"]:
1814
+ # # Allow impersonation for all users:
1815
+ # self.logger.warning(
1816
+ # "OTAWP impersonation is not correct in OTDS before OTAWP pod restart!",
1817
+ # )
1818
+ # else:
1819
+ # self.logger.info(
1820
+ # "OTAWP impersonation is correct in OTDS before OTAWP pod restart!",
1821
+ # )
1822
+ # self.logger.info("Restart OTAWP pod...")
1823
+ # self.restart_otawp_pod()
1824
+ # # For some reason we need to double-check that the impersonation
1825
+ # # for OTAWP has been set correctly and if not set it again:
1826
+ # otawp_resource = self.otds_object.get_resource(
1827
+ # name=self.settings.otawp.resource_name,
1828
+ # )
1829
+ # if "allowImpersonation" not in otawp_resource or not otawp_resource["allowImpersonation"]:
1830
+ # # Allow impersonation for all users:
1831
+ # self.logger.warning(
1832
+ # "OTAWP impersonation is not correct in OTDS - set it once more...",
1833
+ # )
1834
+ # self.otds_object.impersonate_resource(
1835
+ # resource_name=self.settings.otawp.resource_name,
1836
+ # )
1837
+
1838
+ # # Restart Aviator Search (Omnigroup) to ensure group synchronisation is working
1839
+ # if self.settings.avts.enabled: # is Aviator Search deployed?
1840
+ # self.logger.info(
1841
+ # "Restarting Aviator Search Omnigroup server after creation of OTDS client ID and client secret...",
1842
+ # )
1843
+ # self.k8s_object.restart_stateful_set(sts_name="idol-omnigroupserver")
1844
+
1845
+ # Upload log file for later review to "Deployment" folder
1846
+ # in "Administration" folder in OTCS Enterprise volume:
1847
+ if os.path.exists(self.settings.cust_log_file) and self.settings.otcs.upload_log_file:
2179
1848
  self.log_header("Upload log file to Extended ECM")
2180
1849
  response = self.otcs_backend_object.get_node_from_nickname(
2181
- self.settings.cust_target_folder_nickname
1850
+ nickname=self.settings.cust_target_folder_nickname,
1851
+ )
1852
+ target_folder_id = self.otcs_backend_object.get_result_value(
1853
+ response=response,
1854
+ key="id",
2182
1855
  )
2183
- target_folder_id = self.otcs_backend_object.get_result_value(response, "id")
2184
1856
  if not target_folder_id:
2185
1857
  target_folder_id = 2000 # use Enterprise Workspace as fallback
2186
1858
  # Check if the log file has been uploaded before.
2187
1859
  # This can happen if we re-run the python container:
2188
1860
  # In this case we add a version to the existing document:
2189
1861
  response = self.otcs_backend_object.get_node_by_parent_and_name(
2190
- int(target_folder_id), os.path.basename(self.settings.cust_log_file)
1862
+ parent_id=int(target_folder_id),
1863
+ name=os.path.basename(self.settings.cust_log_file),
2191
1864
  )
2192
1865
  target_document_id = self.otcs_backend_object.get_result_value(
2193
- response, "id"
1866
+ response=response,
1867
+ key="id",
2194
1868
  )
2195
1869
  if target_document_id:
2196
1870
  response = self.otcs_backend_object.add_document_version(
@@ -2209,37 +1883,14 @@ class Customizer:
2209
1883
  description="Initial Python Log after first run of customization",
2210
1884
  )
2211
1885
 
2212
- self.settings.customizer_end_time = datetime.now()
1886
+ self.customizer_end_time = datetime.now(timezone.utc)
2213
1887
  self.log_header(
2214
1888
  "Customizer completed in {}".format(
2215
- self.settings.customizer_end_time - self.settings.customizer_start_time
2216
- )
1889
+ self.customizer_end_time - self.customizer_start_time,
1890
+ ),
2217
1891
  )
2218
1892
 
1893
+ # Return the success status:
1894
+ return success
2219
1895
 
2220
- if __name__ == "__main__":
2221
- logging.basicConfig(
2222
- format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
2223
- datefmt="%d-%b-%Y %H:%M:%S",
2224
- level=logging.INFO,
2225
- handlers=[
2226
- logging.StreamHandler(sys.stdout),
2227
- ],
2228
- )
2229
-
2230
- my_customizer = Customizer(
2231
- otcs=CustomizerSettingsOTCS(
2232
- hostname="otcs.local.xecm.cloud",
2233
- hostname_backend="otcs-admin-0",
2234
- hostname_frontend="otcs-frontend",
2235
- protocol="http",
2236
- port_backend=8080,
2237
- ),
2238
- otds=CustomizerSettingsOTDS(hostname="otds"),
2239
- otpd=CustomizerSettingsOTPD(enabled=False),
2240
- otac=CustomizerSettingsOTAC(enabled=False),
2241
- k8s=CustomizerSettingsK8S(enabled=True),
2242
- otiv=CustomizerSettingsOTIV(enabled=False),
2243
- )
2244
-
2245
- my_customizer.customization_run()
1896
+ # end method definition