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.
- pyxecm/__init__.py +7 -4
- pyxecm/avts.py +727 -254
- pyxecm/coreshare.py +686 -467
- pyxecm/customizer/__init__.py +16 -4
- pyxecm/customizer/__main__.py +58 -0
- pyxecm/customizer/api/__init__.py +5 -0
- pyxecm/customizer/api/__main__.py +6 -0
- pyxecm/customizer/api/app.py +163 -0
- pyxecm/customizer/api/auth/__init__.py +1 -0
- pyxecm/customizer/api/auth/functions.py +92 -0
- pyxecm/customizer/api/auth/models.py +13 -0
- pyxecm/customizer/api/auth/router.py +78 -0
- pyxecm/customizer/api/common/__init__.py +1 -0
- pyxecm/customizer/api/common/functions.py +47 -0
- pyxecm/customizer/api/common/metrics.py +92 -0
- pyxecm/customizer/api/common/models.py +21 -0
- pyxecm/customizer/api/common/payload_list.py +870 -0
- pyxecm/customizer/api/common/router.py +72 -0
- pyxecm/customizer/api/settings.py +128 -0
- pyxecm/customizer/api/terminal/__init__.py +1 -0
- pyxecm/customizer/api/terminal/router.py +87 -0
- pyxecm/customizer/api/v1_csai/__init__.py +1 -0
- pyxecm/customizer/api/v1_csai/router.py +87 -0
- pyxecm/customizer/api/v1_maintenance/__init__.py +1 -0
- pyxecm/customizer/api/v1_maintenance/functions.py +100 -0
- pyxecm/customizer/api/v1_maintenance/models.py +12 -0
- pyxecm/customizer/api/v1_maintenance/router.py +76 -0
- pyxecm/customizer/api/v1_otcs/__init__.py +1 -0
- pyxecm/customizer/api/v1_otcs/functions.py +61 -0
- pyxecm/customizer/api/v1_otcs/router.py +179 -0
- pyxecm/customizer/api/v1_payload/__init__.py +1 -0
- pyxecm/customizer/api/v1_payload/functions.py +179 -0
- pyxecm/customizer/api/v1_payload/models.py +51 -0
- pyxecm/customizer/api/v1_payload/router.py +499 -0
- pyxecm/customizer/browser_automation.py +721 -286
- pyxecm/customizer/customizer.py +1076 -1425
- pyxecm/customizer/exceptions.py +35 -0
- pyxecm/customizer/guidewire.py +1186 -0
- pyxecm/customizer/k8s.py +901 -379
- pyxecm/customizer/log.py +107 -0
- pyxecm/customizer/m365.py +2967 -920
- pyxecm/customizer/nhc.py +1169 -0
- pyxecm/customizer/openapi.py +258 -0
- pyxecm/customizer/payload.py +18228 -7820
- pyxecm/customizer/pht.py +717 -286
- pyxecm/customizer/salesforce.py +516 -342
- pyxecm/customizer/sap.py +58 -41
- pyxecm/customizer/servicenow.py +611 -372
- pyxecm/customizer/settings.py +445 -0
- pyxecm/customizer/successfactors.py +408 -346
- pyxecm/customizer/translate.py +83 -48
- pyxecm/helper/__init__.py +5 -2
- pyxecm/helper/assoc.py +83 -43
- pyxecm/helper/data.py +2406 -870
- pyxecm/helper/logadapter.py +27 -0
- pyxecm/helper/web.py +229 -101
- pyxecm/helper/xml.py +596 -171
- pyxecm/maintenance_page/__init__.py +5 -0
- pyxecm/maintenance_page/__main__.py +6 -0
- pyxecm/maintenance_page/app.py +51 -0
- pyxecm/maintenance_page/settings.py +28 -0
- pyxecm/maintenance_page/static/favicon.avif +0 -0
- pyxecm/maintenance_page/templates/maintenance.html +165 -0
- pyxecm/otac.py +235 -141
- pyxecm/otawp.py +2668 -1220
- pyxecm/otca.py +569 -0
- pyxecm/otcs.py +7956 -3237
- pyxecm/otds.py +2178 -925
- pyxecm/otiv.py +36 -21
- pyxecm/otmm.py +1272 -325
- pyxecm/otpd.py +231 -127
- pyxecm-2.0.1.dist-info/METADATA +122 -0
- pyxecm-2.0.1.dist-info/RECORD +76 -0
- {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/WHEEL +1 -1
- pyxecm-1.6.dist-info/METADATA +0 -53
- pyxecm-1.6.dist-info/RECORD +0 -32
- {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info/licenses}/LICENSE +0 -0
- {pyxecm-1.6.dist-info → pyxecm-2.0.1.dist-info}/top_level.txt +0 -0
pyxecm/customizer/customizer.py
CHANGED
|
@@ -1,47 +1,7 @@
|
|
|
1
|
-
"""
|
|
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
|
|
54
|
-
from
|
|
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
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
335
|
-
self.core_share_settings = core_share
|
|
56
|
+
"""
|
|
336
57
|
|
|
337
|
-
|
|
338
|
-
self.aviator_settings = aviator
|
|
58
|
+
self.logger = logger
|
|
339
59
|
|
|
340
|
-
#
|
|
341
|
-
self.
|
|
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
|
|
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 =
|
|
360
|
-
"""
|
|
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):
|
|
364
|
-
|
|
365
|
-
|
|
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,
|
|
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",
|
|
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
|
-
|
|
396
|
-
|
|
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",
|
|
132
|
+
self.logger.info(
|
|
133
|
+
"Microsoft 365 Tenant ID = %s",
|
|
134
|
+
self.settings.m365.tenant_id,
|
|
401
135
|
)
|
|
402
|
-
logger.
|
|
403
|
-
"Microsoft 365 Client ID = %s",
|
|
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 =
|
|
140
|
+
self.logger.debug(
|
|
141
|
+
"Microsoft 365 Client Secret = <sensitive>",
|
|
142
|
+
# self.settings.m365.client_secret,
|
|
407
143
|
)
|
|
408
|
-
logger.info(
|
|
409
|
-
"Microsoft 365
|
|
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.
|
|
417
|
-
"Microsoft 365
|
|
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.
|
|
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.
|
|
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.
|
|
441
|
-
client_id=self.
|
|
442
|
-
client_secret=self.
|
|
443
|
-
domain=self.
|
|
444
|
-
sku_id=self.
|
|
445
|
-
teams_app_name=self.
|
|
446
|
-
teams_app_external_id=self.
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
self.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
"
|
|
466
|
-
"
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
self.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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=
|
|
565
|
-
update_existing_app=
|
|
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
|
|
575
|
-
self.
|
|
576
|
-
self.
|
|
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
|
|
584
|
-
self.
|
|
585
|
-
self.
|
|
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.
|
|
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:
|
|
650
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
664
|
-
self.
|
|
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
|
|
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
|
|
671
|
-
self.
|
|
419
|
+
self.logger.debug(
|
|
420
|
+
"Aviator Search User Password = %s",
|
|
421
|
+
self.settings.avts.password,
|
|
672
422
|
)
|
|
673
423
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
-
|
|
694
|
-
|
|
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",
|
|
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",
|
|
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",
|
|
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.
|
|
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.
|
|
729
|
-
sso_url=self.
|
|
730
|
-
client_id=self.
|
|
731
|
-
client_secret=self.
|
|
732
|
-
username=self.
|
|
733
|
-
password=self.
|
|
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
|
|
763
|
-
logger.info(
|
|
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.
|
|
515
|
+
self.settings.k8s.kubeconfig_file,
|
|
767
516
|
)
|
|
768
517
|
|
|
769
518
|
k8s_object = K8s(
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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.
|
|
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.
|
|
535
|
+
self.settings.k8s.sts_otcs_frontend,
|
|
787
536
|
)
|
|
788
537
|
sys.exit()
|
|
789
538
|
|
|
790
|
-
self.
|
|
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.
|
|
794
|
-
self.
|
|
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.
|
|
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.
|
|
553
|
+
self.settings.k8s.sts_otcs_admin,
|
|
805
554
|
)
|
|
806
555
|
sys.exit()
|
|
807
556
|
|
|
808
|
-
self.
|
|
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.
|
|
812
|
-
self.
|
|
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
|
-
|
|
826
|
-
|
|
575
|
+
OTDS:
|
|
576
|
+
The OTDS object
|
|
827
577
|
|
|
828
|
-
|
|
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
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
623
|
+
if self.settings.otds.enable_audit:
|
|
860
624
|
otds_object.enable_audit()
|
|
861
625
|
|
|
862
|
-
if self.
|
|
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
|
-
|
|
880
|
-
|
|
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
|
-
|
|
884
|
-
|
|
648
|
+
|
|
649
|
+
Returns:
|
|
650
|
+
The OTAC object.
|
|
651
|
+
|
|
885
652
|
"""
|
|
886
653
|
|
|
887
|
-
logger.info("Connection parameters OTAC:")
|
|
888
|
-
logger.info("OTAC
|
|
889
|
-
logger.info("OTAC
|
|
890
|
-
logger.info("OTAC
|
|
891
|
-
logger.
|
|
892
|
-
logger.info(
|
|
893
|
-
|
|
894
|
-
|
|
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.
|
|
905
|
-
self.
|
|
906
|
-
int(self.
|
|
907
|
-
self.
|
|
908
|
-
self.
|
|
909
|
-
self.
|
|
910
|
-
self.
|
|
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",
|
|
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
|
|
690
|
+
self.logger.error("Failed to enable OTAC certificate for OTCS!")
|
|
920
691
|
else:
|
|
921
|
-
logger.info("Successfully enabled OTAC certificate for
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
resource_name (str): name of OTDS resource for Extended ECM
|
|
733
|
+
url (HttpURL):
|
|
734
|
+
The OTCS URL.
|
|
735
|
+
|
|
969
736
|
Returns:
|
|
970
|
-
OTCS:
|
|
737
|
+
OTCS:
|
|
738
|
+
The OTCS object
|
|
739
|
+
|
|
971
740
|
"""
|
|
972
741
|
|
|
973
|
-
logger.info("Connection parameters OTCS (Extended ECM):")
|
|
974
|
-
logger.info("OTCS
|
|
975
|
-
logger.info(
|
|
976
|
-
"OTCS
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
logger.info(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
logger.info("OTCS User
|
|
984
|
-
logger.
|
|
985
|
-
|
|
986
|
-
|
|
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.
|
|
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.
|
|
775
|
+
self.settings.k8s.sts_otcs_admin,
|
|
995
776
|
)
|
|
996
|
-
logger.info(
|
|
777
|
+
self.logger.info(
|
|
997
778
|
"FEME URI = %s",
|
|
998
|
-
self.
|
|
779
|
+
self.settings.otcs.feme_uri,
|
|
999
780
|
)
|
|
1000
781
|
|
|
1001
|
-
|
|
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
|
-
|
|
1008
|
-
hostname,
|
|
1009
|
-
|
|
1010
|
-
self.
|
|
1011
|
-
self.
|
|
1012
|
-
self.
|
|
1013
|
-
|
|
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.
|
|
1017
|
-
feme_uri=self.
|
|
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.
|
|
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
|
-
#
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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
|
-
|
|
1046
|
-
|
|
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.
|
|
1055
|
-
otcs_resource =
|
|
1056
|
-
self.
|
|
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
|
-
|
|
860
|
+
OTIV:
|
|
861
|
+
The OTIV object.
|
|
862
|
+
|
|
1078
863
|
"""
|
|
1079
864
|
|
|
1080
|
-
logger.info("Parameters for OTIV (Intelligent Viewing):")
|
|
1081
|
-
logger.info(
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
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.
|
|
1091
|
-
product_name=self.
|
|
1092
|
-
product_description=self.
|
|
1093
|
-
license_file=self.
|
|
1094
|
-
default_license=self.
|
|
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.
|
|
896
|
+
otiv_resource = self.otds_object.get_resource(self.settings.otiv.resource_name)
|
|
1098
897
|
while otiv_resource is None:
|
|
1099
|
-
logger.
|
|
898
|
+
self.logger.info(
|
|
1100
899
|
"OTDS Resource -> %s for Intelligent Viewing not found. OTIV may not be ready. Wait 30 sec...",
|
|
1101
|
-
self.
|
|
900
|
+
self.settings.otiv.resource_name,
|
|
1102
901
|
)
|
|
1103
902
|
time.sleep(30)
|
|
1104
903
|
otiv_resource = self.otds_object.get_resource(
|
|
1105
|
-
self.
|
|
904
|
+
self.settings.otiv.resource_name,
|
|
1106
905
|
)
|
|
1107
906
|
|
|
1108
907
|
otiv_license = self.otds_object.add_license_to_resource(
|
|
1109
|
-
self.
|
|
1110
|
-
self.
|
|
1111
|
-
self.
|
|
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.
|
|
1118
|
-
self.
|
|
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
|
-
|
|
950
|
+
OTPD:
|
|
951
|
+
The OTPD (PowerDocs) object.
|
|
952
|
+
|
|
1151
953
|
"""
|
|
1152
954
|
|
|
1153
|
-
logger.info("Connection parameters OTPD (PowerDocs):")
|
|
1154
|
-
logger.info(
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
logger.info("OTPD
|
|
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.
|
|
968
|
+
self.logger.info("OTPD K8s Pod Name = %s", self.settings.k8s.pod_otpd)
|
|
1168
969
|
|
|
1169
970
|
otpd_object = OTPD(
|
|
1170
|
-
self.
|
|
1171
|
-
self.
|
|
1172
|
-
|
|
1173
|
-
self.
|
|
1174
|
-
self.
|
|
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.
|
|
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
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
-
|
|
1206
|
-
|
|
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(
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
logger.
|
|
1215
|
-
logger.info(
|
|
1216
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
1047
|
+
self.settings.otcs.resource_name,
|
|
1230
1048
|
)
|
|
1231
1049
|
time.sleep(30)
|
|
1232
1050
|
otcs_resource = self.otds_object.get_resource(
|
|
1233
|
-
self.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
1066
|
+
name=self.settings.otawp.resource_name,
|
|
1458
1067
|
description="AppWorks Platform",
|
|
1459
1068
|
display_name="AppWorks Platform",
|
|
1460
|
-
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.
|
|
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(
|
|
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",
|
|
1090
|
+
self.logger.debug(
|
|
1091
|
+
"Placeholder values after OTAWP init = %s",
|
|
1092
|
+
self.settings.placeholder_values,
|
|
1476
1093
|
)
|
|
1477
1094
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
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
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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.
|
|
1522
|
-
|
|
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
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|
|
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.
|
|
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.
|
|
1641
|
-
self.
|
|
1642
|
-
str(self.
|
|
1643
|
-
"sysadmin",
|
|
1644
|
-
self.
|
|
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(
|
|
1652
|
-
|
|
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
|
-
|
|
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(
|
|
1670
|
-
pod_name = self.
|
|
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(
|
|
1692
|
-
pod_name = self.
|
|
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
|
-
|
|
1713
|
-
|
|
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.
|
|
1327
|
+
self.logger.info("Waiting 30 seconds for OTCS backend to become ready...")
|
|
1716
1328
|
time.sleep(30)
|
|
1717
|
-
otcs_cookie =
|
|
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(
|
|
1722
|
-
pod_name = self.
|
|
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(
|
|
1732
|
-
pod_name = self.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1405
|
+
self.otds_object.consolidate(self.settings.otcs.resource_name)
|
|
1806
1406
|
|
|
1807
|
-
if self.
|
|
1808
|
-
self.otds_object.consolidate(self.
|
|
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 (
|
|
1416
|
+
otpd_object (OTPD):
|
|
1417
|
+
The PowerDocs object.
|
|
1418
|
+
|
|
1817
1419
|
"""
|
|
1818
1420
|
|
|
1819
|
-
if self.
|
|
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.
|
|
1426
|
+
self.settings.otpd.db_importfile,
|
|
1825
1427
|
)
|
|
1826
1428
|
|
|
1827
1429
|
try:
|
|
1828
|
-
package = requests.get(self.
|
|
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.
|
|
1434
|
+
self.settings.otpd.db_importfile,
|
|
1833
1435
|
package.status_code,
|
|
1834
1436
|
)
|
|
1835
|
-
filename = "
|
|
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.
|
|
1842
|
-
self.
|
|
1843
|
-
self.
|
|
1844
|
-
self.
|
|
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(
|
|
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
|
|
1850
|
-
logger.error("
|
|
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):
|
|
1462
|
+
enable (bool, optional):
|
|
1463
|
+
Whether or not to activate the maintenance mode web page.
|
|
1464
|
+
Defaults to True.
|
|
1465
|
+
|
|
1859
1466
|
"""
|
|
1860
|
-
|
|
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.
|
|
1474
|
+
self.settings.k8s.ingress_otxecm,
|
|
1867
1475
|
"otcs",
|
|
1868
|
-
self.
|
|
1869
|
-
self.
|
|
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.
|
|
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.
|
|
1491
|
+
self.settings.k8s.ingress_otxecm,
|
|
1884
1492
|
"otcs",
|
|
1885
|
-
self.
|
|
1886
|
-
self.
|
|
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
|
|
1893
|
-
"""
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
-
|
|
1900
|
-
|
|
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
|
-
|
|
1525
|
+
self.logger.error("Failed to initialize OTDS - exiting...")
|
|
1526
|
+
return False
|
|
1908
1527
|
|
|
1909
1528
|
# Establish in-cluster Kubernetes connection
|
|
1910
|
-
self.
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1565
|
+
self.logger.error("Failed to initialize OTCS frontend - exiting...")
|
|
1566
|
+
return False
|
|
1952
1567
|
|
|
1953
|
-
if self.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1971
|
-
self.log_header("Initialize
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2021
|
-
|
|
2022
|
-
|
|
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(
|
|
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),
|
|
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(
|
|
1680
|
+
self.logger.info(
|
|
1681
|
+
"Found external payload file -> '%s'",
|
|
1682
|
+
filename.path,
|
|
1683
|
+
)
|
|
2033
1684
|
cust_payload_list.append(filename.path)
|
|
2034
|
-
|
|
2035
|
-
logger.
|
|
2036
|
-
"
|
|
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
|
-
|
|
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.
|
|
2063
|
-
upload_status_files=self.
|
|
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...",
|
|
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.
|
|
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,
|
|
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
|
|
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
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
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 =
|
|
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(
|
|
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),
|
|
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,
|
|
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
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
#
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
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),
|
|
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,
|
|
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.
|
|
1886
|
+
self.customizer_end_time = datetime.now(timezone.utc)
|
|
2213
1887
|
self.log_header(
|
|
2214
1888
|
"Customizer completed in {}".format(
|
|
2215
|
-
self.
|
|
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
|
-
|
|
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
|