mas-cli 5.1.4__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.

Files changed (114) hide show
  1. mas/cli/__init__.py +11 -0
  2. mas/cli/aiservice/install/__init__.py +11 -0
  3. mas/cli/aiservice/install/app.py +894 -0
  4. mas/cli/aiservice/install/argBuilder.py +180 -0
  5. mas/cli/aiservice/install/argParser.py +507 -0
  6. mas/cli/aiservice/install/params.py +100 -0
  7. mas/cli/aiservice/install/summarizer.py +134 -0
  8. mas/cli/cli.py +432 -0
  9. mas/cli/displayMixins.py +132 -0
  10. mas/cli/gencfg.py +113 -0
  11. mas/cli/install/__init__.py +11 -0
  12. mas/cli/install/app.py +1316 -0
  13. mas/cli/install/argBuilder.py +465 -0
  14. mas/cli/install/argParser.py +1176 -0
  15. mas/cli/install/catalogs.py +27 -0
  16. mas/cli/install/params.py +172 -0
  17. mas/cli/install/settings/__init__.py +23 -0
  18. mas/cli/install/settings/additionalConfigs.py +227 -0
  19. mas/cli/install/settings/db2Settings.py +252 -0
  20. mas/cli/install/settings/kafkaSettings.py +103 -0
  21. mas/cli/install/settings/manageSettings.py +273 -0
  22. mas/cli/install/settings/mongodbSettings.py +46 -0
  23. mas/cli/install/settings/turbonomicSettings.py +29 -0
  24. mas/cli/install/summarizer.py +398 -0
  25. mas/cli/templates/facilities-configs.yml.j2 +25 -0
  26. mas/cli/templates/ibm-mas-tekton.yaml +49772 -0
  27. mas/cli/templates/jdbccfg.yml.j2 +52 -0
  28. mas/cli/templates/pod-templates/best-effort/ibm-data-dictionary-assetdatadictionary.yml +26 -0
  29. mas/cli/templates/pod-templates/best-effort/ibm-mas-bascfg.yml +56 -0
  30. mas/cli/templates/pod-templates/best-effort/ibm-mas-coreidp.yml +21 -0
  31. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-actions.yml +28 -0
  32. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-auth.yml +32 -0
  33. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-datapower.yml +12 -0
  34. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-devops.yml +14 -0
  35. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-dm.yml +22 -0
  36. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-dsc.yml +40 -0
  37. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-edgeconfig.yml +10 -0
  38. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-fpl.yml +24 -0
  39. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-guardian.yml +20 -0
  40. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-iot.yml +10 -0
  41. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-mbgx.yml +18 -0
  42. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-mfgx.yml +14 -0
  43. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-monitor.yml +18 -0
  44. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-orgmgmt.yml +48 -0
  45. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-provision.yml +28 -0
  46. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-registry.yml +26 -0
  47. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-state.yml +40 -0
  48. mas/cli/templates/pod-templates/best-effort/ibm-mas-iot-webui.yml +22 -0
  49. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-healthextaccelerator.yml +13 -0
  50. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-healthextworkspace.yml +10 -0
  51. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-imagestitching.yml +10 -0
  52. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageaccelerators.yml +10 -0
  53. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageapp.yml +46 -0
  54. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-manageworkspace.yml +48 -0
  55. mas/cli/templates/pod-templates/best-effort/ibm-mas-manage-slackproxy.yml +10 -0
  56. mas/cli/templates/pod-templates/best-effort/ibm-mas-pushnotificationcfg.yml +13 -0
  57. mas/cli/templates/pod-templates/best-effort/ibm-mas-scimcfg.yml +14 -0
  58. mas/cli/templates/pod-templates/best-effort/ibm-mas-slscfg.yml +10 -0
  59. mas/cli/templates/pod-templates/best-effort/ibm-mas-smtpcfg.yml +10 -0
  60. mas/cli/templates/pod-templates/best-effort/ibm-mas-suite.yml +136 -0
  61. mas/cli/templates/pod-templates/best-effort/ibm-mas-visualinspection.yml +34 -0
  62. mas/cli/templates/pod-templates/best-effort/ibm-sls-licenseservice.yml +10 -0
  63. mas/cli/templates/pod-templates/guaranteed/ibm-data-dictionary-assetdatadictionary.yml +56 -0
  64. mas/cli/templates/pod-templates/guaranteed/ibm-mas-bascfg.yml +140 -0
  65. mas/cli/templates/pod-templates/guaranteed/ibm-mas-coreidp.yml +45 -0
  66. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-actions.yml +70 -0
  67. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-auth.yml +80 -0
  68. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-datapower.yml +24 -0
  69. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-devops.yml +26 -0
  70. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-dm.yml +52 -0
  71. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-dsc.yml +106 -0
  72. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-edgeconfig.yml +16 -0
  73. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-fpl.yml +62 -0
  74. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-guardian.yml +44 -0
  75. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-iot.yml +16 -0
  76. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-mbgx.yml +42 -0
  77. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-mfgx.yml +32 -0
  78. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-monitor.yml +42 -0
  79. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-orgmgmt.yml +126 -0
  80. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-provision.yml +70 -0
  81. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-registry.yml +62 -0
  82. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-state.yml +106 -0
  83. mas/cli/templates/pod-templates/guaranteed/ibm-mas-iot-webui.yml +52 -0
  84. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-healthextaccelerator.yml +28 -0
  85. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-healthextworkspace.yml +18 -0
  86. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-imagestitching.yml +16 -0
  87. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageaccelerators.yml +16 -0
  88. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageapp.yml +106 -0
  89. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-manageworkspace.yml +126 -0
  90. mas/cli/templates/pod-templates/guaranteed/ibm-mas-manage-slackproxy.yml +16 -0
  91. mas/cli/templates/pod-templates/guaranteed/ibm-mas-pushnotificationcfg.yml +25 -0
  92. mas/cli/templates/pod-templates/guaranteed/ibm-mas-scimcfg.yml +26 -0
  93. mas/cli/templates/pod-templates/guaranteed/ibm-mas-slscfg.yml +16 -0
  94. mas/cli/templates/pod-templates/guaranteed/ibm-mas-smtpcfg.yml +16 -0
  95. mas/cli/templates/pod-templates/guaranteed/ibm-mas-suite.yml +340 -0
  96. mas/cli/templates/pod-templates/guaranteed/ibm-mas-visualinspection.yml +76 -0
  97. mas/cli/templates/pod-templates/guaranteed/ibm-sls-licenseservice.yml +16 -0
  98. mas/cli/templates/suite_mongocfg.yml.j2 +55 -0
  99. mas/cli/uninstall/__init__.py +11 -0
  100. mas/cli/uninstall/app.py +197 -0
  101. mas/cli/uninstall/argParser.py +115 -0
  102. mas/cli/update/__init__.py +11 -0
  103. mas/cli/update/app.py +673 -0
  104. mas/cli/update/argParser.py +156 -0
  105. mas/cli/upgrade/__init__.py +11 -0
  106. mas/cli/upgrade/app.py +164 -0
  107. mas/cli/upgrade/argParser.py +68 -0
  108. mas/cli/upgrade/settings/__init__.py +19 -0
  109. mas/cli/validators.py +151 -0
  110. mas_cli-5.1.4.data/scripts/mas-cli +87 -0
  111. mas_cli-5.1.4.dist-info/METADATA +73 -0
  112. mas_cli-5.1.4.dist-info/RECORD +114 -0
  113. mas_cli-5.1.4.dist-info/WHEEL +5 -0
  114. mas_cli-5.1.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,156 @@
1
+ # *****************************************************************************
2
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ #
9
+ # *****************************************************************************
10
+
11
+ import argparse
12
+
13
+ from .. import __version__ as packageVersion
14
+ from ..cli import getHelpFormatter
15
+
16
+ updateArgParser = argparse.ArgumentParser(
17
+ prog='mas update',
18
+ description="\n".join([
19
+ f"IBM Maximo Application Suite Admin CLI v{packageVersion}",
20
+ "Update the IBM Maximo Operator Catalog, and related MAS dependencies by configuring and launching the MAS Update Tekton Pipeline.\n",
21
+ "Interactive Mode:",
22
+ "Omitting the --catalog option will trigger an interactive prompt"
23
+ ]),
24
+ epilog="Refer to the online documentation for more information: https://ibm-mas.github.io/cli/",
25
+ formatter_class=getHelpFormatter(),
26
+ add_help=False
27
+ )
28
+
29
+ masArgGroup = updateArgParser.add_argument_group('Catalog Selection')
30
+ masArgGroup.add_argument(
31
+ '-c', '--catalog',
32
+ dest='mas_catalog_version',
33
+ required=False,
34
+ help="Maximo Operator Catalog Version (e.g. v9-240625-amd64)"
35
+ )
36
+
37
+ depsArgGroup = updateArgParser.add_argument_group('Update Dependencies')
38
+ depsArgGroup.add_argument(
39
+ '--db2-namespace',
40
+ required=False,
41
+ help="Namespace where Db2u operator and instances will be updated",
42
+ )
43
+
44
+ depsArgGroup.add_argument(
45
+ '--mongodb-namespace',
46
+ required=False,
47
+ help="Namespace where MongoCE operator and instances will be updated",
48
+ )
49
+
50
+ depsArgGroup.add_argument(
51
+ '--mongodb-v5-upgrade',
52
+ required=False,
53
+ action="store_const",
54
+ const="true",
55
+ help="Required to confirm a major version update for MongoDb to version 5",
56
+ )
57
+
58
+ depsArgGroup.add_argument(
59
+ '--mongodb-v6-upgrade',
60
+ required=False,
61
+ action="store_const",
62
+ const="true",
63
+ help="Required to confirm a major version update for MongoDb to version 6",
64
+ )
65
+
66
+ depsArgGroup.add_argument(
67
+ '--mongodb-v7-upgrade',
68
+ required=False,
69
+ action="store_const",
70
+ const="true",
71
+ help="Required to confirm a major version update for MongoDb to version 7",
72
+ )
73
+
74
+ depsArgGroup.add_argument(
75
+ '--kafka-namespace',
76
+ required=False,
77
+ help="Namespace where Kafka operator and instances will be updated",
78
+ )
79
+
80
+ depsArgGroup.add_argument(
81
+ '--kafka-provider',
82
+ required=False,
83
+ choices=["redhat", "strimzi"],
84
+ help="The type of Kakfa operator installed in the target namespace for updte",
85
+ )
86
+
87
+ droArgGroup = updateArgParser.add_argument_group('UDS to DRO Migration')
88
+
89
+ droArgGroup.add_argument(
90
+ '--dro-migration',
91
+ required=False,
92
+ help="Required to confirm the migration from IBM User Data Services (UDS) to IBM Data Reporter Operator (DRO)",
93
+ )
94
+
95
+ droArgGroup.add_argument(
96
+ '--dro-storage-class',
97
+ required=False,
98
+ help="Set Custom RWO Storage Class name for DRO as part of the update",
99
+ )
100
+
101
+ droArgGroup.add_argument(
102
+ '--dro-namespace',
103
+ required=False,
104
+ help="Set Custom Namespace for DRO(Default: redhat-marketplace)",
105
+ )
106
+
107
+ # Development Mode
108
+ # -----------------------------------------------------------------------------
109
+ devArgGroup = updateArgParser.add_argument_group("Development Mode")
110
+ devArgGroup.add_argument(
111
+ "--artifactory-username",
112
+ required=False,
113
+ help="Username for access to development builds on Artifactory"
114
+ )
115
+ devArgGroup.add_argument(
116
+ "--artifactory-token",
117
+ required=False,
118
+ help="API Token for access to development builds on Artifactory"
119
+ )
120
+
121
+ # More Options
122
+ # -----------------------------------------------------------------------------
123
+ otherArgGroup = updateArgParser.add_argument_group('More')
124
+ otherArgGroup.add_argument(
125
+ "--dev-mode",
126
+ required=False,
127
+ action="store_true",
128
+ default=False,
129
+ help="Configure installation for development mode",
130
+ )
131
+ otherArgGroup.add_argument(
132
+ "--cp4d-version",
133
+ dest="cpd_product_version",
134
+ required=False,
135
+ help="Product version of CP4D to use"
136
+ )
137
+ otherArgGroup.add_argument(
138
+ '--no-confirm',
139
+ required=False,
140
+ action='store_true',
141
+ default=False,
142
+ help="Launch the upgrade without prompting for confirmation",
143
+ )
144
+ otherArgGroup.add_argument(
145
+ '--skip-pre-check',
146
+ required=False,
147
+ action='store_true',
148
+ default=False,
149
+ help="Skips the 'pre-update-check' and 'post-update-verify' tasks in the update pipeline",
150
+ )
151
+ otherArgGroup.add_argument(
152
+ '-h', "--help",
153
+ action='help',
154
+ default=False,
155
+ help="Show this help message and exit",
156
+ )
@@ -0,0 +1,11 @@
1
+ # *****************************************************************************
2
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ #
9
+ # *****************************************************************************
10
+
11
+ from ..cli import BaseApp # noqa: F401
mas/cli/upgrade/app.py ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python
2
+ # *****************************************************************************
3
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
4
+ #
5
+ # All rights reserved. This program and the accompanying materials
6
+ # are made available under the terms of the Eclipse Public License v1.0
7
+ # which accompanies this distribution, and is available at
8
+ # http://www.eclipse.org/legal/epl-v10.html
9
+ #
10
+ # *****************************************************************************
11
+
12
+ import sys
13
+ import logging
14
+ import logging.handlers
15
+ from prompt_toolkit import prompt, print_formatted_text, HTML
16
+ from prompt_toolkit.completion import WordCompleter
17
+
18
+ from halo import Halo
19
+
20
+ from ..cli import BaseApp
21
+ from ..validators import InstanceIDValidator
22
+ from .argParser import upgradeArgParser
23
+ from .settings import UpgradeSettingsMixin
24
+
25
+ from mas.devops.ocp import createNamespace
26
+ from mas.devops.mas import listMasInstances, getMasChannel, getWorkspaceId, verifyAppInstance
27
+ from mas.devops.tekton import installOpenShiftPipelines, updateTektonDefinitions, launchUpgradePipeline
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class UpgradeApp(BaseApp, UpgradeSettingsMixin):
33
+ def upgrade(self, argv):
34
+ """
35
+ Upgrade MAS instance
36
+ """
37
+ args = upgradeArgParser.parse_args(args=argv)
38
+ instanceId = args.mas_instance_id
39
+ self.noConfirm = args.no_confirm
40
+ self.skipPreCheck = args.skip_pre_check
41
+ self.licenseAccepted = args.accept_license
42
+ self.devMode = args.dev_mode
43
+
44
+ if instanceId is None:
45
+ self.printH1("Set Target OpenShift Cluster")
46
+ # Connect to the target cluster
47
+ self.connect()
48
+ else:
49
+ logger.debug("MAS instance ID is set, so we assume already connected to the desired OCP")
50
+ # Need to lookup target architecture because configDb2 will try to access self.architecture
51
+ self.lookupTargetArchitecture()
52
+
53
+ if self.dynamicClient is None:
54
+ print_formatted_text(HTML("<Red>Error: The Kubernetes dynamic Client is not available. See log file for details</Red>"))
55
+ sys.exit(1)
56
+
57
+ if instanceId is None:
58
+ # Interactive mode
59
+ self.printH1("Instance Selection")
60
+ print_formatted_text(HTML("<LightSlateGrey>Select a MAS instance to upgrade from the list below:</LightSlateGrey>"))
61
+ suites = listMasInstances(self.dynamicClient)
62
+ suiteOptions = []
63
+
64
+ if len(suites) == 0:
65
+ print_formatted_text(HTML("<Red>Error: No MAS instances detected on this cluster</Red>"))
66
+ sys.exit(1)
67
+
68
+ for suite in suites:
69
+ print_formatted_text(HTML(f"- <u>{suite['metadata']['name']}</u> v{suite['status']['versions']['reconciled']}"))
70
+ suiteOptions.append(suite['metadata']['name'])
71
+
72
+ suiteCompleter = WordCompleter(suiteOptions)
73
+ print()
74
+ instanceId = prompt(HTML('<Yellow>Enter MAS instance ID: </Yellow>'), completer=suiteCompleter, validator=InstanceIDValidator(), validate_while_typing=False)
75
+
76
+ currentChannel = getMasChannel(self.dynamicClient, instanceId)
77
+ if currentChannel is not None:
78
+ if self.devMode:
79
+ # This is mainly used for the scenario where Manage Foundation would be installed, because core-upgrade does not use the value of nextChannel,
80
+ # it uses a compatibility_matrix object in ansible-devops to determine the next channel, so nextChannel is only informative for core upgrade purposes
81
+ nextChannel = prompt(HTML('<Yellow>Custom channel</Yellow> '))
82
+ else:
83
+ if currentChannel not in self.upgrade_path:
84
+ self.fatalError(f"No upgrade available, {instanceId} is are already on the latest release {currentChannel}")
85
+ nextChannel = self.upgrade_path[currentChannel]
86
+ else:
87
+ # We still allow the upgrade to proceed even though we can't detect the MAS instance. The upgrade may be being
88
+ # queued up to run after install for instance
89
+ currentChannel = "Unknown"
90
+ nextChannel = "Unknown"
91
+
92
+ if not self.licenseAccepted and not self.devMode:
93
+ self.printH1("License Terms")
94
+ self.printDescription([
95
+ "To continue with the upgrade, you must accept the license terms:",
96
+ self.licenses[nextChannel]
97
+ ])
98
+
99
+ if self.noConfirm:
100
+ self.fatalError("You must accept the license terms with --accept-license when using the --no-confirm flag")
101
+ else:
102
+ if not self.yesOrNo("Do you accept the license terms"):
103
+ exit(1)
104
+
105
+ # The only scenario where Manage Foundation needs to be installed during an upgrade is from 9.0.x to 9.1.x (if Manage was not already installed in 9.0.x).
106
+ self.setParam("should_install_manage_foundation", "false")
107
+ if nextChannel.startswith("9.1") and not verifyAppInstance(self.dynamicClient, instanceId, "manage"):
108
+ self.manageAppName = "Manage foundation"
109
+ self.showAdvancedOptions = False
110
+ self.installIoT = False
111
+ self.installFacilities = False
112
+ self.installManage = True
113
+ self.isManageFoundation = True
114
+ self.printDescription([f"{self.manageAppName} installs the following capabilities: User, Security groups, Application configurator and Mobile configurator."])
115
+ self.printH1("Configure IBM Container Registry")
116
+ self.promptForString("IBM entitlement key", "ibm_entitlement_key", isPassword=True)
117
+ if self.devMode:
118
+ self.promptForString("Artifactory username", "artifactory_username")
119
+ self.promptForString("Artifactory token", "artifactory_token", isPassword=True)
120
+ self.setParam("should_install_manage_foundation", "true")
121
+ self.setParam("mas_appws_components", "")
122
+ self.setParam("mas_app_settings_aio_flag", "false")
123
+ self.setParam("mas_app_channel_manage", nextChannel)
124
+ self.setParam("mas_workspace_id", getWorkspaceId(self.dynamicClient, instanceId))
125
+ # It has been decided that we don't need to ask for any specific Manage Settings
126
+ # self.manageSettings()
127
+ self.configDb2(silentMode=True)
128
+
129
+ self.printH1("Review Settings")
130
+ print_formatted_text(HTML(f"<LightSlateGrey>Instance ID ..................... {instanceId}</LightSlateGrey>"))
131
+ print_formatted_text(HTML(f"<LightSlateGrey>Current MAS Channel ............. {currentChannel}</LightSlateGrey>"))
132
+ print_formatted_text(HTML(f"<LightSlateGrey>Next MAS Channel ................ {nextChannel}</LightSlateGrey>"))
133
+ print_formatted_text(HTML(f"<LightSlateGrey>Skip Pre-Upgrade Checks ......... {self.skipPreCheck}</LightSlateGrey>"))
134
+
135
+ if not self.noConfirm:
136
+ print()
137
+ continueWithUpgrade = self.yesOrNo("Proceed with these settings")
138
+
139
+ if self.noConfirm or continueWithUpgrade:
140
+ self.createTektonFileWithDigest()
141
+
142
+ self.printH1("Launch Upgrade")
143
+ pipelinesNamespace = f"mas-{instanceId}-pipelines"
144
+
145
+ with Halo(text='Validating OpenShift Pipelines installation', spinner=self.spinner) as h:
146
+ installOpenShiftPipelines(self.dynamicClient)
147
+ h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator is installed and ready to use")
148
+
149
+ with Halo(text=f'Preparing namespace ({pipelinesNamespace})', spinner=self.spinner) as h:
150
+ createNamespace(self.dynamicClient, pipelinesNamespace)
151
+ h.stop_and_persist(symbol=self.successIcon, text=f"Namespace is ready ({pipelinesNamespace})")
152
+
153
+ with Halo(text=f'Installing latest Tekton definitions (v{self.version})', spinner=self.spinner) as h:
154
+ updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath)
155
+ h.stop_and_persist(symbol=self.successIcon, text=f"Latest Tekton definitions are installed (v{self.version})")
156
+
157
+ with Halo(text='Submitting PipelineRun for {instanceId} upgrade', spinner=self.spinner) as h:
158
+ pipelineURL = launchUpgradePipeline(self.dynamicClient, instanceId, self.skipPreCheck, params=self.params)
159
+ if pipelineURL is not None:
160
+ h.stop_and_persist(symbol=self.successIcon, text=f"PipelineRun for {instanceId} upgrade submitted")
161
+ print_formatted_text(HTML(f"\nView progress:\n <Cyan><u>{pipelineURL}</u></Cyan>\n"))
162
+ else:
163
+ h.stop_and_persist(symbol=self.failureIcon, text=f"Failed to submit PipelineRun for {instanceId} upgrade, see log file for details")
164
+ print()
@@ -0,0 +1,68 @@
1
+ # *****************************************************************************
2
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ #
9
+ # *****************************************************************************
10
+
11
+ import argparse
12
+
13
+ from .. import __version__ as packageVersion
14
+ from ..cli import getHelpFormatter
15
+
16
+ upgradeArgParser = argparse.ArgumentParser(
17
+ prog='mas upgrade',
18
+ description="\n".join([
19
+ f"IBM Maximo Application Suite Admin CLI v{packageVersion}",
20
+ "Upgrade MAS by configuring and launching the MAS Upgrade Tekton Pipeline.\n",
21
+ "Interactive Mode:",
22
+ "Omitting the --instance-id option will trigger an interactive prompt"
23
+ ]),
24
+ epilog="Refer to the online documentation for more information: https://ibm-mas.github.io/cli/",
25
+ formatter_class=getHelpFormatter(),
26
+ add_help=False
27
+ )
28
+
29
+ masArgGroup = upgradeArgParser.add_argument_group('MAS Instance Selection')
30
+ masArgGroup.add_argument(
31
+ '--mas-instance-id',
32
+ required=False,
33
+ help="The MAS instance ID to be upgraded"
34
+ )
35
+ otherArgGroup = upgradeArgParser.add_argument_group('More')
36
+ otherArgGroup.add_argument(
37
+ '--skip-pre-check',
38
+ required=False,
39
+ action='store_true',
40
+ default=False,
41
+ help="Disable the 'pre-upgrade-check' and 'post-upgrade-verify' tasks in the upgrade pipeline"
42
+ )
43
+ otherArgGroup.add_argument(
44
+ '--no-confirm',
45
+ required=False,
46
+ action='store_true',
47
+ default=False,
48
+ help="Launch the upgrade without prompting for confirmation",
49
+ )
50
+ otherArgGroup.add_argument(
51
+ "--accept-license",
52
+ action="store_true",
53
+ default=False,
54
+ help="Accept all license terms without prompting"
55
+ )
56
+ otherArgGroup.add_argument(
57
+ "--dev-mode",
58
+ required=False,
59
+ action="store_true",
60
+ default=False,
61
+ help="Configure upgrade for development mode",
62
+ )
63
+ otherArgGroup.add_argument(
64
+ '-h', "--help",
65
+ action='help',
66
+ default=False,
67
+ help="Show this help message and exit",
68
+ )
@@ -0,0 +1,19 @@
1
+ # *****************************************************************************
2
+ # Copyright (c) 2025 IBM Corporation and other Contributors.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ #
9
+ # *****************************************************************************
10
+
11
+ from ...install.settings.db2Settings import Db2SettingsMixin
12
+ from ...install.settings.manageSettings import ManageSettingsMixin
13
+
14
+
15
+ class UpgradeSettingsMixin(Db2SettingsMixin, ManageSettingsMixin):
16
+ """
17
+ This class collects all the Mixins providing interactive prompts for mas-upgrade
18
+ """
19
+ pass
mas/cli/validators.py ADDED
@@ -0,0 +1,151 @@
1
+ # *****************************************************************************
2
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.eclipse.org/legal/epl-v10.html
8
+ #
9
+ # *****************************************************************************
10
+
11
+ from re import match
12
+ from os import path
13
+ from json import loads, JSONDecodeError
14
+
15
+ # Use of the openshift client rather than the kubernetes client allows us access to "apply"
16
+ from openshift import dynamic
17
+ from kubernetes import config
18
+ from kubernetes.client import api_client
19
+
20
+ from prompt_toolkit.validation import Validator, ValidationError
21
+
22
+ from mas.devops.ocp import getStorageClass
23
+ from mas.devops.mas import verifyMasInstance
24
+
25
+ import logging
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class InstanceIDFormatValidator(Validator):
31
+ def validate(self, document):
32
+ """
33
+ Validate that a MAS instance ID exists on the target cluster
34
+ """
35
+ instanceId = document.text
36
+
37
+ if not match(r"^[a-z][a-z0-9-]{1,10}[a-z0-9]$", instanceId):
38
+ raise ValidationError(message='MAS instance ID does not meet the requirements', cursor_position=len(instanceId))
39
+
40
+
41
+ class WorkspaceIDFormatValidator(Validator):
42
+ def validate(self, document):
43
+ """
44
+ Validate that a MAS instance ID exists on the target cluster
45
+ """
46
+ instanceId = document.text
47
+
48
+ if not match(r"^[a-z][a-z0-9]{2,11}$", instanceId):
49
+ raise ValidationError(message='Workspace ID does not meet the requirements', cursor_position=len(instanceId))
50
+
51
+
52
+ class TimeoutFormatValidator(Validator):
53
+ def validate(self, document):
54
+ """
55
+ Validate that a MAS instance ID exists on the target cluster
56
+ """
57
+ string_to_validate = document.text
58
+ if string_to_validate != "" and not match(r'^([0-9]+)([hm])$', string_to_validate):
59
+ message = f"Error: Your input: {string_to_validate} does not meet the required pattern. Please use it in hours or minutes format (e.g., 12h, 12m)."
60
+ raise ValidationError(message=message, cursor_position=len(string_to_validate))
61
+
62
+
63
+ class WorkspaceNameFormatValidator(Validator):
64
+ def validate(self, document):
65
+ """
66
+ Validate that a MAS instance ID exists on the target cluster
67
+ """
68
+ instanceId = document.text
69
+
70
+ if not match(r"^.{3,300}$", instanceId):
71
+ raise ValidationError(message='Workspace name does not meet the requirements', cursor_position=len(instanceId))
72
+
73
+
74
+ class InstanceIDValidator(Validator):
75
+ def validate(self, document):
76
+ """
77
+ Validate that a MAS instance ID exists on the target cluster
78
+ """
79
+ instanceId = document.text
80
+
81
+ dynClient = dynamic.DynamicClient(
82
+ api_client.ApiClient(configuration=config.load_kube_config())
83
+ )
84
+ if not verifyMasInstance(dynClient, instanceId):
85
+ raise ValidationError(message='Not a valid MAS instance ID on this cluster', cursor_position=len(instanceId))
86
+
87
+
88
+ class StorageClassValidator(Validator):
89
+ def validate(self, document):
90
+ """
91
+ Validate that a StorageClass exists on the target cluster
92
+ """
93
+ name = document.text
94
+
95
+ dynClient = dynamic.DynamicClient(
96
+ api_client.ApiClient(configuration=config.load_kube_config())
97
+ )
98
+ if getStorageClass(dynClient, name) is None:
99
+ raise ValidationError(message='Specified storage class is not available on this cluster', cursor_position=len(name))
100
+
101
+
102
+ class YesNoValidator(Validator):
103
+ def validate(self, document):
104
+ """
105
+ Validate that a response is understandable as a yes/no response
106
+ """
107
+ response = document.text
108
+ if response.lower() not in ["y", "n", "yes", "no"]:
109
+ raise ValidationError(message='Enter a valid response: y(es), n(o)', cursor_position=len(response))
110
+
111
+
112
+ class FileExistsValidator(Validator):
113
+ def validate(self, document):
114
+ """
115
+ Validate that a file exists on the local system
116
+ """
117
+ response = document.text
118
+ if not path.isfile(response):
119
+ raise ValidationError(message=f"{response} does not exist, or is not a file", cursor_position=len(response))
120
+
121
+
122
+ class DirectoryExistsValidator(Validator):
123
+ def validate(self, document):
124
+ """
125
+ Validate that a file exists on the local system
126
+ """
127
+ response = document.text
128
+ if not path.isdir(response):
129
+ raise ValidationError(message=f"{response} does not exist, or is not a directory", cursor_position=len(response))
130
+
131
+
132
+ class OptimizerInstallPlanValidator(Validator):
133
+ def validate(self, document):
134
+ """
135
+ Validate that a response is a valid install plan for Optimizer
136
+ """
137
+ response = document.text
138
+ if response not in ["full", "limited"]:
139
+ raise ValidationError(message='Enter a valid response: full, limited', cursor_position=len(response))
140
+
141
+
142
+ class JsonValidator(Validator):
143
+ def validate(self, document):
144
+ """
145
+ Validate that a response is a valid JSON
146
+ """
147
+ inputJson = document.text
148
+ try:
149
+ loads(inputJson)
150
+ except JSONDecodeError:
151
+ raise (ValidationError(message='Enter a valid JSON', cursor_position=len(inputJson)))
@@ -0,0 +1,87 @@
1
+ #!python
2
+ # *****************************************************************************
3
+ # Copyright (c) 2024 IBM Corporation and other Contributors.
4
+ #
5
+ # All rights reserved. This program and the accompanying materials
6
+ # are made available under the terms of the Eclipse Public License v1.0
7
+ # which accompanies this distribution, and is available at
8
+ # http://www.eclipse.org/legal/epl-v10.html
9
+ #
10
+ # *****************************************************************************
11
+
12
+ import logging
13
+ import logging.handlers
14
+ from sys import argv
15
+
16
+ from mas.cli import __version__ as VERSION
17
+ from mas.cli.install.app import InstallApp
18
+ from mas.cli.aiservice.install.app import AiServiceInstallApp
19
+ from mas.cli.update.app import UpdateApp
20
+ from mas.cli.upgrade.app import UpgradeApp
21
+ from mas.cli.uninstall.app import UninstallApp
22
+
23
+ from prompt_toolkit import HTML, print_formatted_text
24
+ from urllib3.exceptions import MaxRetryError
25
+ from jinja2.exceptions import TemplateNotFound
26
+ from kubeconfig.exceptions import KubectlCommandError
27
+ from kubernetes.client.exceptions import ApiException
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ def usage():
33
+ print_formatted_text(HTML(""))
34
+
35
+ print_formatted_text(HTML(f"\n<u>IBM Maximo Application Suite Admin CLI v{VERSION}</u>"))
36
+ print_formatted_text(HTML("Powered by <DarkGoldenRod><u>https://github.com/ibm-mas/ansible-devops/</u></DarkGoldenRod> and <DarkGoldenRod><u>https://tekton.dev/</u></DarkGoldenRod>"))
37
+ print("")
38
+ print_formatted_text(HTML("Important Notice:\nThis standalone CLI (<ForestGreen>mas-cli</ForestGreen>) is still in beta state, not all functions supported by the <ForestGreen>mas</ForestGreen> function in quay.io/ibmmas/cli are supported yet"))
39
+ print("")
40
+ print_formatted_text(HTML(
41
+ "<b>MAS Management Actions:</b>\n"
42
+ + " - <ForestGreen>mas-cli install</ForestGreen> Install IBM Maximo Application Suite\n" # noqa: W503
43
+ + " - <ForestGreen>mas-cli update</ForestGreen> Apply updates and security fixes\n" # noqa: W503
44
+ + " - <ForestGreen>mas-cli upgrade</ForestGreen> Upgrade to a new MAS release\n" # noqa: W503
45
+ + " - <ForestGreen>mas-cli uninstall</ForestGreen> Remove MAS from the cluster\n" # noqa: W503
46
+
47
+ ))
48
+ print_formatted_text(HTML("For usage information run <ForestGreen>mas-cli [action] --help</ForestGreen>\n"))
49
+
50
+
51
+ if __name__ == '__main__':
52
+ try:
53
+ function = argv[1]
54
+
55
+ if function == "install":
56
+ app = InstallApp()
57
+ app.install(argv[2:])
58
+ elif function == "aiservice-install":
59
+ app = AiServiceInstallApp()
60
+ app.install(argv[2:])
61
+ elif function == "uninstall":
62
+ app = UninstallApp()
63
+ app.uninstall(argv[2:])
64
+ elif function == "update":
65
+ app = UpdateApp()
66
+ app.update(argv[2:])
67
+ elif function == "upgrade":
68
+ app = UpgradeApp()
69
+ app.upgrade(argv[2:])
70
+ elif function in ["-h", "--help"]:
71
+ usage()
72
+ exit(0)
73
+ else:
74
+ usage()
75
+ print_formatted_text(HTML(f"<Red>Unknown action: {function}</Red>\n"))
76
+ exit(1)
77
+
78
+ except KeyboardInterrupt:
79
+ pass
80
+ except ApiException as e:
81
+ app.fatalError(message=f"[{e.status}:{e.reason}] {e.summary()}")
82
+ except MaxRetryError as e:
83
+ app.fatalError(message="Unable to connect to API server", exception=e)
84
+ except TemplateNotFound as e:
85
+ app.fatalError("Could not find template", exception=e)
86
+ except KubectlCommandError as e:
87
+ app.fatalError("Could not execute kubectl command", exception=e)