mas-cli 11.6.0__py3-none-any.whl → 11.7.0__py3-none-any.whl

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

Potentially problematic release.


This version of mas-cli might be problematic. Click here for more details.

mas/cli/install/app.py CHANGED
@@ -29,6 +29,8 @@ from .argBuilder import installArgBuilderMixin
29
29
  from .argParser import installArgParser
30
30
  from .settings import InstallSettingsMixin
31
31
  from .summarizer import InstallSummarizerMixin
32
+ from .params import requiredParams, optionalParams
33
+ from .catalogs import catalogChoices
32
34
 
33
35
  from mas.cli.validators import (
34
36
  InstanceIDFormatValidator,
@@ -52,7 +54,17 @@ from mas.devops.tekton import (
52
54
  logger = logging.getLogger(__name__)
53
55
 
54
56
 
57
+ def logMethodCall(func):
58
+ def wrapper(self, *args, **kwargs):
59
+ logger.debug(f">>> InstallApp.{func.__name__}")
60
+ result = func(self, *args, **kwargs)
61
+ logger.debug(f"<<< InstallApp.{func.__name__}")
62
+ return result
63
+ return wrapper
64
+
65
+
55
66
  class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGeneratorMixin, installArgBuilderMixin):
67
+ @logMethodCall
56
68
  def validateCatalogSource(self):
57
69
  catalogsAPI = self.dynamicClient.resources.get(api_version="operators.coreos.com/v1alpha1", kind="CatalogSource")
58
70
  try:
@@ -75,6 +87,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
75
87
  # There's no existing catalog installed
76
88
  pass
77
89
 
90
+ @logMethodCall
78
91
  def validateInternalRegistryAvailable(self):
79
92
  """
80
93
  We can save customers wasted time by detecting if the image-registry service
@@ -93,6 +106,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
93
106
  ])
94
107
  )
95
108
 
109
+ @logMethodCall
96
110
  def licensePrompt(self):
97
111
  licenses = {
98
112
  "8.9.x": " - <u>https://ibm.biz/MAS89-License</u>",
@@ -114,6 +128,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
114
128
  if not self.yesOrNo("Do you accept the license terms"):
115
129
  exit(1)
116
130
 
131
+ @logMethodCall
117
132
  def configICR(self):
118
133
  if self.devMode:
119
134
  self.setParam("mas_icr_cp", getenv("MAS_ICR_CP", "docker-na-public.artifactory.swg-devops.com/wiotp-docker-local"))
@@ -124,6 +139,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
124
139
  self.setParam("mas_icr_cpopen", getenv("MAS_ICR_CPOPEN", "icr.io/cpopen"))
125
140
  self.setParam("sls_icr_cpopen", getenv("SLS_ICR_CPOPEN", "icr.io/cpopen"))
126
141
 
142
+ @logMethodCall
127
143
  def configICRCredentials(self):
128
144
  self.printH1("Configure IBM Container Registry")
129
145
  self.promptForString("IBM entitlement key", "ibm_entitlement_key", isPassword=True)
@@ -131,11 +147,13 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
131
147
  self.promptForString("Artifactory username", "artifactory_username")
132
148
  self.promptForString("Artifactory token", "artifactory_token", isPassword=True)
133
149
 
150
+ @logMethodCall
134
151
  def configCertManager(self):
135
152
  # Only install of Red Hat Cert-Manager has been supported since the January 2025 catalog update
136
153
  self.setParam("cert_manager_provider", "redhat")
137
154
  self.setParam("cert_manager_action", "install")
138
155
 
156
+ @logMethodCall
139
157
  def configCatalog(self):
140
158
  self.printH1("IBM Maximo Operator Catalog Selection")
141
159
  if self.devMode:
@@ -148,6 +166,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
148
166
  self.setParam("mas_catalog_version", self.installOptions[catalogSelection - 1]["catalog"])
149
167
  self.setParam("mas_channel", self.installOptions[catalogSelection - 1]["release"])
150
168
 
169
+ @logMethodCall
151
170
  def configSLS(self) -> None:
152
171
  self.printH1("Configure Product License")
153
172
  self.slsLicenseFileLocal = self.promptForFile("License file", mustExist=True, envVar="SLS_LICENSE_FILE_LOCAL")
@@ -155,42 +174,52 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
155
174
  self.promptForString("Contact first name", "uds_contact_firstname")
156
175
  self.promptForString("Contact last name", "uds_contact_lastname")
157
176
 
158
- self.promptForString("IBM Data Reporter Operator (DRO) Namespace", "dro_namespace", default="redhat-marketplace")
177
+ if self.showAdvancedOptions:
178
+ self.promptForString("IBM Suite License Services (SLS) Namespace", "sls_namespace", default="ibm-sls")
179
+ self.promptForString("IBM Data Reporter Operator (DRO) Namespace", "dro_namespace", default="redhat-marketplace")
159
180
 
181
+ @logMethodCall
160
182
  def selectLocalConfigDir(self) -> None:
161
183
  if self.localConfigDir is None:
162
184
  # You need to tell us where the configuration file can be found
163
185
  self.localConfigDir = self.promptForDir("Select Local configuration directory")
164
186
 
187
+ @logMethodCall
165
188
  def configGrafana(self) -> None:
166
- try:
167
- packagemanifestAPI = self.dynamicClient.resources.get(api_version="packages.operators.coreos.com/v1", kind="PackageManifest")
168
- packagemanifestAPI.get(name="grafana-operator", namespace="openshift-marketplace")
169
- if self.skipGrafanaInstall:
170
- self.setParam("grafana_action", "none")
171
- else:
172
- self.setParam("grafana_action", "install")
173
- except NotFoundError:
189
+ if self.architecture == "s390x":
190
+ # We are not supporting Grafana on s390x at the moment
174
191
  self.setParam("grafana_action", "none")
192
+ else:
193
+ try:
194
+ packagemanifestAPI = self.dynamicClient.resources.get(api_version="packages.operators.coreos.com/v1", kind="PackageManifest")
195
+ packagemanifestAPI.get(name="grafana-operator", namespace="openshift-marketplace")
196
+ if self.skipGrafanaInstall:
197
+ self.setParam("grafana_action", "none")
198
+ else:
199
+ self.setParam("grafana_action", "install")
200
+ except NotFoundError:
201
+ self.setParam("grafana_action", "none")
175
202
 
176
- if self.interactiveMode:
177
- self.printH1("Configure Grafana")
178
- if self.getParam("grafana_action") == "none":
179
- print_formatted_text("The Grafana operator package is not available in any catalogs on the target cluster, the installation of Grafana will be disabled")
180
- else:
181
- self.promptForString("Install namespace", "grafana_v5_namespace", default="grafana5")
182
- self.promptForString("Grafana storage size", "grafana_instance_storage_size", default="10Gi")
183
-
184
- def configMongoDb(self) -> None:
185
- self.printH1("Configure MongoDb")
186
- self.promptForString("Install namespace", "mongodb_namespace", default="mongoce")
203
+ if self.interactiveMode and self.showAdvancedOptions:
204
+ self.printH1("Configure Grafana")
205
+ if self.getParam("grafana_action") == "none":
206
+ print_formatted_text("The Grafana operator package is not available in any catalogs on the target cluster, the installation of Grafana will be disabled")
207
+ else:
208
+ self.promptForString("Install namespace", "grafana_v5_namespace", default="grafana5")
209
+ self.promptForString("Grafana storage size", "grafana_instance_storage_size", default="10Gi")
187
210
 
211
+ @logMethodCall
188
212
  def configSpecialCharacters(self):
189
- self.printH1("Configure special characters for userID and username")
190
- self.yesOrNo("Do you want to allow special characters for user IDs and usernames?", "mas_special_characters")
213
+ if self.showAdvancedOptions:
214
+ self.printH1("Configure special characters for userID and username")
215
+ self.printDescription([
216
+ "By default Maximo Application Suite will not allow special characters in usernames and userIDs, and this is the recommended setting. However, legacy Maximo products allowed this, so for maximum compatibilty when migrating from EAM 7 you can choose to enable this support."
217
+ ])
218
+ self.yesOrNo("Allow special characters for user IDs and usernames", "mas_special_characters")
191
219
 
220
+ @logMethodCall
192
221
  def configCP4D(self):
193
- if self.getParam("mas_catalog_version") in ["v9-240625-amd64", "v9-240730-amd64", "v9-240827-amd64", "v9-241003-amd64"]:
222
+ if self.getParam("mas_catalog_version") in ["v9-240625-amd64", "v9-240730-amd64", "v9-240827-amd64", "v9-241003-amd64", "v9-241107-amd64"]:
194
223
  logger.debug(f"Using automatic CP4D product version: {self.getParam('cpd_product_version')}")
195
224
  self.setParam("cpd_product_version", "4.8.0")
196
225
  elif self.getParam("cpd_product_version") == "":
@@ -205,37 +234,42 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
205
234
  logger.debug(f"Using user-provided (flags) CP4D product version: {self.getParam('cpd_product_version')}")
206
235
  self.deployCP4D = True
207
236
 
237
+ @logMethodCall
208
238
  def configSSOProperties(self):
209
- self.printH1("Single Sign-On (SSO)")
210
- self.printDescription([
211
- "Many aspects of Maximo Application Suite's Single Sign-On (SSO) can be customized:",
212
- " - Idle session automatic logout timer",
213
- " - Session, access token, and refresh token timeouts",
214
- " - Default identity provider (IDP), and seamless login",
215
- " - Brower cookie properties"
216
- ])
217
- if self.yesOrNo("Configure SSO properties"):
218
- self.promptForInt("Idle session logout timer (seconds)", "idle_timeout")
219
- self.promptForString("Session timeout (e.g. '12h' for 12 hours)", "idp_session_timeout", validator=TimeoutFormatValidator())
220
- self.promptForString("Access token timeout (e.g. '30m' for 30 minutes)", "access_token_timeout", validator=TimeoutFormatValidator())
221
- self.promptForString("Refresh token timeout (e.g. '12h' for 12 hours)", "refresh_token_timeout", validator=TimeoutFormatValidator())
222
- self.promptForString("Default Identity Provider", "default_idp")
223
-
224
- self.promptForString("SSO cookie name", "sso_cookie_name")
225
- self.yesOrNo("Enable seamless login", "seamless_login")
226
- self.yesOrNo("Allow default SSO cookie name", "allow_default_sso_cookie_name")
227
- self.yesOrNo("Use only custom cookie name", "use_only_custom_cookie_name")
228
- self.yesOrNo("Disable LDAP cookie", "disable_ldap_cookie")
229
- self.yesOrNo("Allow custom cache key", "allow_custom_cache_key")
230
-
239
+ if self.showAdvancedOptions:
240
+ self.printH1("Single Sign-On (SSO)")
241
+ self.printDescription([
242
+ "Many aspects of Maximo Application Suite's Single Sign-On (SSO) can be customized:",
243
+ " - Idle session automatic logout timer",
244
+ " - Session, access token, and refresh token timeouts",
245
+ " - Default identity provider (IDP), and seamless login",
246
+ " - Brower cookie properties"
247
+ ])
248
+ if self.yesOrNo("Configure SSO properties"):
249
+ self.promptForInt("Idle session logout timer (seconds)", "idle_timeout")
250
+ self.promptForString("Session timeout (e.g. '12h' for 12 hours)", "idp_session_timeout", validator=TimeoutFormatValidator())
251
+ self.promptForString("Access token timeout (e.g. '30m' for 30 minutes)", "access_token_timeout", validator=TimeoutFormatValidator())
252
+ self.promptForString("Refresh token timeout (e.g. '12h' for 12 hours)", "refresh_token_timeout", validator=TimeoutFormatValidator())
253
+ self.promptForString("Default Identity Provider", "default_idp")
254
+
255
+ self.promptForString("SSO cookie name", "sso_cookie_name")
256
+ self.yesOrNo("Enable seamless login", "seamless_login")
257
+ self.yesOrNo("Allow default SSO cookie name", "allow_default_sso_cookie_name")
258
+ self.yesOrNo("Use only custom cookie name", "use_only_custom_cookie_name")
259
+ self.yesOrNo("Disable LDAP cookie", "disable_ldap_cookie")
260
+ self.yesOrNo("Allow custom cache key", "allow_custom_cache_key")
261
+
262
+ @logMethodCall
231
263
  def configGuidedTour(self):
232
- self.printH1("Enable Guided Tour")
233
- self.printDescription([
234
- "By default, Maximo Application Suite is configured with guided tour, you can disable this if it not required"
235
- ])
236
- if not self.yesOrNo("Enable Guided Tour"):
237
- self.setParam("mas_enable_walkme", "false")
264
+ if self.showAdvancedOptions:
265
+ self.printH1("Enable Guided Tour")
266
+ self.printDescription([
267
+ "By default, Maximo Application Suite is configured with guided tour, you can disable this if it not required"
268
+ ])
269
+ if not self.yesOrNo("Enable Guided Tour"):
270
+ self.setParam("mas_enable_walkme", "false")
238
271
 
272
+ @logMethodCall
239
273
  def configMAS(self):
240
274
  self.printH1("Configure MAS Instance")
241
275
  self.printDescription([
@@ -268,13 +302,18 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
268
302
  self.configSpecialCharacters()
269
303
  self.configGuidedTour()
270
304
 
305
+ @logMethodCall
271
306
  def configCATrust(self) -> None:
272
- self.printH1("Certificate Authority Trust")
273
- self.printDescription([
274
- "By default, Maximo Application Suite is configured to trust well-known certificate authoritories, you can disable this so that it will only trust the CAs that you explicitly define"
275
- ])
276
- self.yesOrNo("Trust default CAs", "mas_trust_default_cas")
307
+ if self.showAdvancedOptions:
308
+ self.printH1("Certificate Authority Trust")
309
+ self.printDescription([
310
+ "By default, Maximo Application Suite is configured to trust well-known certificate authoritories, you can disable this so that it will only trust the CAs that you explicitly define"
311
+ ])
312
+ self.yesOrNo("Trust default CAs", "mas_trust_default_cas")
313
+ else:
314
+ self.setParam("mas_trust_default_cas", True)
277
315
 
316
+ @logMethodCall
278
317
  def configOperationMode(self):
279
318
  self.printH1("Configure Operational Mode")
280
319
  self.printDescription([
@@ -287,63 +326,68 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
287
326
  ])
288
327
  self.operationalMode = self.promptForInt("Operational Mode", default=1)
289
328
 
329
+ @logMethodCall
290
330
  def configAnnotations(self):
291
331
  if self.operationalMode == 2:
292
332
  self.setParam("mas_annotations", "mas.ibm.com/operationalMode=nonproduction")
293
333
 
334
+ @logMethodCall
294
335
  def configSNO(self):
295
336
  if self.isSNO():
296
337
  self.setParam("mongodb_replicas", "1")
297
338
  self.setParam("mongodb_cpu_requests", "500m")
298
339
  self.setParam("mas_app_settings_aio_flag", "false")
299
340
 
341
+ @logMethodCall
300
342
  def configDNSAndCerts(self):
301
- self.printH1("Cluster Ingress Secret Override")
302
- self.printDescription([
303
- "In most OpenShift clusters the installation is able to automatically locate the default ingress certificate, however in some configurations it is necessary to manually configure the name of the secret",
304
- "Unless you see an error during the ocp-verify stage indicating that the secret can not be determined you do not need to set this and can leave the response empty"
305
- ])
306
- self.promptForString("Cluster ingress certificate secret name", "ocp_ingress_tls_secret_name", default="")
307
-
308
- self.printH1("Configure Domain & Certificate Management")
309
- configureDomainAndCertMgmt = self.yesOrNo('Configure domain & certificate management')
310
- if configureDomainAndCertMgmt:
311
- configureDomain = self.yesOrNo('Configure custom domain')
312
- if configureDomain:
313
- self.promptForString("MAS top-level domain", "mas_domain")
314
- self.printDescription([
315
- "",
316
- "DNS Integrations:",
317
- " 1. Cloudflare",
318
- " 2. IBM Cloud Internet Services",
319
- " 3. AWS Route 53",
320
- " 4. None (I will set up DNS myself)"
321
- ])
322
-
323
- dnsProvider = self.promptForInt("DNS Provider")
324
-
325
- if dnsProvider == 1:
326
- self.configDNSAndCertsCloudflare()
327
- elif dnsProvider == 2:
328
- self.configDNSAndCertsCIS()
329
- elif dnsProvider == 3:
330
- self.configDNSAndCertsRoute53()
331
- elif dnsProvider == 4:
332
- # Use MAS default self-signed cluster issuer with a custom domain
343
+ if self.showAdvancedOptions:
344
+ self.printH1("Cluster Ingress Secret Override")
345
+ self.printDescription([
346
+ "In most OpenShift clusters the installation is able to automatically locate the default ingress certificate, however in some configurations it is necessary to manually configure the name of the secret",
347
+ "Unless you see an error during the ocp-verify stage indicating that the secret can not be determined you do not need to set this and can leave the response empty"
348
+ ])
349
+ self.promptForString("Cluster ingress certificate secret name", "ocp_ingress_tls_secret_name", default="")
350
+
351
+ self.printH1("Configure Domain & Certificate Management")
352
+ configureDomainAndCertMgmt = self.yesOrNo('Configure domain & certificate management')
353
+ if configureDomainAndCertMgmt:
354
+ configureDomain = self.yesOrNo('Configure custom domain')
355
+ if configureDomain:
356
+ self.promptForString("MAS top-level domain", "mas_domain")
357
+ self.printDescription([
358
+ "",
359
+ "DNS Integrations:",
360
+ " 1. Cloudflare",
361
+ " 2. IBM Cloud Internet Services",
362
+ " 3. AWS Route 53",
363
+ " 4. None (I will set up DNS myself)"
364
+ ])
365
+
366
+ dnsProvider = self.promptForInt("DNS Provider")
367
+
368
+ if dnsProvider == 1:
369
+ self.configDNSAndCertsCloudflare()
370
+ elif dnsProvider == 2:
371
+ self.configDNSAndCertsCIS()
372
+ elif dnsProvider == 3:
373
+ self.configDNSAndCertsRoute53()
374
+ elif dnsProvider == 4:
375
+ # Use MAS default self-signed cluster issuer with a custom domain
376
+ self.setParam("dns_provider", "")
377
+ self.setParam("mas_cluster_issuer", "")
378
+ else:
379
+ # Use MAS default self-signed cluster issuer with the default domain
333
380
  self.setParam("dns_provider", "")
381
+ self.setParam("mas_domain", "")
334
382
  self.setParam("mas_cluster_issuer", "")
335
- else:
336
- # Use MAS default self-signed cluster issuer with the default domain
337
- self.setParam("dns_provider", "")
338
- self.setParam("mas_domain", "")
339
- self.setParam("mas_cluster_issuer", "")
340
- self.manualCerts = self.yesOrNo("Configure manual certificates")
341
- self.setParam("mas_manual_cert_mgmt", self.manualCerts)
342
- if self.getParam("mas_manual_cert_mgmt"):
343
- self.manualCertsDir = self.promptForDir("Enter the path containing the manual certificates", mustExist=True)
344
- else:
345
- self.manualCertsDir = None
383
+ self.manualCerts = self.yesOrNo("Configure manual certificates")
384
+ self.setParam("mas_manual_cert_mgmt", self.manualCerts)
385
+ if self.getParam("mas_manual_cert_mgmt"):
386
+ self.manualCertsDir = self.promptForDir("Enter the path containing the manual certificates", mustExist=True)
387
+ else:
388
+ self.manualCertsDir = None
346
389
 
390
+ @logMethodCall
347
391
  def configDNSAndCertsCloudflare(self):
348
392
  # User has chosen to set up DNS integration with Cloudflare
349
393
  self.setParam("dns_provider", "cloudflare")
@@ -366,6 +410,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
366
410
  ]
367
411
  self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
368
412
 
413
+ @logMethodCall
369
414
  def configDNSAndCertsCIS(self):
370
415
  self.setParam("dns_provider", "cis")
371
416
  self.promptForString("CIS e-mail", "cis_email")
@@ -387,6 +432,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
387
432
  ]
388
433
  self.setParam("mas_cluster_issuer", certIssuerOptions[certIssuer - 1])
389
434
 
435
+ @logMethodCall
390
436
  def configDNSAndCertsRoute53(self):
391
437
  self.setParam("dns_provider", "route53")
392
438
  self.printDescription([
@@ -411,6 +457,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
411
457
 
412
458
  self.setParam("mas_cluster_issuer", f"{self.getParam('mas_instance_id')}-route53-le-prod")
413
459
 
460
+ @logMethodCall
414
461
  def configApps(self):
415
462
  self.printH1("Application Selection")
416
463
  self.installIoT = self.yesOrNo("Install IoT")
@@ -458,6 +505,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
458
505
  if self.installAiBroker:
459
506
  self.configAppChannel("aibroker")
460
507
 
508
+ @logMethodCall
461
509
  def configAppChannel(self, appId):
462
510
  versions = self.getCompatibleVersions(self.params["mas_channel"], appId)
463
511
  if len(versions) == 0:
@@ -465,6 +513,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
465
513
  else:
466
514
  self.params[f"mas_app_channel_{appId}"] = versions[0]
467
515
 
516
+ @logMethodCall
468
517
  def configStorageClasses(self):
469
518
  self.printH1("Configure Storage Class Usage")
470
519
  self.printDescription([
@@ -540,11 +589,13 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
540
589
  self.pipelineStorageClass = self.getParam("storage_class_rwx")
541
590
  self.pipelineStorageAccessMode = "ReadWriteMany"
542
591
 
592
+ @logMethodCall
543
593
  def setIoTStorageClasses(self) -> None:
544
594
  if self.installIoT:
545
595
  self.setParam("mas_app_settings_iot_fpl_pvc_storage_class", self.getParam("storage_class_rwo"))
546
596
  self.setParam("mas_app_settings_iot_mqttbroker_pvc_storage_class", self.getParam("storage_class_rwo"))
547
597
 
598
+ @logMethodCall
548
599
  def optimizerSettings(self) -> None:
549
600
  if self.installOptimizer:
550
601
  self.printH1("Configure Maximo Optimizer")
@@ -552,8 +603,9 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
552
603
 
553
604
  self.promptForString("Plan [full/limited]", "mas_app_plan_optimizer", default="full", validator=OptimizerInstallPlanValidator())
554
605
 
606
+ @logMethodCall
555
607
  def predictSettings(self) -> None:
556
- if self.installPredict:
608
+ if self.showAdvancedOptions and self.installPredict:
557
609
  self.printH1("Configure Maximo Predict")
558
610
  self.printDescription([
559
611
  "Predict application supports integration with IBM SPSS and Watson Openscale which are optional services installed on top of IBM Cloud Pak for Data",
@@ -563,6 +615,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
563
615
  self.yesOrNo("Install IBM SPSS Statistics", "cpd_install_spss")
564
616
  self.yesOrNo("Install Watson OpenScale", "cpd_install_openscale")
565
617
 
618
+ @logMethodCall
566
619
  def assistSettings(self) -> None:
567
620
  if self.installAssist:
568
621
  self.printH1("Configure Maximo Assist")
@@ -575,10 +628,41 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
575
628
  self.promptForString("IBM Cloud API Key", "ibmcloud_apikey", isPassword=True)
576
629
  self.promptForString("IBM Cloud Resource Group", "ibmcos_resourcegroup")
577
630
 
578
- def interactiveMode(self) -> None:
631
+ @logMethodCall
632
+ def chooseInstallFlavour(self) -> None:
633
+ self.printH1("Choose Install Mode")
634
+ self.printDescription([
635
+ "There are two flavours of the interactive install to choose from: <u>Simplified</u> and <u>Advanced</u>. The simplified option will present fewer dialogs, but you lose the ability to configure the following aspects of the installation:",
636
+ " - Configure installation namespaces",
637
+ " - Provide pod templates",
638
+ " - Configure Single Sign-On (SSO) settings"
639
+ " - Configure whether to trust well-known certificate authorities by default (defaults to enabled)",
640
+ " - Configure whether the Guided Tour feature is enabled (defaults to enabled)",
641
+ " - Configure whether special characters are allowed in usernames and userids (defaults to disabled)",
642
+ " - Configure a custom domain, DNS integrations, and manual certificates",
643
+ " - Customize Maximo Manage database settings (schema, tablespace, indexspace)",
644
+ " - Customize Maximo Manage server bundle configuration (defaults to \"all\" configuration)",
645
+ " - Enable optional Maximo Manage integration Cognos Analytics and Watson Studio Local",
646
+ " - Enable optional Maximo Predict integration with SPSS and Watson OpenScale",
647
+ " - Enable optional IBM Turbonomic integration",
648
+ " - Customize Db2 node affinity and tolerations, memory, cpu, and storage settings (when using the IBM Db2 Universal Operator)",
649
+ " - Choose alternative Apache Kafka providers (default to Strimzi)",
650
+ " - Customize Grafana storage settings"
651
+ ])
652
+ self.showAdvancedOptions = self.yesOrNo("Show advanced installation options")
653
+
654
+ @logMethodCall
655
+ def interactiveMode(self, simplified: bool, advanced: bool) -> None:
579
656
  # Interactive mode
580
657
  self.interactiveMode = True
581
658
 
659
+ if simplified:
660
+ self.showAdvancedOptions = False
661
+ elif advanced:
662
+ self.showAdvancedOptions = True
663
+ else:
664
+ self.chooseInstallFlavour()
665
+
582
666
  # Catalog
583
667
  self.configCatalog()
584
668
  if not self.devMode:
@@ -608,7 +692,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
608
692
  self.aibrokerSettings()
609
693
 
610
694
  # Dependencies
611
- self.configMongoDb() # Will only do anything if IoT or Manage have been selected for install
695
+ self.configMongoDb()
612
696
  self.configDb2()
613
697
  self.configKafka() # Will only do anything if IoT has been selected for install
614
698
 
@@ -618,11 +702,15 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
618
702
  # TODO: Support ECK integration via the interactive install mode
619
703
  # TODO: Support MAS superuser username/password via the interactive install mode
620
704
 
705
+ @logMethodCall
621
706
  def nonInteractiveMode(self) -> None:
622
- # Non-interactive mode
623
707
  self.interactiveMode = False
624
708
 
625
709
  # Set defaults
710
+ # ---------------------------------------------------------------------
711
+ # Unless a config file named "mongodb-system.yaml" is provided via the additional configs mechanism we will be installing a new MongoDb instance
712
+ self.setParam("mongodb_action", "install")
713
+
626
714
  self.storageClassProvider = "custom"
627
715
  self.installAssist = False
628
716
  self.installIoT = False
@@ -649,162 +737,6 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
649
737
 
650
738
  self.configGrafana()
651
739
 
652
- requiredParams = [
653
- # MAS
654
- "mas_catalog_version",
655
- "mas_channel",
656
- "mas_instance_id",
657
- "mas_workspace_id",
658
- "mas_workspace_name",
659
- # Storage classes
660
- "storage_class_rwo",
661
- "storage_class_rwx",
662
- # Entitlement
663
- "ibm_entitlement_key",
664
- # DRO
665
- "uds_contact_email",
666
- "uds_contact_firstname",
667
- "uds_contact_lastname"
668
- ]
669
- optionalParams = [
670
- # Pipeline
671
- "image_pull_policy",
672
- # OpenShift
673
- "ocp_ingress_tls_secret_name",
674
- # MAS
675
- "mas_catalog_digest",
676
- "mas_superuser_username",
677
- "mas_superuser_password",
678
- "mas_trust_default_cas",
679
- "mas_app_settings_server_bundles_size",
680
- "mas_app_settings_default_jms",
681
- "mas_app_settings_persistent_volumes_flag",
682
- "mas_app_settings_demodata",
683
- "mas_app_settings_customization_archive_name",
684
- "mas_app_settings_customization_archive_url",
685
- "mas_app_settings_customization_archive_username",
686
- "mas_app_settings_customization_archive_password",
687
- "mas_app_settings_tablespace",
688
- "mas_app_settings_indexspace",
689
- "mas_app_settings_db2_schema",
690
- "mas_app_settings_crypto_key",
691
- "mas_app_settings_cryptox_key",
692
- "mas_app_settings_old_crypto_key",
693
- "mas_app_settings_old_cryptox_key",
694
- "mas_app_settings_override_encryption_secrets_flag",
695
- "mas_app_settings_base_lang",
696
- "mas_app_settings_secondary_langs",
697
- "mas_app_settings_server_timezone",
698
- "mas_appws_bindings_jdbc_manage",
699
- "mas_appws_components",
700
- "mas_domain",
701
- # DNS Providers
702
- # TODO: Add CloudFlare and Route53 support
703
- "dns_provider",
704
- "cis_email",
705
- "cis_apikey",
706
- "cis_crn",
707
- "cis_subdomain",
708
- # DRO
709
- "dro_namespace",
710
- # MongoDb
711
- "mongodb_namespace",
712
- # Db2
713
- "db2_action_system",
714
- "db2_action_manage",
715
- "db2_type",
716
- "db2_timezone",
717
- "db2_namespace",
718
- "db2_channel",
719
- "db2_affinity_key",
720
- "db2_affinity_value",
721
- "db2_tolerate_key",
722
- "db2_tolerate_value",
723
- "db2_tolerate_effect",
724
- "db2_cpu_requests",
725
- "db2_cpu_limits",
726
- "db2_memory_requests",
727
- "db2_memory_limits",
728
- "db2_backup_storage_size",
729
- "db2_data_storage_size",
730
- "db2_logs_storage_size",
731
- "db2_meta_storage_size",
732
- "db2_temp_storage_size",
733
- # CP4D
734
- "cpd_product_version",
735
- "cpd_install_cognos",
736
- "cpd_install_openscale",
737
- "cpd_install_spss",
738
- # Kafka
739
- "kafka_namespace",
740
- "kafka_version",
741
- "aws_msk_instance_type",
742
- "aws_msk_instance_number",
743
- "aws_msk_volume_size",
744
- "aws_msk_cidr_az1",
745
- "aws_msk_cidr_az2",
746
- "aws_msk_cidr_az3",
747
- "aws_msk_egress_cidr",
748
- "aws_msk_ingress_cidr",
749
- "eventstreams_resource_group",
750
- "eventstreams_instance_name",
751
- "eventstreams_instance_location",
752
- # COS
753
- "cos_type",
754
- "ibmcos_resourcegroup",
755
- # ECK
756
- "eck_action",
757
- "eck_enable_logstash",
758
- "eck_remote_es_hosts",
759
- "eck_remote_es_username",
760
- "eck_remote_es_password",
761
- # Turbonomic
762
- "turbonomic_target_name",
763
- "turbonomic_server_url",
764
- "turbonomic_server_version",
765
- "turbonomic_username",
766
- "turbonomic_password",
767
- # Cloud Providers
768
- "ibmcloud_apikey",
769
- "aws_region",
770
- "aws_access_key_id",
771
- "secret_access_key",
772
- "aws_vpc_id",
773
- # Dev Mode
774
- "artifactory_username",
775
- "artifactory_token",
776
- # TODO: The way arcgis has been implemented needs to be fixed
777
- "install_arcgis",
778
- "mas_arcgis_channel",
779
- # Guided Tour
780
- "mas_enable_walkme",
781
- # Aibroker
782
- "mas_aibroker_storage_provider",
783
- "mas_aibroker_storage_accesskey",
784
- "mas_aibroker_storage_secretkey",
785
- "mas_aibroker_storage_host",
786
- "mas_aibroker_storage_port",
787
- "mas_aibroker_storage_ssl",
788
- "mas_aibroker_storage_region",
789
- "mas_aibroker_storage_pipelines_bucket",
790
- "mas_aibroker_storage_tenants_bucket",
791
- "mas_aibroker_storage_templates_bucket",
792
- "mas_aibroker_tenant_name",
793
- "mas_aibroker_watsonxai_apikey",
794
- "mas_aibroker_watsonxai_url",
795
- "mas_aibroker_watsonxai_project_id",
796
- "mas_aibroker_watsonx_action",
797
- "mas_aibroker_db_host",
798
- "mas_aibroker_db_port",
799
- "mas_aibroker_db_user",
800
- "mas_aibroker_db_database",
801
- "mas_aibroker_db_secret_name",
802
- "mas_aibroker_db_secret_key",
803
- "mas_aibroker_db_secret_value",
804
- # Special chars
805
- "mas_special_characters"
806
- ]
807
-
808
740
  for key, value in vars(self.args).items():
809
741
  # These fields we just pass straight through to the parameters and fail if they are not set
810
742
  if key in requiredParams:
@@ -841,6 +773,10 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
841
773
 
842
774
  elif key == "additional_configs":
843
775
  self.localConfigDir = value
776
+ # If there is a file named mongodb-system.yaml we will use this as a BYO MongoDB datasource
777
+ if path.exists(path.join(self.localConfigDir, "mongodb-system.yaml")):
778
+ self.setParam("mongodb_action", "byo")
779
+ self.setParam("sls_mongodb_cfg_file", "/workspace/additional-configs/mongodb-system.yaml")
844
780
 
845
781
  elif key == "pod_templates":
846
782
  # For the named configurations we will convert into the path
@@ -928,7 +864,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
928
864
  self.fatalError(f"Unsupported format for {key} ({value}). Expected string:int:int:boolean")
929
865
 
930
866
  # Arguments that we don't need to do anything with
931
- elif key in ["accept_license", "dev_mode", "skip_pre_check", "skip_grafana_install", "no_confirm", "no_wait_for_pvc", "help"]:
867
+ elif key in ["accept_license", "dev_mode", "skip_pre_check", "skip_grafana_install", "no_confirm", "no_wait_for_pvc", "help", "advanced", "simplified"]:
932
868
  pass
933
869
 
934
870
  elif key == "manual_certificates":
@@ -949,6 +885,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
949
885
  self.validateCatalogSource()
950
886
  self.licensePrompt()
951
887
 
888
+ @logMethodCall
952
889
  def install(self, argv):
953
890
  """
954
891
  Install MAS instance
@@ -975,141 +912,24 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
975
912
  if args.skip_pre_check:
976
913
  self.setParam("skip_pre_check", "true")
977
914
 
978
- self.installOptions = [
979
- {
980
- "#": 1,
981
- "catalog": "v9-241003-amd64",
982
- "release": "9.0.x",
983
- "core": "9.0.3",
984
- "assist": "9.0.2",
985
- "iot": "9.0.3",
986
- "manage": "9.0.3",
987
- "monitor": "9.0.3",
988
- "optimizer": "9.0.3",
989
- "predict": "9.0.2",
990
- "inspection": "9.0.3",
991
- "aibroker": "9.0.2"
992
- },
993
- {
994
- "#": 2,
995
- "catalog": "v9-241003-amd64",
996
- "release": "8.11.x",
997
- "core": "8.11.15",
998
- "assist": "8.8.6",
999
- "iot": "8.8.13",
1000
- "manage": "8.7.12",
1001
- "monitor": "8.11.11",
1002
- "optimizer": "8.5.9",
1003
- "predict": "8.9.5",
1004
- "inspection": "8.9.6"
1005
- },
1006
- {
1007
- "#": 3,
1008
- "catalog": "v9-241003-amd64",
1009
- "release": "8.10.x",
1010
- "core": "8.10.18",
1011
- "assist": "8.7.7",
1012
- "iot": "8.7.17",
1013
- "manage": "8.6.18",
1014
- "monitor": "8.10.14",
1015
- "optimizer": "8.4.10",
1016
- "predict": "8.8.3",
1017
- "inspection": "8.8.4"
1018
- },
1019
- {
1020
- "#": 4,
1021
- "catalog": "v9-240827-amd64",
1022
- "release": "9.0.x",
1023
- "core": "9.0.2",
1024
- "assist": "9.0.2",
1025
- "iot": "9.0.2",
1026
- "manage": "9.0.2",
1027
- "monitor": "9.0.2",
1028
- "optimizer": "9.0.2",
1029
- "predict": "9.0.1",
1030
- "inspection": "9.0.2"
1031
- },
1032
- {
1033
- "#": 5,
1034
- "catalog": "v9-240827-amd64",
1035
- "release": "8.11.x",
1036
- "core": "8.11.14",
1037
- "assist": "8.8.6",
1038
- "iot": "8.8.12",
1039
- "manage": "8.7.11",
1040
- "monitor": "8.11.10",
1041
- "optimizer": "8.5.8",
1042
- "predict": "8.9.3",
1043
- "inspection": "8.9.5"
1044
- },
1045
- {
1046
- "#": 6,
1047
- "catalog": "v9-240827-amd64",
1048
- "release": "8.10.x",
1049
- "core": "8.10.17",
1050
- "assist": "8.7.7",
1051
- "iot": "8.7.16",
1052
- "manage": "8.6.17",
1053
- "monitor": "8.10.13",
1054
- "optimizer": "8.4.9",
1055
- "predict": "8.8.3",
1056
- "inspection": "8.8.4"
1057
- },
1058
- {
1059
- "#": 7,
1060
- "catalog": "v9-240730-amd64",
1061
- "release": "9.0.x",
1062
- "core": "9.0.1",
1063
- "assist": "9.0.1",
1064
- "iot": "9.0.1",
1065
- "manage": "9.0.1",
1066
- "monitor": "9.0.1",
1067
- "optimizer": "9.0.1",
1068
- "predict": "9.0.0",
1069
- "inspection": "9.0.0"
1070
- },
1071
- {
1072
- "#": 8,
1073
- "catalog": "v9-240730-amd64",
1074
- "release": "8.11.x",
1075
- "core": "8.11.13",
1076
- "assist": "8.8.5",
1077
- "iot": "8.8.11",
1078
- "manage": "8.7.10",
1079
- "monitor": "8.11.9",
1080
- "optimizer": "8.5.7",
1081
- "predict": "8.9.3",
1082
- "inspection": "8.9.4"
1083
- },
1084
- {
1085
- "#": 9,
1086
- "catalog": "v9-240730-amd64",
1087
- "release": "8.10.x",
1088
- "core": "8.10.16",
1089
- "assist": "8.7.6",
1090
- "iot": "8.7.15",
1091
- "manage": "8.6.16",
1092
- "monitor": "8.10.12",
1093
- "optimizer": "8.4.8",
1094
- "predict": "8.8.3",
1095
- "inspection": "8.8.4"
1096
- }
1097
- ]
1098
-
1099
915
  if instanceId is None:
1100
916
  self.printH1("Set Target OpenShift Cluster")
1101
917
  # Connect to the target cluster
1102
918
  self.connect()
1103
919
  else:
1104
920
  logger.debug("MAS instance ID is set, so we assume already connected to the desired OCP")
921
+ self.lookupTargetArchitecture()
1105
922
 
1106
923
  if self.dynamicClient is None:
1107
924
  print_formatted_text(HTML("<Red>Error: The Kubernetes dynamic Client is not available. See log file for details</Red>"))
1108
925
  exit(1)
1109
926
 
927
+ # Configure the installOptions for the appropriate architecture
928
+ self.installOptions = catalogChoices[self.architecture]
929
+
1110
930
  # Basic settings before the user provides any input
1111
931
  self.configICR()
1112
- self.configCertManager()
932
+ self.configCertManager() # TODO: I think this is redundant, we should look to remove this and the appropriate params in the install pipeline
1113
933
  self.deployCP4D = False
1114
934
 
1115
935
  # UDS install has not been supported since the January 2024 catalog update
@@ -1117,7 +937,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
1117
937
 
1118
938
  # User must either provide the configuration via numerous command line arguments, or the interactive prompts
1119
939
  if instanceId is None:
1120
- self.interactiveMode()
940
+ self.interactiveMode(simplified=args.simplified, advanced=args.advanced)
1121
941
  else:
1122
942
  self.nonInteractiveMode()
1123
943
 
@@ -1211,6 +1031,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
1211
1031
  h.stop_and_persist(symbol=self.failureIcon, text=f"Failed to submit PipelineRun for {self.getParam('mas_instance_id')} install, see log file for details")
1212
1032
  print()
1213
1033
 
1034
+ @logMethodCall
1214
1035
  def setupApprovals(self, namespace: str) -> None:
1215
1036
  """
1216
1037
  Ensure the supported approval configmaps are in the expected state for the start of the run: