mas-cli 15.10.0__py3-none-any.whl → 15.11.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/__init__.py +1 -1
- mas/cli/aiservice/install/app.py +5 -2
- mas/cli/aiservice/upgrade/__init__.py +11 -0
- mas/cli/aiservice/upgrade/app.py +139 -0
- mas/cli/aiservice/upgrade/argParser.py +69 -0
- mas/cli/cli.py +2 -1
- mas/cli/install/app.py +5 -2
- mas/cli/install/catalogs.py +3 -2
- mas/cli/install/settings/db2Settings.py +7 -5
- mas/cli/templates/ibm-mas-tekton.yaml +547 -198
- mas/cli/uninstall/app.py +5 -2
- mas/cli/update/app.py +11 -6
- mas/cli/upgrade/app.py +6 -2
- mas/cli/validators.py +15 -1
- {mas_cli-15.10.0.data → mas_cli-15.11.0.data}/scripts/mas-cli +4 -0
- {mas_cli-15.10.0.dist-info → mas_cli-15.11.0.dist-info}/METADATA +3 -2
- {mas_cli-15.10.0.dist-info → mas_cli-15.11.0.dist-info}/RECORD +19 -16
- {mas_cli-15.10.0.dist-info → mas_cli-15.11.0.dist-info}/WHEEL +0 -0
- {mas_cli-15.10.0.dist-info → mas_cli-15.11.0.dist-info}/top_level.txt +0 -0
mas/cli/__init__.py
CHANGED
mas/cli/aiservice/install/app.py
CHANGED
|
@@ -454,8 +454,11 @@ class AiServiceInstallApp(BaseApp, aiServiceInstallArgBuilderMixin, aiServiceIns
|
|
|
454
454
|
wait = False
|
|
455
455
|
|
|
456
456
|
with Halo(text='Validating OpenShift Pipelines installation', spinner=self.spinner) as h:
|
|
457
|
-
installOpenShiftPipelines(self.dynamicClient)
|
|
458
|
-
|
|
457
|
+
if installOpenShiftPipelines(self.dynamicClient):
|
|
458
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator is installed and ready to use")
|
|
459
|
+
else:
|
|
460
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator installation failed")
|
|
461
|
+
self.fatalError("Installation failed")
|
|
459
462
|
|
|
460
463
|
with Halo(text=f'Preparing namespace ({pipelinesNamespace})', spinner=self.spinner) as h:
|
|
461
464
|
createNamespace(self.dynamicClient, pipelinesNamespace)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# Copyright (c) 2024, 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 ...cli import BaseApp # noqa: F401
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# *****************************************************************************
|
|
3
|
+
# Copyright (c) 2024, 2025 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 AiserviceInstanceIDValidator
|
|
22
|
+
from .argParser import upgradeArgParser
|
|
23
|
+
|
|
24
|
+
from mas.devops.ocp import createNamespace
|
|
25
|
+
from mas.devops.mas import listAiServiceInstances, getAiserviceChannel
|
|
26
|
+
from mas.devops.tekton import installOpenShiftPipelines, updateTektonDefinitions, launchAiServiceUpgradePipeline
|
|
27
|
+
from openshift.dynamic.exceptions import ResourceNotFoundError
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AiServiceUpgradeApp(BaseApp):
|
|
32
|
+
def upgrade(self, argv):
|
|
33
|
+
"""
|
|
34
|
+
Upgrade AI Service instance
|
|
35
|
+
"""
|
|
36
|
+
args = upgradeArgParser.parse_args(args=argv)
|
|
37
|
+
aiserviceInstanceId = args.aiservice_instance_id
|
|
38
|
+
self.noConfirm = args.no_confirm
|
|
39
|
+
self.skipPreCheck = args.skip_pre_check
|
|
40
|
+
self.licenseAccepted = args.accept_license
|
|
41
|
+
self.devMode = args.dev_mode
|
|
42
|
+
|
|
43
|
+
if aiserviceInstanceId is None:
|
|
44
|
+
self.printH1("Set Target OpenShift Cluster")
|
|
45
|
+
# Connect to the target cluster
|
|
46
|
+
self.connect()
|
|
47
|
+
else:
|
|
48
|
+
logger.debug("AI Service instance ID is set, so we assume already connected to the desired OCP")
|
|
49
|
+
# Need to lookup target architecture because configDb2 will try to access self.architecture
|
|
50
|
+
self.lookupTargetArchitecture()
|
|
51
|
+
|
|
52
|
+
if self.dynamicClient is None:
|
|
53
|
+
print_formatted_text(HTML("<Red>Error: The Kubernetes dynamic Client is not available. See log file for details</Red>"))
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
if aiserviceInstanceId is None:
|
|
57
|
+
# Interactive mode
|
|
58
|
+
self.printH1("AI Service Instance Selection")
|
|
59
|
+
print_formatted_text(HTML("<LightSlateGrey>Select a AI Service instance to upgrade from the list below:</LightSlateGrey>"))
|
|
60
|
+
try:
|
|
61
|
+
aiserviceInstances = listAiServiceInstances(self.dynamicClient)
|
|
62
|
+
except ResourceNotFoundError:
|
|
63
|
+
aiserviceInstances = []
|
|
64
|
+
aiserviceOptions = []
|
|
65
|
+
|
|
66
|
+
if len(aiserviceInstances) == 0:
|
|
67
|
+
print_formatted_text(HTML("<Red>Error: No AI Service instances detected on this cluster</Red>"))
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
|
|
70
|
+
for aiservice in aiserviceInstances:
|
|
71
|
+
print_formatted_text(HTML(f"- <u>{aiservice['metadata']['name']}</u> v{aiservice['status']['versions']['reconciled']}"))
|
|
72
|
+
aiserviceOptions.append(aiservice['metadata']['name'])
|
|
73
|
+
|
|
74
|
+
aiserviceCompleter = WordCompleter(aiserviceOptions)
|
|
75
|
+
print()
|
|
76
|
+
aiserviceInstanceId = prompt(HTML('<Yellow>Enter AI Service instance ID: </Yellow>'), completer=aiserviceCompleter, validator=AiserviceInstanceIDValidator(), validate_while_typing=False)
|
|
77
|
+
|
|
78
|
+
currentAiserviceChannel = getAiserviceChannel(self.dynamicClient, aiserviceInstanceId)
|
|
79
|
+
if currentAiserviceChannel is not None:
|
|
80
|
+
if self.devMode:
|
|
81
|
+
# this enables upgrade of custom channel for AI service
|
|
82
|
+
nextAiserviceChannel = prompt(HTML('<Yellow>Custom channel</Yellow> '))
|
|
83
|
+
else:
|
|
84
|
+
if currentAiserviceChannel not in self.upgrade_path:
|
|
85
|
+
self.fatalError(f"No upgrade available, {aiserviceInstanceId} is are already on the latest release {currentAiserviceChannel}")
|
|
86
|
+
nextAiserviceChannel = self.upgrade_path[currentAiserviceChannel]
|
|
87
|
+
|
|
88
|
+
if not self.licenseAccepted and not self.devMode:
|
|
89
|
+
self.printH1("License Terms")
|
|
90
|
+
self.printDescription([
|
|
91
|
+
"To continue with the upgrade, you must accept the license terms:",
|
|
92
|
+
self.licenses[nextAiserviceChannel]
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
if self.noConfirm:
|
|
96
|
+
self.fatalError("You must accept the license terms with --accept-license when using the --no-confirm flag")
|
|
97
|
+
else:
|
|
98
|
+
if not self.yesOrNo("Do you accept the license terms"):
|
|
99
|
+
exit(1)
|
|
100
|
+
|
|
101
|
+
self.printH1("Review Settings")
|
|
102
|
+
print_formatted_text(HTML(f"<LightSlateGrey>AI Service Instance ID ..................... {aiserviceInstanceId}</LightSlateGrey>"))
|
|
103
|
+
print_formatted_text(HTML(f"<LightSlateGrey>Current AI Service Channel ............. {currentAiserviceChannel}</LightSlateGrey>"))
|
|
104
|
+
print_formatted_text(HTML(f"<LightSlateGrey>Next AI Service Channel ................ {nextAiserviceChannel}</LightSlateGrey>"))
|
|
105
|
+
print_formatted_text(HTML(f"<LightSlateGrey>Skip Pre-Upgrade Checks ......... {self.skipPreCheck}</LightSlateGrey>"))
|
|
106
|
+
|
|
107
|
+
if not self.noConfirm:
|
|
108
|
+
print()
|
|
109
|
+
continueWithUpgrade = self.yesOrNo("Proceed with these settings")
|
|
110
|
+
|
|
111
|
+
if self.noConfirm or continueWithUpgrade:
|
|
112
|
+
self.createTektonFileWithDigest()
|
|
113
|
+
|
|
114
|
+
self.printH1("Launch Upgrade")
|
|
115
|
+
pipelinesNamespace = f"aiservice-{aiserviceInstanceId}-pipelines"
|
|
116
|
+
|
|
117
|
+
with Halo(text='Validating OpenShift Pipelines installation', spinner=self.spinner) as h:
|
|
118
|
+
if installOpenShiftPipelines(self.dynamicClient):
|
|
119
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator is installed and ready to use")
|
|
120
|
+
else:
|
|
121
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator installation failed")
|
|
122
|
+
self.fatalError("Installation failed")
|
|
123
|
+
|
|
124
|
+
with Halo(text=f'Preparing namespace ({pipelinesNamespace})', spinner=self.spinner) as h:
|
|
125
|
+
createNamespace(self.dynamicClient, pipelinesNamespace)
|
|
126
|
+
h.stop_and_persist(symbol=self.successIcon, text=f"Namespace is ready ({pipelinesNamespace})")
|
|
127
|
+
|
|
128
|
+
with Halo(text=f'Installing latest Tekton definitions (v{self.version})', spinner=self.spinner) as h:
|
|
129
|
+
updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath)
|
|
130
|
+
h.stop_and_persist(symbol=self.successIcon, text=f"Latest Tekton definitions are installed (v{self.version})")
|
|
131
|
+
|
|
132
|
+
with Halo(text='Submitting PipelineRun for {aiserviceInstanceId} upgrade', spinner=self.spinner) as h:
|
|
133
|
+
pipelineURL = launchAiServiceUpgradePipeline(self.dynamicClient, aiserviceInstanceId, self.skipPreCheck, aiserviceChannel=nextAiserviceChannel, params=self.params)
|
|
134
|
+
if pipelineURL is not None:
|
|
135
|
+
h.stop_and_persist(symbol=self.successIcon, text=f"PipelineRun for {aiserviceInstanceId} upgrade submitted")
|
|
136
|
+
print_formatted_text(HTML(f"\nView progress:\n <Cyan><u>{pipelineURL}</u></Cyan>\n"))
|
|
137
|
+
else:
|
|
138
|
+
h.stop_and_persist(symbol=self.failureIcon, text=f"Failed to submit PipelineRun for {aiserviceInstanceId} upgrade, see log file for details")
|
|
139
|
+
print()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# *****************************************************************************
|
|
2
|
+
# Copyright (c) 2024, 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
|
+
import argparse
|
|
12
|
+
|
|
13
|
+
from ... import __version__ as packageVersion
|
|
14
|
+
from ...cli import getHelpFormatter
|
|
15
|
+
|
|
16
|
+
upgradeArgParser = argparse.ArgumentParser(
|
|
17
|
+
prog='mas aiservice-upgrade',
|
|
18
|
+
description="\n".join([
|
|
19
|
+
f"IBM Maximo Application Suite Admin CLI v{packageVersion}",
|
|
20
|
+
"Upgrade AI Service by configuring and launching the AI Service Upgrade Tekton Pipeline.\n",
|
|
21
|
+
"Interactive Mode:",
|
|
22
|
+
"Omitting the --aiservice-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
|
+
'--aiservice-instance-id',
|
|
32
|
+
required=False,
|
|
33
|
+
help="The AI Service Instance ID to be upgraded"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
otherArgGroup = upgradeArgParser.add_argument_group('More')
|
|
37
|
+
otherArgGroup.add_argument(
|
|
38
|
+
'--skip-pre-check',
|
|
39
|
+
required=False,
|
|
40
|
+
action='store_true',
|
|
41
|
+
default=False,
|
|
42
|
+
help="Disable the 'pre-upgrade-check' and 'post-upgrade-verify' tasks in the upgrade pipeline"
|
|
43
|
+
)
|
|
44
|
+
otherArgGroup.add_argument(
|
|
45
|
+
'--no-confirm',
|
|
46
|
+
required=False,
|
|
47
|
+
action='store_true',
|
|
48
|
+
default=False,
|
|
49
|
+
help="Launch the upgrade without prompting for confirmation",
|
|
50
|
+
)
|
|
51
|
+
otherArgGroup.add_argument(
|
|
52
|
+
"--accept-license",
|
|
53
|
+
action="store_true",
|
|
54
|
+
default=False,
|
|
55
|
+
help="Accept all license terms without prompting"
|
|
56
|
+
)
|
|
57
|
+
otherArgGroup.add_argument(
|
|
58
|
+
"--dev-mode",
|
|
59
|
+
required=False,
|
|
60
|
+
action="store_true",
|
|
61
|
+
default=False,
|
|
62
|
+
help="Configure upgrade for development mode",
|
|
63
|
+
)
|
|
64
|
+
otherArgGroup.add_argument(
|
|
65
|
+
'-h', "--help",
|
|
66
|
+
action='help',
|
|
67
|
+
default=False,
|
|
68
|
+
help="Show this help message and exit",
|
|
69
|
+
)
|
mas/cli/cli.py
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
# *****************************************************************************
|
|
10
10
|
|
|
11
11
|
import logging
|
|
12
|
+
import logging.handlers
|
|
12
13
|
import urllib3
|
|
13
14
|
|
|
14
15
|
from argparse import RawTextHelpFormatter
|
|
@@ -118,7 +119,7 @@ class BaseApp(PrintMixin, PromptMixin):
|
|
|
118
119
|
logging.getLogger('asyncio').setLevel(logging.INFO)
|
|
119
120
|
|
|
120
121
|
# Supports extended semver, unlike mas.cli.__version__
|
|
121
|
-
self.version = "15.
|
|
122
|
+
self.version = "15.11.0"
|
|
122
123
|
self.h1count = 0
|
|
123
124
|
self.h2count = 0
|
|
124
125
|
|
mas/cli/install/app.py
CHANGED
|
@@ -1263,8 +1263,11 @@ class InstallApp(BaseApp, InstallSettingsMixin, InstallSummarizerMixin, ConfigGe
|
|
|
1263
1263
|
pipelinesNamespace = f"mas-{self.getParam('mas_instance_id')}-pipelines"
|
|
1264
1264
|
|
|
1265
1265
|
with Halo(text='Validating OpenShift Pipelines installation', spinner=self.spinner) as h:
|
|
1266
|
-
installOpenShiftPipelines(self.dynamicClient)
|
|
1267
|
-
|
|
1266
|
+
if installOpenShiftPipelines(self.dynamicClient):
|
|
1267
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator is installed and ready to use")
|
|
1268
|
+
else:
|
|
1269
|
+
h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator installation failed")
|
|
1270
|
+
self.fatalError("Installation failed")
|
|
1268
1271
|
|
|
1269
1272
|
with Halo(text=f'Preparing namespace ({pipelinesNamespace})', spinner=self.spinner) as h:
|
|
1270
1273
|
createNamespace(self.dynamicClient, pipelinesNamespace)
|
mas/cli/install/catalogs.py
CHANGED
|
@@ -9,20 +9,21 @@
|
|
|
9
9
|
# *****************************************************************************
|
|
10
10
|
supportedCatalogs = {
|
|
11
11
|
"amd64": [
|
|
12
|
+
"v9-251030-amd64",
|
|
12
13
|
"v9-251010-amd64",
|
|
13
14
|
"v9-250925-amd64",
|
|
14
15
|
"v9-250902-amd64",
|
|
15
|
-
"v9-250828-amd64",
|
|
16
16
|
],
|
|
17
17
|
"s390x": [
|
|
18
|
+
"v9-251030-s390x",
|
|
18
19
|
# No "v9-250925-s390x" catalog for the s390x
|
|
19
20
|
"v9-250902-s390x",
|
|
20
21
|
"v9-250828-s390x",
|
|
21
22
|
],
|
|
22
23
|
"ppc64le": [
|
|
24
|
+
"v9-251030-ppc64le",
|
|
23
25
|
"v9-251010-ppc64le",
|
|
24
26
|
"v9-250925-ppc64le",
|
|
25
27
|
"v9-250902-ppc64le",
|
|
26
|
-
"v9-250828-ppc64le",
|
|
27
28
|
],
|
|
28
29
|
}
|
|
@@ -157,6 +157,8 @@ class Db2SettingsMixin():
|
|
|
157
157
|
f"Note that the same settings are applied to both the IoT and {self.manageAppName} Db2 instances",
|
|
158
158
|
"Use existing node labels and taints to control scheduling of the Db2 workload in your cluster",
|
|
159
159
|
"For more information refer to the Red Hat documentation:",
|
|
160
|
+
" - <Orange><u>https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/nodes/controlling-pod-placement-onto-nodes-scheduling#nodes-scheduler-node-affinity</u></Orange>",
|
|
161
|
+
" - <Orange><u>https://docs.redhat.com/en/documentation/openshift_container_platform/4.19/html/nodes/controlling-pod-placement-onto-nodes-scheduling#nodes-scheduler-taints-tolerations</u></Orange>",
|
|
160
162
|
" - <Orange><u>https://docs.openshift.com/container-platform/4.18/nodes/scheduling/nodes-scheduler-node-affinity.html</u></Orange>",
|
|
161
163
|
" - <Orange><u>https://docs.openshift.com/container-platform/4.18/nodes/scheduling/nodes-scheduler-taints-tolerations.html</u></Orange>",
|
|
162
164
|
" - <Orange><u>https://docs.openshift.com/container-platform/4.17/nodes/scheduling/nodes-scheduler-node-affinity.html</u></Orange>",
|
|
@@ -220,8 +222,8 @@ class Db2SettingsMixin():
|
|
|
220
222
|
self.params["db2_cpu_requests"] = "300m"
|
|
221
223
|
|
|
222
224
|
else:
|
|
223
|
-
self.setParam("db2_meta_storage_size", "
|
|
224
|
-
self.setParam("db2_backup_storage_size", "
|
|
225
|
-
self.setParam("db2_logs_storage_size", "
|
|
226
|
-
self.setParam("db2_temp_storage_size", "
|
|
227
|
-
self.setParam("db2_data_storage_size", "
|
|
225
|
+
self.setParam("db2_meta_storage_size", "10Gi")
|
|
226
|
+
self.setParam("db2_backup_storage_size", "50Gi")
|
|
227
|
+
self.setParam("db2_logs_storage_size", "10Gi")
|
|
228
|
+
self.setParam("db2_temp_storage_size", "10Gi")
|
|
229
|
+
self.setParam("db2_data_storage_size", "50Gi")
|