mas-cli 10.3.2__py3-none-any.whl → 10.4.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 mas-cli might be problematic. Click here for more details.
- mas/cli/__init__.py +1 -1
- mas/cli/cli.py +36 -1
- mas/cli/install/app.py +92 -2
- mas/cli/install/argParser.py +63 -2
- mas/cli/templates/ibm-mas-tekton.yaml +164 -631
- mas/cli/validators.py +10 -0
- {mas_cli-10.3.2.data → mas_cli-10.4.1.data}/scripts/mas-cli +1 -1
- {mas_cli-10.3.2.dist-info → mas_cli-10.4.1.dist-info}/METADATA +1 -1
- {mas_cli-10.3.2.dist-info → mas_cli-10.4.1.dist-info}/RECORD +11 -11
- {mas_cli-10.3.2.dist-info → mas_cli-10.4.1.dist-info}/WHEEL +1 -1
- {mas_cli-10.3.2.dist-info → mas_cli-10.4.1.dist-info}/top_level.txt +0 -0
mas/cli/__init__.py
CHANGED
mas/cli/cli.py
CHANGED
|
@@ -20,6 +20,7 @@ from sys import exit
|
|
|
20
20
|
from openshift import dynamic
|
|
21
21
|
from kubernetes import config
|
|
22
22
|
from kubernetes.client import api_client
|
|
23
|
+
from openshift.dynamic.exceptions import NotFoundError
|
|
23
24
|
|
|
24
25
|
from prompt_toolkit import prompt, print_formatted_text, HTML
|
|
25
26
|
|
|
@@ -198,8 +199,10 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
198
199
|
# We are already connected to a cluster, but prompt the user if they want to use this connection
|
|
199
200
|
continueWithExistingCluster = prompt(HTML('<Yellow>Proceed with this cluster?</Yellow> '), validator=YesNoValidator(), validate_while_typing=False)
|
|
200
201
|
promptForNewServer = continueWithExistingCluster in ["n", "no"]
|
|
201
|
-
except Exception:
|
|
202
|
+
except Exception as e:
|
|
202
203
|
# We are already connected to a cluster, but the connection is not valid so prompt for connection details
|
|
204
|
+
logger.debug("Failed looking up OpenShift Console route to verify connection")
|
|
205
|
+
logger.exception(e, stack_info=True)
|
|
203
206
|
promptForNewServer = True
|
|
204
207
|
else:
|
|
205
208
|
# We are not already connected to any cluster, so prompt for connection details
|
|
@@ -214,3 +217,35 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
214
217
|
if self._dynClient is None:
|
|
215
218
|
print_formatted_text(HTML("<Red>Unable to connect to cluster. See log file for details</Red>"))
|
|
216
219
|
exit(1)
|
|
220
|
+
|
|
221
|
+
def initializeApprovalConfigMap(self, namespace: str, id: str, key: str=None, maxRetries: int=100, delay: int=300, ignoreFailure: bool=True) -> None:
|
|
222
|
+
"""
|
|
223
|
+
Set key = None if you don't want approval workflow enabled
|
|
224
|
+
"""
|
|
225
|
+
cmAPI = self.dynamicClient.resources.get(api_version="v1", kind="ConfigMap")
|
|
226
|
+
configMap = {
|
|
227
|
+
"apiVersion": "v1",
|
|
228
|
+
"kind": "ConfigMap",
|
|
229
|
+
"metadata": {
|
|
230
|
+
"name": f"approval-{id}",
|
|
231
|
+
"namespace": namespace
|
|
232
|
+
},
|
|
233
|
+
"data": {
|
|
234
|
+
"MAX_RETRIES": str(maxRetries),
|
|
235
|
+
"DELAY": str(delay),
|
|
236
|
+
"IGNORE_FAILURE": str(ignoreFailure),
|
|
237
|
+
"CONFIGMAP_KEY": key,
|
|
238
|
+
key: ""
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# Delete any existing configmap and create a new one
|
|
243
|
+
try:
|
|
244
|
+
logger.debug(f"Deleting any existing approval workflow configmap for {id}")
|
|
245
|
+
cmAPI.delete(name=f"approval-{id}", namespace=namespace)
|
|
246
|
+
except NotFoundError:
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
if key is not None:
|
|
250
|
+
logger.debug(f"Enabling approval workflow for {id} using {key} with {maxRetries} max retries on a {delay}s delay ({'ignoring failures' if ignoreFailure else 'abort on failure'})")
|
|
251
|
+
cmAPI.create(body=configMap, namespace=namespace)
|
mas/cli/install/app.py
CHANGED
|
@@ -33,12 +33,20 @@ from mas.cli.validators import (
|
|
|
33
33
|
InstanceIDFormatValidator,
|
|
34
34
|
WorkspaceIDFormatValidator,
|
|
35
35
|
WorkspaceNameFormatValidator,
|
|
36
|
+
TimeoutFormatValidator,
|
|
36
37
|
StorageClassValidator,
|
|
37
38
|
OptimizerInstallPlanValidator
|
|
38
39
|
)
|
|
39
40
|
|
|
40
41
|
from mas.devops.ocp import createNamespace, getStorageClass, getStorageClasses
|
|
41
|
-
from mas.devops.tekton import
|
|
42
|
+
from mas.devops.tekton import (
|
|
43
|
+
installOpenShiftPipelines,
|
|
44
|
+
updateTektonDefinitions,
|
|
45
|
+
preparePipelinesNamespace,
|
|
46
|
+
prepareInstallSecrets,
|
|
47
|
+
testCLI,
|
|
48
|
+
launchInstallPipeline
|
|
49
|
+
)
|
|
42
50
|
|
|
43
51
|
logger = logging.getLogger(__name__)
|
|
44
52
|
|
|
@@ -51,6 +59,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
51
59
|
catalogDisplayName = catalog.spec.displayName
|
|
52
60
|
|
|
53
61
|
m = re.match(r".+(?P<catalogId>v[89]-(?P<catalogVersion>[0-9]+)-amd64)", catalogDisplayName)
|
|
62
|
+
print(f"m: {m}")
|
|
54
63
|
if m:
|
|
55
64
|
# catalogId = v8-yymmdd-amd64
|
|
56
65
|
# catalogVersion = yymmdd
|
|
@@ -187,6 +196,23 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
187
196
|
|
|
188
197
|
self.deployCP4D = True
|
|
189
198
|
|
|
199
|
+
def configSSOProperties(self):
|
|
200
|
+
self.printH1("Single Sign-On (SSO)")
|
|
201
|
+
self.printDescription([
|
|
202
|
+
"Many aspects of Maximo Application Suite's Single Sign-On (SSO) can be customized:",
|
|
203
|
+
" - Idle session automatic logout timer",
|
|
204
|
+
" - Session, access token, and refresh token timeouts",
|
|
205
|
+
" - Default identity provider (IDP), and seamless login"
|
|
206
|
+
])
|
|
207
|
+
sso_response = self.yesOrNo("Configure SSO properties")
|
|
208
|
+
if sso_response:
|
|
209
|
+
self.promptForInt("Enter the idle timeout (in seconds)", "idle_timeout", default=1800)
|
|
210
|
+
self.promptForString("Enter the IDP session timeout (e.g., '12h' for 12 hours)", "idp_session_timeout", validator=TimeoutFormatValidator(), default="12h")
|
|
211
|
+
self.promptForString("Enter the access token timeout (e.g., '30m' for 30 minutes)", "access_token_timeout", validator=TimeoutFormatValidator(), default="30m")
|
|
212
|
+
self.promptForString("Enter the refresh token timeout (e.g., '12h' for 12 hours)", "refresh_token_timeout", validator=TimeoutFormatValidator(), default="12h")
|
|
213
|
+
self.promptForString("Enter the default Identity Provider (IDP)", "default_idp", default="local")
|
|
214
|
+
self.yesOrNo("Enable seamless login?", param="seamless_login")
|
|
215
|
+
|
|
190
216
|
def configMAS(self):
|
|
191
217
|
self.printH1("Configure MAS Instance")
|
|
192
218
|
self.printDescription([
|
|
@@ -215,6 +241,7 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
215
241
|
self.configOperationMode()
|
|
216
242
|
self.configCATrust()
|
|
217
243
|
self.configDNSAndCerts()
|
|
244
|
+
self.configSSOProperties()
|
|
218
245
|
|
|
219
246
|
def configCATrust(self) -> None:
|
|
220
247
|
self.printH1("Certificate Authority Trust")
|
|
@@ -568,22 +595,38 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
568
595
|
self.db2SetAffinity = False
|
|
569
596
|
self.db2SetTolerations = False
|
|
570
597
|
|
|
598
|
+
self.approvals = {
|
|
599
|
+
"approval_core": {"id": "suite-verify"}, # After Core Platform verification has completed
|
|
600
|
+
"approval_assist": {"id": "app-cfg-assist"}, # After Assist workspace has been configured
|
|
601
|
+
"approval_iot": {"id": "app-cfg-iot"}, # After IoT workspace has been configured
|
|
602
|
+
"approval_manage": {"id": "app-cfg-manage"}, # After Manage workspace has been configured
|
|
603
|
+
"approval_monitor": {"id": "app-cfg-monitor"}, # After Monitor workspace has been configured
|
|
604
|
+
"approval_optimizer": {"id": "app-cfg-optimizer"}, # After Optimizer workspace has been configured
|
|
605
|
+
"approval_predict": {"id": "app-cfg-predict"}, # After Predict workspace has been configured
|
|
606
|
+
"approval_visualinspection": {"id": "app-cfg-visualinspection"} # After Visual Inspection workspace has been configured
|
|
607
|
+
}
|
|
608
|
+
|
|
571
609
|
self.configGrafana()
|
|
572
610
|
|
|
573
611
|
requiredParams = [
|
|
612
|
+
# MAS
|
|
574
613
|
"mas_catalog_version",
|
|
575
614
|
"mas_channel",
|
|
576
615
|
"mas_instance_id",
|
|
577
616
|
"mas_workspace_id",
|
|
578
617
|
"mas_workspace_name",
|
|
618
|
+
# Storage classes
|
|
579
619
|
"storage_class_rwo",
|
|
580
620
|
"storage_class_rwx",
|
|
621
|
+
# Entitlement
|
|
581
622
|
"ibm_entitlement_key",
|
|
623
|
+
# DRO
|
|
582
624
|
"uds_contact_email",
|
|
583
625
|
"uds_contact_firstname",
|
|
584
626
|
"uds_contact_lastname"
|
|
585
627
|
]
|
|
586
628
|
optionalParams = [
|
|
629
|
+
# MAS
|
|
587
630
|
"mas_superuser_username",
|
|
588
631
|
"mas_superuser_password",
|
|
589
632
|
"mas_trust_default_cas",
|
|
@@ -609,9 +652,11 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
609
652
|
"mas_app_settings_secondary_langs",
|
|
610
653
|
"mas_app_settings_server_timezone",
|
|
611
654
|
"ocp_ingress_tls_secret_name",
|
|
655
|
+
# DRO
|
|
612
656
|
"dro_namespace",
|
|
657
|
+
# MongoDb
|
|
613
658
|
"mongodb_namespace",
|
|
614
|
-
|
|
659
|
+
# Db2
|
|
615
660
|
"db2_action_system",
|
|
616
661
|
"db2_action_manage",
|
|
617
662
|
"db2_type",
|
|
@@ -632,9 +677,12 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
632
677
|
"db2_logs_storage_size",
|
|
633
678
|
"db2_meta_storage_size",
|
|
634
679
|
"db2_temp_storage_size",
|
|
680
|
+
# CP4D
|
|
681
|
+
"cpd_product_version",
|
|
635
682
|
"cpd_install_cognos",
|
|
636
683
|
"cpd_install_openscale",
|
|
637
684
|
"cpd_install_spss",
|
|
685
|
+
# Kafka
|
|
638
686
|
"kafka_namespace",
|
|
639
687
|
"kafka_version",
|
|
640
688
|
"aws_msk_instance_type",
|
|
@@ -648,21 +696,27 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
648
696
|
"eventstreams_resource_group",
|
|
649
697
|
"eventstreams_instance_name",
|
|
650
698
|
"eventstreams_instance_location",
|
|
699
|
+
# ECK
|
|
651
700
|
"eck_action",
|
|
652
701
|
"eck_enable_logstash",
|
|
653
702
|
"eck_remote_es_hosts",
|
|
654
703
|
"eck_remote_es_username",
|
|
655
704
|
"eck_remote_es_password",
|
|
705
|
+
# Turbonomic
|
|
656
706
|
"turbonomic_target_name",
|
|
657
707
|
"turbonomic_server_url",
|
|
658
708
|
"turbonomic_server_version",
|
|
659
709
|
"turbonomic_username",
|
|
660
710
|
"turbonomic_password",
|
|
711
|
+
# Cloud Providers
|
|
661
712
|
"ibmcloud_apikey",
|
|
662
713
|
"aws_region",
|
|
663
714
|
"aws_access_key_id",
|
|
664
715
|
"secret_access_key",
|
|
665
716
|
"aws_vpc_id",
|
|
717
|
+
# Dev Mode
|
|
718
|
+
"artifactory_username",
|
|
719
|
+
"artifactory_token",
|
|
666
720
|
# TODO: The way arcgis has been implemented needs to be fixed
|
|
667
721
|
"install_arcgis",
|
|
668
722
|
"mas_arcgis_channel"
|
|
@@ -765,6 +819,23 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
765
819
|
self.fatalError(f"{key} must be set")
|
|
766
820
|
self.slsLicenseFileLocal = value
|
|
767
821
|
|
|
822
|
+
elif key.startswith("approval_"):
|
|
823
|
+
if key not in self.approvals:
|
|
824
|
+
raise KeyError(f"{key} is not a supported approval workflow ID: {self.approvals.keys()}")
|
|
825
|
+
|
|
826
|
+
if value != "":
|
|
827
|
+
valueParts = value.split(":")
|
|
828
|
+
if len(valueParts) != 4:
|
|
829
|
+
self.fatalError(f"Unsupported format for {key} ({value}). Expected APPROVAL_KEY:MAX_RETRIES:RETRY_DELAY:IGNORE_FAILURE")
|
|
830
|
+
else:
|
|
831
|
+
try:
|
|
832
|
+
self.approvals[key]["approvalKey"] = valueParts[0]
|
|
833
|
+
self.approvals[key]["maxRetries"] = int(valueParts[1])
|
|
834
|
+
self.approvals[key]["retryDelay"] = int(valueParts[2])
|
|
835
|
+
self.approvals[key]["ignoreFailure"] = bool(valueParts[3])
|
|
836
|
+
except:
|
|
837
|
+
self.fatalError(f"Unsupported format for {key} ({value}). Expected string:int:int:boolean")
|
|
838
|
+
|
|
768
839
|
# Arguments that we don't need to do anything with
|
|
769
840
|
elif key in ["accept_license", "dev_mode", "skip_pre_check", "no_confirm", "no_wait_for_pvc", "help"]:
|
|
770
841
|
pass
|
|
@@ -958,6 +1029,9 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
958
1029
|
podTemplates=self.podTemplatesSecret,
|
|
959
1030
|
certs=self.certsSecret
|
|
960
1031
|
)
|
|
1032
|
+
|
|
1033
|
+
self.setupApprovals(pipelinesNamespace)
|
|
1034
|
+
|
|
961
1035
|
h.stop_and_persist(symbol=self.successIcon, text=f"Namespace is ready ({pipelinesNamespace})")
|
|
962
1036
|
|
|
963
1037
|
with Halo(text=f'Testing availability of MAS CLI image in cluster', spinner=self.spinner) as h:
|
|
@@ -976,3 +1050,19 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
976
1050
|
else:
|
|
977
1051
|
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")
|
|
978
1052
|
print()
|
|
1053
|
+
|
|
1054
|
+
def setupApprovals(self, namespace: str) -> None:
|
|
1055
|
+
"""
|
|
1056
|
+
Ensure the supported approval configmaps are in the expected state for the start of the run:
|
|
1057
|
+
- not present (if approval is not required)
|
|
1058
|
+
- present with the chosen state field initialized to ""
|
|
1059
|
+
"""
|
|
1060
|
+
for approval in self.approvals.values():
|
|
1061
|
+
if "approvalKey" in approval:
|
|
1062
|
+
# Enable this approval workload
|
|
1063
|
+
logger.debug(f"Approval workflow for {approval['id']} will be enabled during install ({approval['maxRetries']} / {approval['retryDelay']}s / {approval['approvalKey']} / {approval['ignoreFailure']})")
|
|
1064
|
+
self.initializeApprovalConfigMap(namespace, approval['id'], approval['approvalKey'], approval['maxRetries'], approval['retryDelay'], approval['ignoreFailure'])
|
|
1065
|
+
else:
|
|
1066
|
+
# Disable this approval workload
|
|
1067
|
+
logger.debug(f"Approval workflow for {approval['id']} will be disabled during install")
|
|
1068
|
+
self.initializeApprovalConfigMap(namespace, approval['id'])
|
mas/cli/install/argParser.py
CHANGED
|
@@ -146,7 +146,7 @@ masAdvancedArgGroup.add_argument(
|
|
|
146
146
|
masAdvancedArgGroup.add_argument(
|
|
147
147
|
"--manual-certificates",
|
|
148
148
|
required=False,
|
|
149
|
-
help="
|
|
149
|
+
help="Path to directory containing the certificates to be applied"
|
|
150
150
|
)
|
|
151
151
|
|
|
152
152
|
# Storage
|
|
@@ -756,12 +756,73 @@ cloudArgGroup.add_argument(
|
|
|
756
756
|
help="Set target Virtual Private Cloud ID for the MSK instance"
|
|
757
757
|
)
|
|
758
758
|
|
|
759
|
+
# Development Mode
|
|
760
|
+
# -----------------------------------------------------------------------------
|
|
761
|
+
devArgGroup = installArgParser.add_argument_group("Development Mode")
|
|
762
|
+
devArgGroup.add_argument(
|
|
763
|
+
"--artifactory-username",
|
|
764
|
+
required=False,
|
|
765
|
+
help="Username for access to development builds on Artifactory"
|
|
766
|
+
)
|
|
767
|
+
devArgGroup.add_argument(
|
|
768
|
+
"--artifactory-token",
|
|
769
|
+
required=False,
|
|
770
|
+
help="API Token for access to development builds on Artifactory"
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
# Approvals
|
|
774
|
+
# -----------------------------------------------------------------------------
|
|
775
|
+
approvalsGroup = installArgParser.add_argument_group("Integrated Approval Workflow (APPROVAL_KEY:MAX_RETRIES:RETRY_DELAY:IGNORE_FAILURE)")
|
|
776
|
+
approvalsGroup.add_argument(
|
|
777
|
+
"--approval-core",
|
|
778
|
+
default="",
|
|
779
|
+
help="Require approval after the Core Platform has been configured"
|
|
780
|
+
)
|
|
781
|
+
approvalsGroup.add_argument(
|
|
782
|
+
"--approval-assist",
|
|
783
|
+
default="",
|
|
784
|
+
help="Require approval after the Maximo Assist workspace has been configured"
|
|
785
|
+
)
|
|
786
|
+
approvalsGroup.add_argument(
|
|
787
|
+
"--approval-iot",
|
|
788
|
+
default="",
|
|
789
|
+
help="Require approval after the Maximo IoT workspace has been configured"
|
|
790
|
+
)
|
|
791
|
+
approvalsGroup.add_argument(
|
|
792
|
+
"--approval-manage",
|
|
793
|
+
default="",
|
|
794
|
+
help="Require approval after the Maximo Manage workspace has been configured"
|
|
795
|
+
)
|
|
796
|
+
approvalsGroup.add_argument(
|
|
797
|
+
"--approval-monitor",
|
|
798
|
+
default="",
|
|
799
|
+
help="Require approval after the Maximo Monitor workspace has been configured"
|
|
800
|
+
)
|
|
801
|
+
approvalsGroup.add_argument(
|
|
802
|
+
"--approval-optimizer",
|
|
803
|
+
default="",
|
|
804
|
+
help="Require approval after the Maximo Optimizer workspace has been configured"
|
|
805
|
+
)
|
|
806
|
+
approvalsGroup.add_argument(
|
|
807
|
+
"--approval-predict",
|
|
808
|
+
default="",
|
|
809
|
+
help="Require approval after the Maximo Predict workspace has been configured"
|
|
810
|
+
)
|
|
811
|
+
approvalsGroup.add_argument(
|
|
812
|
+
"--approval-visualinspection",
|
|
813
|
+
default="",
|
|
814
|
+
help="Require approval after the Maximo Visual Inspection workspace has been configured"
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
# More Options
|
|
819
|
+
# -----------------------------------------------------------------------------
|
|
759
820
|
otherArgGroup = installArgParser.add_argument_group("More")
|
|
760
821
|
otherArgGroup.add_argument(
|
|
761
822
|
"--accept-license",
|
|
762
823
|
action="store_true",
|
|
763
824
|
default=False,
|
|
764
|
-
help=""
|
|
825
|
+
help="Accept all license terms without prompting"
|
|
765
826
|
)
|
|
766
827
|
otherArgGroup.add_argument(
|
|
767
828
|
"--dev-mode",
|