From 1fcebca61c92af2e915aedbbac5c8bff3ddb41e1 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 10:00:36 +0530 Subject: [PATCH 01/26] [patch] Changes to support db migrate --- python/src/mas/cli/db2_migration/__init__.py | 9 + python/src/mas/cli/db2_migration/app.py | 196 ++++++++++++++++++ python/src/mas/cli/db2_migration/argParser.py | 94 +++++++++ .../pipelines/mas-devops-db2-migration.yml.j2 | 100 +++++++++ 4 files changed, 399 insertions(+) create mode 100644 python/src/mas/cli/db2_migration/__init__.py create mode 100644 python/src/mas/cli/db2_migration/app.py create mode 100644 python/src/mas/cli/db2_migration/argParser.py create mode 100644 tekton/src/pipelines/mas-devops-db2-migration.yml.j2 diff --git a/python/src/mas/cli/db2_migration/__init__.py b/python/src/mas/cli/db2_migration/__init__.py new file mode 100644 index 0000000000..a8e4860b7e --- /dev/null +++ b/python/src/mas/cli/db2_migration/__init__.py @@ -0,0 +1,9 @@ +# ----------------------------------------------------------- +# Licensed Materials - Property of IBM +# 5737-M66 +# (C) Copyright IBM Corp. 2026 All Rights Reserved. +# US Government Users Restricted Rights - Use, duplication, or disclosure +# restricted by GSA ADP Schedule Contract with IBM Corp. +# ----------------------------------------------------------- + +from ..cli import BaseApp # noqa: F401 diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py new file mode 100644 index 0000000000..8db9625f66 --- /dev/null +++ b/python/src/mas/cli/db2_migration/app.py @@ -0,0 +1,196 @@ +# ----------------------------------------------------------- +# Licensed Materials - Property of IBM +# 5737-M66 +# (C) Copyright IBM Corp. 2026 All Rights Reserved. +# US Government Users Restricted Rights - Use, duplication, or disclosure +# restricted by GSA ADP Schedule Contract with IBM Corp. +# ----------------------------------------------------------- + +import logging + +from typing import List, Dict, Any +from halo import Halo +from prompt_toolkit import print_formatted_text, HTML +from openshift.dynamic.exceptions import NotFoundError + +from ..cli import BaseApp +from .argParser import db2MigrationArgParser +from mas.devops.ocp import createNamespace +from mas.devops.tekton import ( + preparePipelinesNamespace, + installOpenShiftPipelines, + updateTektonDefinitions, + launchDb2MigrationPipeline # NEW FUNCTION +) + +logger = logging.getLogger(__name__) + + +class Db2MigrationApp(BaseApp): + """Application class for DB2 cluster migration""" + + def detectDb2uClusters(self, namespace: str) -> List[Dict[str, Any]]: + """Detect all Db2uCluster instances in the specified namespace. + + Args: + namespace (str): Kubernetes namespace to search + + Returns: + List[Dict[str, Any]]: List of Db2uCluster resources found + """ + try: + db2ClusterAPI = self.dynamicClient.resources.get( + api_version="db2u.databases.ibm.com/v1", + kind="Db2uCluster" + ) + clusters = db2ClusterAPI.get(namespace=namespace) + return clusters.items if clusters else [] + except NotFoundError: + return [] + + def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: + """Prompt user to select a cluster from the detected list. + + Args: + clusters (List[Dict[str, Any]]): List of available clusters + + Returns: + Dict[str, Any]: Selected cluster resource + """ + if len(clusters) == 1: + cluster = clusters[0] + clusterName = cluster.metadata.name + self.printHighlight(f"Found 1 Db2uCluster: {clusterName}") + return cluster + + # Multiple clusters - prompt for selection + self.printH2("Available Db2uClusters") + options = [] + for i, cluster in enumerate(clusters): + name = cluster.metadata.name + version = cluster.spec.version if hasattr(cluster.spec, 'version') else 'unknown' + status = cluster.status.state if hasattr(cluster, 'status') and hasattr(cluster.status, 'state') else 'unknown' + options.append(f"{name} (version: {version}, status: {status})") + + selectedIndex = self.promptForListSelection("Select cluster to migrate", options) + return clusters[selectedIndex] + + def promptForBackup(self) -> bool: + """Prompt user whether to perform backup before migration. + + Returns: + bool: True if backup should be performed, False otherwise + """ + self.printH2("Backup Configuration") + print_formatted_text(HTML( + "It is strongly recommended to backup before migration.\n" + "This will create a full database backup that can be used for rollback.\n" + )) + return self.yesOrNo("Perform backup before migration") + + def migrate(self, argv: List[str]) -> None: + """Main entry point for DB2 migration command. + + Args: + argv (List[str]): Command line arguments + """ + args = db2MigrationArgParser.parse_args(argv) + self.noConfirm = args.no_confirm + + # Connect to cluster + self.connect() + + # Determine mode: interactive vs non-interactive + isInteractive = args.namespace is None + + if isInteractive: + # Interactive mode + self.printH1("DB2 Cluster Migration") + + # Prompt for namespace + namespace = self.promptForString("Enter namespace containing Db2uClusters") + + # Detect clusters + with Halo(text=f"Detecting Db2uClusters in namespace {namespace}", spinner=self.spinner) as h: + clusters = self.detectDb2uClusters(namespace) + if not clusters: + h.fail(f"No Db2uClusters found in namespace {namespace}") + self.fatalError(f"No Db2uClusters found in namespace {namespace}") + h.succeed(f"Found {len(clusters)} Db2uCluster(s)") + + # Select cluster + selectedCluster = self.promptForCluster(clusters) + clusterName = selectedCluster.metadata.name + + # Prompt for backup + enableBackup = self.promptForBackup() + + else: + # Non-interactive mode + namespace = args.namespace + clusterName = args.cluster_name + enableBackup = args.backup == "true" if args.backup else True + + # Validate cluster exists if name provided + if clusterName: + clusters = self.detectDb2uClusters(namespace) + clusterNames = [c.metadata.name for c in clusters] + if clusterName not in clusterNames: + self.fatalError(f"Cluster {clusterName} not found in namespace {namespace}") + else: + # Auto-select if only one cluster + clusters = self.detectDb2uClusters(namespace) + if len(clusters) == 0: + self.fatalError(f"No Db2uClusters found in namespace {namespace}") + elif len(clusters) == 1: + clusterName = clusters[0].metadata.name + else: + self.fatalError("Multiple clusters found. Please specify --cluster-name") + + # Confirmation + if not self.noConfirm: + self.printH2("Migration Summary") + print_formatted_text(HTML( + f"Namespace: {namespace}\n" + f"Cluster: {clusterName}\n" + f"Backup: {'Enabled' if enableBackup else 'Disabled'}\n" + )) + if not self.yesOrNo("Proceed with migration"): + print_formatted_text(HTML("Migration cancelled")) + return + + # Set parameters + self.setParam("db2_migration_namespace", namespace) + self.setParam("db2_migration_cluster_name", clusterName) + self.setParam("db2_migration_backup_enabled", str(enableBackup).lower()) + + # Prepare pipeline namespace + pipelinesNamespace = "mas-pipelines" + + with Halo(text="Validating OpenShift Pipelines installation", spinner=self.spinner) as h: + if installOpenShiftPipelines(self.dynamicClient): + h.succeed("OpenShift Pipelines Operator is installed and ready") + else: + h.fail("OpenShift Pipelines Operator installation failed") + self.fatalError("Installation failed") + + with Halo(text=f"Preparing namespace ({pipelinesNamespace})", spinner=self.spinner) as h: + createNamespace(self.dynamicClient, pipelinesNamespace) + preparePipelinesNamespace(dynClient=self.dynamicClient) + h.succeed(f"Namespace {pipelinesNamespace} is ready") + + with Halo(text=f"Installing latest Tekton definitions (v{self.version})", spinner=self.spinner) as h: + updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath) + h.succeed(f"Latest Tekton definitions are installed (v{self.version})") + + # Launch pipeline + with Halo(text="Submitting PipelineRun for DB2 migration", spinner=self.spinner) as h: + pipelineURL = launchDb2MigrationPipeline( + dynClient=self.dynamicClient, + params=self.params + ) + if pipelineURL: + h.succeed("PipelineRun for DB2 migration submitted") + print_formatted_text(HTML(f"\nView progress:\n {pipelineURL}\n")) + else: + h.fail("Failed to submit PipelineRun, see log file for details") diff --git a/python/src/mas/cli/db2_migration/argParser.py b/python/src/mas/cli/db2_migration/argParser.py new file mode 100644 index 0000000000..4dce880049 --- /dev/null +++ b/python/src/mas/cli/db2_migration/argParser.py @@ -0,0 +1,94 @@ +# ----------------------------------------------------------- +# Licensed Materials - Property of IBM +# 5737-M66 +# (C) Copyright IBM Corp. 2026 All Rights Reserved. +# US Government Users Restricted Rights - Use, duplication, or disclosure +# restricted by GSA ADP Schedule Contract with IBM Corp. +# ----------------------------------------------------------- + +import argparse +import sys + +from ..cli import getHelpFormatter + + +class Db2MigrationArgumentParser(argparse.ArgumentParser): + """Custom argument parser for DB2 migration command""" + + def format_usage(self): + prog = self.prog + return ( + f"Usage (non-interactive mode):\n" + f" {prog} --namespace NAMESPACE [--cluster-name CLUSTER_NAME]\n" + f" [--backup {{true,false}}] [--no-confirm]\n" + f"\n" + f"Usage (interactive mode):\n" + f" {prog}\n" + f"\n" + f"Usage (help):\n" + f" {prog} -h\n" + ) + + def parse_args(self, args=None, namespace=None): + parsedArgs = super().parse_args(args, namespace) + + providedArgs = [] + if args is not None: + providedArgs = [arg for arg in args if arg.startswith("-")] + else: + providedArgs = [arg for arg in sys.argv[1:] if arg.startswith("-")] + + hasAnyArgs = len(providedArgs) > 0 + hasNamespace = parsedArgs.namespace is not None + helpOnly = "--help" in providedArgs or "-h" in providedArgs + + if hasAnyArgs and not hasNamespace and not helpOnly: + self.error("non-interactive mode requires --namespace parameter") + + return parsedArgs + + +db2MigrationArgParser = Db2MigrationArgumentParser( + prog="mas db2ucluster-migration", + description="Migrate Db2uCluster to Db2uInstance by launching the DB2 Migration Tekton Pipeline.", + epilog="Refer to the online documentation for more information: https://ibm-mas.github.io/cli/", + formatter_class=getHelpFormatter(), + add_help=False, +) + +migrationArgGroup = db2MigrationArgParser.add_argument_group( + "Migration Configuration", + "Configure the DB2 migration parameters." +) +migrationArgGroup.add_argument( + "--namespace", + required=False, + help="Namespace containing Db2uCluster instances" +) +migrationArgGroup.add_argument( + "--cluster-name", + required=False, + help="Specific Db2uCluster name to migrate" +) +migrationArgGroup.add_argument( + "--backup", + required=False, + choices=["true", "false"], + help="Enable or disable backup before migration" +) + +otherArgGroup = db2MigrationArgParser.add_argument_group("More", "Additional options.") +otherArgGroup.add_argument( + "--no-confirm", + required=False, + action="store_true", + default=False, + help="Launch without prompting for confirmation" +) +otherArgGroup.add_argument( + "-h", + "--help", + action="help", + default=False, + help="Show this help message and exit" +) diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 new file mode 100644 index 0000000000..b82bb5646d --- /dev/null +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -0,0 +1,100 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: mas-devops-db2-migration +spec: + workspaces: + - name: shared-configs + optional: true + + params: + # Tekton Pipeline Configuration + # ------------------------------------------------------------------------- + - name: image_pull_policy + type: string + default: IfNotPresent + + # DB2 Migration Parameters + # ------------------------------------------------------------------------- + - name: db2_action + type: string + default: "migrate" + description: Action for db2 role (must be 'migrate') + + - name: db2_migration_namespace + type: string + description: Namespace containing Db2uCluster to migrate + + - name: db2_migration_cluster_name + type: string + description: Name of Db2uCluster to migrate + + - name: db2_migration_backup_enabled + type: string + default: "true" + description: Whether to backup before migration + + - name: db2_migration_cleanup_old_cluster + type: string + default: "false" + description: Whether to delete old Db2uCluster CR after migration + + - name: ibm_entitlement_key + type: string + default: "" + description: IBM Entitlement Key + + tasks: + # 1. Wait for approval (auto-approved) + # ------------------------------------------------------------------------- + #- name: waitfor-approval + # timeout: "0" + # taskRef: + # kind: Task + # name: mas-devops-wait-for-configmap-v2 + # params: + # - name: image_pull_policy + # value: $(params.image_pull_policy) + # - name: configmap_name + # value: approval-db2-migration + # - name: configmap_initial_value + # value: approved + # - name: pipeline_status + # value: Started + # - name: pipeline_name + # value: $(context.pipeline.name) + # - name: pipelinerun_name + # value: $(context.pipelineRun.name) + # - name: pipelinerun_namespace + # value: $(context.pipelineRun.namespace) + + # Run DB2 Migration + # ------------------------------------------------------------------------- + - name: db2-migration + timeout: "0" + taskRef: + kind: Task + name: mas-devops-db2 + params: + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/common/cli-params.yml.j2') | indent(8) }} + - name: devops_suite_name + value: db2-migration + - name: db2_action + value: $(params.db2_action) + - name: db2_migration_namespace + value: $(params.db2_migration_namespace) + - name: db2_migration_cluster_name + value: $(params.db2_migration_cluster_name) + - name: db2_migration_backup_enabled + value: $(params.db2_migration_backup_enabled) + - name: db2_migration_cleanup_old_cluster + value: $(params.db2_migration_cleanup_old_cluster) + workspaces: + - name: configs + workspace: shared-configs + + finally: + # Notify on completion + # ------------------------------------------------------------------------- + {{ lookup('template', 'taskdefs/common/finalize.yml.j2') | indent(4) }} From 5e5c40277eb54018f1ad9f4150e85c7088a9b46b Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 10:55:05 +0530 Subject: [PATCH 02/26] Pre-commit updates --- python/src/mas/cli/db2_migration/app.py | 91 +++++++++---------- python/src/mas/cli/db2_migration/argParser.py | 48 ++-------- 2 files changed, 52 insertions(+), 87 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 8db9625f66..4b6ad8adb7 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -16,12 +16,7 @@ from ..cli import BaseApp from .argParser import db2MigrationArgParser from mas.devops.ocp import createNamespace -from mas.devops.tekton import ( - preparePipelinesNamespace, - installOpenShiftPipelines, - updateTektonDefinitions, - launchDb2MigrationPipeline # NEW FUNCTION -) +from mas.devops.tekton import preparePipelinesNamespace, installOpenShiftPipelines, updateTektonDefinitions, launchDb2MigrationPipeline logger = logging.getLogger(__name__) @@ -31,18 +26,15 @@ class Db2MigrationApp(BaseApp): def detectDb2uClusters(self, namespace: str) -> List[Dict[str, Any]]: """Detect all Db2uCluster instances in the specified namespace. - + Args: namespace (str): Kubernetes namespace to search - + Returns: List[Dict[str, Any]]: List of Db2uCluster resources found """ try: - db2ClusterAPI = self.dynamicClient.resources.get( - api_version="db2u.databases.ibm.com/v1", - kind="Db2uCluster" - ) + db2ClusterAPI = self.dynamicClient.resources.get(api_version="db2u.databases.ibm.com/v1", kind="Db2uCluster") clusters = db2ClusterAPI.get(namespace=namespace) return clusters.items if clusters else [] except NotFoundError: @@ -50,10 +42,10 @@ def detectDb2uClusters(self, namespace: str) -> List[Dict[str, Any]]: def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: """Prompt user to select a cluster from the detected list. - + Args: clusters (List[Dict[str, Any]]): List of available clusters - + Returns: Dict[str, Any]: Selected cluster resource """ @@ -62,54 +54,56 @@ def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: clusterName = cluster.metadata.name self.printHighlight(f"Found 1 Db2uCluster: {clusterName}") return cluster - + # Multiple clusters - prompt for selection self.printH2("Available Db2uClusters") options = [] for i, cluster in enumerate(clusters): name = cluster.metadata.name - version = cluster.spec.version if hasattr(cluster.spec, 'version') else 'unknown' - status = cluster.status.state if hasattr(cluster, 'status') and hasattr(cluster.status, 'state') else 'unknown' + version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" + status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" options.append(f"{name} (version: {version}, status: {status})") - + selectedIndex = self.promptForListSelection("Select cluster to migrate", options) return clusters[selectedIndex] def promptForBackup(self) -> bool: """Prompt user whether to perform backup before migration. - + Returns: bool: True if backup should be performed, False otherwise """ self.printH2("Backup Configuration") - print_formatted_text(HTML( - "It is strongly recommended to backup before migration.\n" - "This will create a full database backup that can be used for rollback.\n" - )) + print_formatted_text( + HTML( + "It is strongly recommended to backup before migration.\n" + "This will create a full database backup that can be used for rollback.\n" + ) + ) return self.yesOrNo("Perform backup before migration") def migrate(self, argv: List[str]) -> None: """Main entry point for DB2 migration command. - + Args: argv (List[str]): Command line arguments """ args = db2MigrationArgParser.parse_args(argv) self.noConfirm = args.no_confirm - + # Connect to cluster self.connect() - + # Determine mode: interactive vs non-interactive isInteractive = args.namespace is None - + if isInteractive: # Interactive mode self.printH1("DB2 Cluster Migration") - + # Prompt for namespace namespace = self.promptForString("Enter namespace containing Db2uClusters") - + # Detect clusters with Halo(text=f"Detecting Db2uClusters in namespace {namespace}", spinner=self.spinner) as h: clusters = self.detectDb2uClusters(namespace) @@ -117,20 +111,20 @@ def migrate(self, argv: List[str]) -> None: h.fail(f"No Db2uClusters found in namespace {namespace}") self.fatalError(f"No Db2uClusters found in namespace {namespace}") h.succeed(f"Found {len(clusters)} Db2uCluster(s)") - + # Select cluster selectedCluster = self.promptForCluster(clusters) clusterName = selectedCluster.metadata.name - + # Prompt for backup enableBackup = self.promptForBackup() - + else: # Non-interactive mode namespace = args.namespace clusterName = args.cluster_name enableBackup = args.backup == "true" if args.backup else True - + # Validate cluster exists if name provided if clusterName: clusters = self.detectDb2uClusters(namespace) @@ -146,49 +140,48 @@ def migrate(self, argv: List[str]) -> None: clusterName = clusters[0].metadata.name else: self.fatalError("Multiple clusters found. Please specify --cluster-name") - + # Confirmation if not self.noConfirm: self.printH2("Migration Summary") - print_formatted_text(HTML( - f"Namespace: {namespace}\n" - f"Cluster: {clusterName}\n" - f"Backup: {'Enabled' if enableBackup else 'Disabled'}\n" - )) + print_formatted_text( + HTML( + f"Namespace: {namespace}\n" + f"Cluster: {clusterName}\n" + f"Backup: {'Enabled' if enableBackup else 'Disabled'}\n" + ) + ) if not self.yesOrNo("Proceed with migration"): print_formatted_text(HTML("Migration cancelled")) return - + # Set parameters self.setParam("db2_migration_namespace", namespace) self.setParam("db2_migration_cluster_name", clusterName) self.setParam("db2_migration_backup_enabled", str(enableBackup).lower()) - + # Prepare pipeline namespace pipelinesNamespace = "mas-pipelines" - + with Halo(text="Validating OpenShift Pipelines installation", spinner=self.spinner) as h: if installOpenShiftPipelines(self.dynamicClient): h.succeed("OpenShift Pipelines Operator is installed and ready") else: h.fail("OpenShift Pipelines Operator installation failed") self.fatalError("Installation failed") - + with Halo(text=f"Preparing namespace ({pipelinesNamespace})", spinner=self.spinner) as h: createNamespace(self.dynamicClient, pipelinesNamespace) preparePipelinesNamespace(dynClient=self.dynamicClient) h.succeed(f"Namespace {pipelinesNamespace} is ready") - + with Halo(text=f"Installing latest Tekton definitions (v{self.version})", spinner=self.spinner) as h: updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath) h.succeed(f"Latest Tekton definitions are installed (v{self.version})") - + # Launch pipeline with Halo(text="Submitting PipelineRun for DB2 migration", spinner=self.spinner) as h: - pipelineURL = launchDb2MigrationPipeline( - dynClient=self.dynamicClient, - params=self.params - ) + pipelineURL = launchDb2MigrationPipeline(dynClient=self.dynamicClient, params=self.params) if pipelineURL: h.succeed("PipelineRun for DB2 migration submitted") print_formatted_text(HTML(f"\nView progress:\n {pipelineURL}\n")) diff --git a/python/src/mas/cli/db2_migration/argParser.py b/python/src/mas/cli/db2_migration/argParser.py index 4dce880049..ec69743605 100644 --- a/python/src/mas/cli/db2_migration/argParser.py +++ b/python/src/mas/cli/db2_migration/argParser.py @@ -31,20 +31,20 @@ def format_usage(self): def parse_args(self, args=None, namespace=None): parsedArgs = super().parse_args(args, namespace) - + providedArgs = [] if args is not None: providedArgs = [arg for arg in args if arg.startswith("-")] else: providedArgs = [arg for arg in sys.argv[1:] if arg.startswith("-")] - + hasAnyArgs = len(providedArgs) > 0 hasNamespace = parsedArgs.namespace is not None helpOnly = "--help" in providedArgs or "-h" in providedArgs - + if hasAnyArgs and not hasNamespace and not helpOnly: self.error("non-interactive mode requires --namespace parameter") - + return parsedArgs @@ -56,39 +56,11 @@ def parse_args(self, args=None, namespace=None): add_help=False, ) -migrationArgGroup = db2MigrationArgParser.add_argument_group( - "Migration Configuration", - "Configure the DB2 migration parameters." -) -migrationArgGroup.add_argument( - "--namespace", - required=False, - help="Namespace containing Db2uCluster instances" -) -migrationArgGroup.add_argument( - "--cluster-name", - required=False, - help="Specific Db2uCluster name to migrate" -) -migrationArgGroup.add_argument( - "--backup", - required=False, - choices=["true", "false"], - help="Enable or disable backup before migration" -) +migrationArgGroup = db2MigrationArgParser.add_argument_group("Migration Configuration", "Configure the DB2 migration parameters.") +migrationArgGroup.add_argument("--namespace", required=False, help="Namespace containing Db2uCluster instances") +migrationArgGroup.add_argument("--cluster-name", required=False, help="Specific Db2uCluster name to migrate") +migrationArgGroup.add_argument("--backup", required=False, choices=["true", "false"], help="Enable or disable backup before migration") otherArgGroup = db2MigrationArgParser.add_argument_group("More", "Additional options.") -otherArgGroup.add_argument( - "--no-confirm", - required=False, - action="store_true", - default=False, - help="Launch without prompting for confirmation" -) -otherArgGroup.add_argument( - "-h", - "--help", - action="help", - default=False, - help="Show this help message and exit" -) +otherArgGroup.add_argument("--no-confirm", required=False, action="store_true", default=False, help="Launch without prompting for confirmation") +otherArgGroup.add_argument("-h", "--help", action="help", default=False, help="Show this help message and exit") From b867311c4f0f72e7488089decf776f56bbfc0d46 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 12:12:15 +0530 Subject: [PATCH 03/26] [patch] Register command for Migrate --- python/src/mas/cli/__main__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python/src/mas/cli/__main__.py b/python/src/mas/cli/__main__.py index dda2407428..8aa61d17c1 100644 --- a/python/src/mas/cli/__main__.py +++ b/python/src/mas/cli/__main__.py @@ -51,6 +51,7 @@ def usage() -> None: + " - mas-cli must-gather Collect diagnostic information\n" + " - mas-cli setup-rbac Set up RBAC resources for MAS installation\n" + " - mas-cli pre-install Set up pre-install RBAC for MAS\n" + + " - mas-cli db2ucluster-migration Migrate Db2uCluster to Db2uInstance\n" ) ) print_formatted_text(HTML("For usage information run mas-cli [action] --help\n")) @@ -132,6 +133,12 @@ def main() -> None: app = MustGatherApp() app.mustGather(argv[2:]) return + if function == "db2ucluster-migration": + from mas.cli.db2_migration.app import Db2MigrationApp + + app = Db2MigrationApp() + app.migrate(argv[2:]) + return if function in ["-h", "--help"]: usage() raise SystemExit(0) From c21895bf0efe61e24192adca1c4d91510f741271 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 16:06:20 +0530 Subject: [PATCH 04/26] [patch] Updated pipelines --- python/src/mas/cli/db2_migration/app.py | 20 ++++++++++++++----- tekton/generate-tekton-pipelines.yml | 1 + .../pipelines/mas-devops-db2-migration.yml.j2 | 5 ----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 4b6ad8adb7..504a71b5ee 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -56,16 +56,26 @@ def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: return cluster # Multiple clusters - prompt for selection + #self.printH2("Available Db2uClusters") + #options = [] + #for i, cluster in enumerate(clusters): + # name = cluster.metadata.name + # version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" + # status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" + # options.append(f"{name} (version: {version}, status: {status})") + + #selectedIndex = self.promptForListSelect("Select cluster to migrate", options) + #return clusters[selectedIndex] + self.printH2("Available Db2uClusters") - options = [] for i, cluster in enumerate(clusters): name = cluster.metadata.name version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" - options.append(f"{name} (version: {version}, status: {status})") + print(f" {i+1}. {name} (version: {version}, status: {status})") - selectedIndex = self.promptForListSelection("Select cluster to migrate", options) - return clusters[selectedIndex] + selectedIndex = self.promptForInt("Select cluster to migrate", min=1, max=len(clusters)) + return clusters[selectedIndex - 1] def promptForBackup(self) -> bool: """Prompt user whether to perform backup before migration. @@ -176,7 +186,7 @@ def migrate(self, argv: List[str]) -> None: h.succeed(f"Namespace {pipelinesNamespace} is ready") with Halo(text=f"Installing latest Tekton definitions (v{self.version})", spinner=self.spinner) as h: - updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath) + updateTektonDefinitions(self.dynamicClient, pipelinesNamespace, self.tektonDefsPath) h.succeed(f"Latest Tekton definitions are installed (v{self.version})") # Launch pipeline diff --git a/tekton/generate-tekton-pipelines.yml b/tekton/generate-tekton-pipelines.yml index be5aa9d6ad..f266ff40ab 100644 --- a/tekton/generate-tekton-pipelines.yml +++ b/tekton/generate-tekton-pipelines.yml @@ -23,6 +23,7 @@ - mas-upgrade - mas-rollback - mas-uninstall + - mas-devops-db2-migration # MAS FVT Pipelines - mas-fvt-assist - mas-fvt-core diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 index b82bb5646d..59d3e373f7 100644 --- a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -93,8 +93,3 @@ spec: workspaces: - name: configs workspace: shared-configs - - finally: - # Notify on completion - # ------------------------------------------------------------------------- - {{ lookup('template', 'taskdefs/common/finalize.yml.j2') | indent(4) }} From 1deb3b9cdc46851633c466b640faf698226b943f Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 16:17:12 +0530 Subject: [PATCH 05/26] [patch] Updated comments --- python/src/mas/cli/db2_migration/app.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 504a71b5ee..e4d881711a 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -56,16 +56,16 @@ def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: return cluster # Multiple clusters - prompt for selection - #self.printH2("Available Db2uClusters") - #options = [] - #for i, cluster in enumerate(clusters): + # self.printH2("Available Db2uClusters") + # options = [] + # for i, cluster in enumerate(clusters): # name = cluster.metadata.name # version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" # status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" # options.append(f"{name} (version: {version}, status: {status})") - #selectedIndex = self.promptForListSelect("Select cluster to migrate", options) - #return clusters[selectedIndex] + # selectedIndex = self.promptForListSelect("Select cluster to migrate", options) + # return clusters[selectedIndex] self.printH2("Available Db2uClusters") for i, cluster in enumerate(clusters): From ce23d4b81f57e705ca4c8dcff24d862be2fef1ff Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 18:11:30 +0530 Subject: [PATCH 06/26] [patch] Updated parameters --- python/src/mas/cli/cli.py | 3 ++- tekton/src/tasks/dependencies/db2.yml.j2 | 28 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index 071822a118..e64a80140e 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -126,7 +126,8 @@ def __init__(self) -> None: logging.getLogger("urllib3").setLevel(logging.INFO) # Supports extended semver, unlike mas.cli.__version__ - self.version: str = "100.0.0-pre.local" + # self.version: str = "100.0.0-pre.local" + self.version: str = "21.8.0-pre.mascore-11096" self.h1count: int = 0 self.h2count: int = 0 diff --git a/tekton/src/tasks/dependencies/db2.yml.j2 b/tekton/src/tasks/dependencies/db2.yml.j2 index 1c245910a1..d4aaad598f 100644 --- a/tekton/src/tasks/dependencies/db2.yml.j2 +++ b/tekton/src/tasks/dependencies/db2.yml.j2 @@ -219,6 +219,24 @@ spec: description: S3 secret key for backups default: "" + # DB2 Migration Parameters + - name: db2_migration_namespace + type: string + description: Namespace containing Db2uCluster to migrate + default: "" + - name: db2_migration_cluster_name + type: string + description: Name of Db2uCluster to migrate + default: "" + - name: db2_migration_backup_enabled + type: string + description: Whether to backup before migration + default: "true" + - name: db2_migration_cleanup_old_cluster + type: string + description: Whether to delete old Db2uCluster CR after migration + default: "false" + stepTemplate: env: {{ lookup('template', task_src_dir ~ '/common/cli-env.yml.j2') | indent(6) }} @@ -362,6 +380,16 @@ spec: value: $(params.backup_s3_secret_key) - name: OVERRIDE_STORAGECLASS value: $(params.override_storageclass) + + # DB2 Migration specific + - name: DB2_MIGRATION_NAMESPACE + value: $(params.db2_migration_namespace) + - name: DB2_MIGRATION_CLUSTER_NAME + value: $(params.db2_migration_cluster_name) + - name: DB2_MIGRATION_BACKUP_ENABLED + value: $(params.db2_migration_backup_enabled) + - name: DB2_MIGRATION_CLEANUP_OLD_CLUSTER + value: $(params.db2_migration_cleanup_old_cluster) steps: - name: db2 command: From 2daa20f83322e285d25285e45e62919d050b3d63 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 19:14:43 +0530 Subject: [PATCH 07/26] [patch] Updated parameters --- tekton/src/pipelines/mas-devops-db2-migration.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 index 59d3e373f7..7aec52d3b9 100644 --- a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -47,7 +47,7 @@ spec: tasks: # 1. Wait for approval (auto-approved) - # ------------------------------------------------------------------------- + # --------------------------------------------------------------------------- #- name: waitfor-approval # timeout: "0" # taskRef: From 8e2c8cc3b9c93ee9da57ec8c63ef112752e7935a Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 22 Jun 2026 19:33:49 +0530 Subject: [PATCH 08/26] [patch] Updated parameters to test --- tekton/src/pipelines/mas-devops-db2-migration.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 index 7aec52d3b9..59d3e373f7 100644 --- a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -47,7 +47,7 @@ spec: tasks: # 1. Wait for approval (auto-approved) - # --------------------------------------------------------------------------- + # ------------------------------------------------------------------------- #- name: waitfor-approval # timeout: "0" # taskRef: From d8397a68991778d47e31fb6c9804300f60f48d7f Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 23 Jun 2026 11:21:21 +0530 Subject: [PATCH 09/26] Updated to add new case for db migration --- image/cli/mascli/mas | 10 ++++++++++ python/src/mas/cli/db2_migration/app.py | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/image/cli/mascli/mas b/image/cli/mascli/mas index 1f986b264c..f2a6414dc3 100755 --- a/image/cli/mascli/mas +++ b/image/cli/mascli/mas @@ -359,6 +359,16 @@ case $1 in mas-cli restore "$@" ;; + db2ucluster-migration) + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >> $LOGFILE + echo "!! db2ucluster-migration !!" >> $LOGFILE + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >> $LOGFILE + echo "${TEXT_UNDERLINE}IBM Maximo Application Suite DB2 Migration Manager (v${VERSION})${TEXT_RESET}" + echo "Powered by ${COLOR_CYAN}${TEXT_UNDERLINE}https://github.com/ibm-mas/ansible-devops/${TEXT_RESET} and ${COLOR_CYAN}${TEXT_UNDERLINE}https://tekton.dev/${TEXT_RESET}" + shift + mas-cli db2ucluster-migration "$@" + ;; + gitops-bootstrap) echo "${TEXT_UNDERLINE}IBM Maximo Application Suite GitOps Manager (v${VERSION})${TEXT_RESET}" echo "Powered by ${COLOR_CYAN}${TEXT_UNDERLINE}https://github.com/ibm-mas/gitops/${TEXT_RESET}" diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index e4d881711a..2d1cb57641 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -11,6 +11,7 @@ from typing import List, Dict, Any from halo import Halo from prompt_toolkit import print_formatted_text, HTML +from kubernetes import client from openshift.dynamic.exceptions import NotFoundError from ..cli import BaseApp @@ -111,8 +112,27 @@ def migrate(self, argv: List[str]) -> None: # Interactive mode self.printH1("DB2 Cluster Migration") - # Prompt for namespace - namespace = self.promptForString("Enter namespace containing Db2uClusters") + # List db2u namespaces + with Halo(text="Detecting db2u namespaces", spinner=self.spinner) as h: + try: + v1 = client.CoreV1Api() + allNamespaces = v1.list_namespace() + db2uNamespaces = [ns.metadata.name for ns in allNamespaces.items + if ns.metadata.name.startswith("db2u")] + + if db2uNamespaces: + h.succeed(f"Found {len(db2uNamespaces)} db2u namespace(s)") + print_formatted_text(HTML("Available db2u namespaces:")) + for ns in sorted(db2uNamespaces): + print(f" - {ns}") + print() + else: + h.info("No db2u namespaces found") + except Exception as e: + h.fail(f"Failed to list namespaces: {e}") + + # Prompt for namespace with default + namespace = self.promptForString("Enter namespace containing Db2uClusters", default="db2u") # Detect clusters with Halo(text=f"Detecting Db2uClusters in namespace {namespace}", spinner=self.spinner) as h: From ba11151ed998fe25bae4f2e15bf6fa5ac6295b04 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 23 Jun 2026 11:29:46 +0530 Subject: [PATCH 10/26] Fixed the format --- python/src/mas/cli/db2_migration/app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 2d1cb57641..d5b3952db7 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -117,8 +117,7 @@ def migrate(self, argv: List[str]) -> None: try: v1 = client.CoreV1Api() allNamespaces = v1.list_namespace() - db2uNamespaces = [ns.metadata.name for ns in allNamespaces.items - if ns.metadata.name.startswith("db2u")] + db2uNamespaces = [ns.metadata.name for ns in allNamespaces.items if ns.metadata.name.startswith("db2u")] if db2uNamespaces: h.succeed(f"Found {len(db2uNamespaces)} db2u namespace(s)") From fb9533a75af3d92d6f464039f36ea4aac90d3167 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 23 Jun 2026 12:52:22 +0530 Subject: [PATCH 11/26] Added the help text --- image/cli/app-root/src/.bashrc | 1 + 1 file changed, 1 insertion(+) diff --git a/image/cli/app-root/src/.bashrc b/image/cli/app-root/src/.bashrc index 00a04b16e4..79250897d0 100644 --- a/image/cli/app-root/src/.bashrc +++ b/image/cli/app-root/src/.bashrc @@ -29,6 +29,7 @@ echo " - ${TEXT_BOLD}${COLOR_GREEN}mas must-gather${TEXT_RESET} to perform must echo " - ${TEXT_BOLD}${COLOR_GREEN}mas uninstall${TEXT_RESET} to uninstall a MAS instance" echo " - ${TEXT_BOLD}${COLOR_GREEN}mas backup${TEXT_RESET} to backup a MAS instance" echo " - ${TEXT_BOLD}${COLOR_GREEN}mas restore${TEXT_RESET} to restore a MAS instance" +echo " - ${TEXT_BOLD}${COLOR_GREEN}mas db2ucluster-migration${TEXT_RESET} to migrate Db2uCluster to Db2uInstance" # None of these functions are tested/supported on s390x /ppc64le yet if [ $arch != "s390x" ] && [ $arch != "ppc64le" ]; then From aeda5800b9723078d98f0791939a3158bfd10cb3 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 23 Jun 2026 19:20:09 +0530 Subject: [PATCH 12/26] Reverting self.version to default to test in containerised cli --- python/src/mas/cli/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index e64a80140e..e8f1edd2f8 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -126,8 +126,8 @@ def __init__(self) -> None: logging.getLogger("urllib3").setLevel(logging.INFO) # Supports extended semver, unlike mas.cli.__version__ - # self.version: str = "100.0.0-pre.local" - self.version: str = "21.8.0-pre.mascore-11096" + self.version: str = "100.0.0-pre.local" + # self.version: str = "21.8.0-pre.mascore-11096" self.h1count: int = 0 self.h2count: int = 0 From aa9452358add0e2cfdc6f0caf31013c32816d5d1 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 24 Jun 2026 08:50:27 +0530 Subject: [PATCH 13/26] Removed import client from app.py --- python/src/mas/cli/db2_migration/app.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index d5b3952db7..d0c753b217 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -11,7 +11,7 @@ from typing import List, Dict, Any from halo import Halo from prompt_toolkit import print_formatted_text, HTML -from kubernetes import client +# from kubernetes import client from openshift.dynamic.exceptions import NotFoundError from ..cli import BaseApp @@ -115,9 +115,12 @@ def migrate(self, argv: List[str]) -> None: # List db2u namespaces with Halo(text="Detecting db2u namespaces", spinner=self.spinner) as h: try: - v1 = client.CoreV1Api() - allNamespaces = v1.list_namespace() + namespaceAPI = self.dynamicClient.resources.get(api_version="v1", kind="Namespace") + allNamespaces = namespaceAPI.get() db2uNamespaces = [ns.metadata.name for ns in allNamespaces.items if ns.metadata.name.startswith("db2u")] + # v1 = client.CoreV1Api() + # allNamespaces = v1.list_namespace() + # db2uNamespaces = [ns.metadata.name for ns in allNamespaces.items if ns.metadata.name.startswith("db2u")] if db2uNamespaces: h.succeed(f"Found {len(db2uNamespaces)} db2u namespace(s)") From 84251ffc2fd0b33b06df3268c4ad815959b60c5b Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 24 Jun 2026 09:03:22 +0530 Subject: [PATCH 14/26] Removed unwanted lines from app.py --- python/src/mas/cli/db2_migration/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index d0c753b217..cd0130bcbc 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -11,7 +11,6 @@ from typing import List, Dict, Any from halo import Halo from prompt_toolkit import print_formatted_text, HTML -# from kubernetes import client from openshift.dynamic.exceptions import NotFoundError from ..cli import BaseApp From 536d15949613988610e322c631cc2f5ac69b47df Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 24 Jun 2026 10:26:51 +0530 Subject: [PATCH 15/26] Fixed import modules in cli.py --- python/src/mas/cli/cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index e8f1edd2f8..67292d2970 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -25,8 +25,8 @@ from kubernetes import config from kubernetes.client.api_client import ApiClient from kubernetes.client import Configuration -from openshift.dynamic import DynamicClient -from openshift.dynamic.exceptions import NotFoundError +from kubernetes.dynamic import DynamicClient +from kubernetes.dynamic.exceptions import NotFoundError from prompt_toolkit import prompt, print_formatted_text, HTML @@ -127,7 +127,6 @@ def __init__(self) -> None: # Supports extended semver, unlike mas.cli.__version__ self.version: str = "100.0.0-pre.local" - # self.version: str = "21.8.0-pre.mascore-11096" self.h1count: int = 0 self.h2count: int = 0 From 8bb7022dd72bd920cba3fb4dedaa48d0d8eac314 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 24 Jun 2026 10:55:31 +0530 Subject: [PATCH 16/26] merged master --- python/src/mas/cli/db2_migration/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index cd0130bcbc..3c83348fbc 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -55,7 +55,7 @@ def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: self.printHighlight(f"Found 1 Db2uCluster: {clusterName}") return cluster - # Multiple clusters - prompt for selection + # Multiple clusters - Prompt for selection # self.printH2("Available Db2uClusters") # options = [] # for i, cluster in enumerate(clusters): From 20a83adfe8b10ac720e00d04ae9cd636eb5f7fc4 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 24 Jun 2026 15:40:40 +0530 Subject: [PATCH 17/26] updated python --- python/src/mas/cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index 462520c992..0a7f68d33c 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -6,7 +6,7 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # -# ***************************************************************************** +# ****************************************************************************** import logging import logging.handlers From 7c5db2ef69656e935f7c13d5bfeae36f0fe8869a Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Fri, 26 Jun 2026 12:06:29 +0530 Subject: [PATCH 18/26] Updated branch --- python/src/mas/cli/db2_migration/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 3c83348fbc..35427f8725 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -4,7 +4,7 @@ # (C) Copyright IBM Corp. 2026 All Rights Reserved. # US Government Users Restricted Rights - Use, duplication, or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. -# ----------------------------------------------------------- +# ------------------------------------------------------------ import logging From a2b7fc68a07cef014f5a827743c6d421a053ae5d Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Fri, 26 Jun 2026 12:23:25 +0530 Subject: [PATCH 19/26] Updated branch #1 --- python/src/mas/cli/db2_migration/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 35427f8725..3c83348fbc 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -4,7 +4,7 @@ # (C) Copyright IBM Corp. 2026 All Rights Reserved. # US Government Users Restricted Rights - Use, duplication, or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. -# ------------------------------------------------------------ +# ----------------------------------------------------------- import logging From 5408920d5ec1064e2bea80408d389b8ec926f277 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Mon, 20 Jul 2026 15:46:18 +0530 Subject: [PATCH 20/26] Rebuilding CLI --- python/src/mas/cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index dc77421e32..aea9f48bda 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -6,7 +6,7 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # -# ****************************************************************************** +# ***************************************************************************** import logging import logging.handlers From 15e7b51af7db72a54b9c040dc1428381d4a341b6 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 21 Jul 2026 14:10:40 +0530 Subject: [PATCH 21/26] Updated the cli tag to test, revert after testing --- image/cli/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image/cli/Dockerfile b/image/cli/Dockerfile index 2cae56b63d..a59a84c6b3 100644 --- a/image/cli/Dockerfile +++ b/image/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/ibmmas/cli-base:latest +FROM quay.io/ibmmas/cli-base:1.13.0-pre.mascore-11096 ARG VERSION_LABEL # Auto-expire the image in quay.io after 3 weeks From 0e453215ba2d590e6cd4c32f437e782212befdbe Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Tue, 11 Aug 2026 20:16:45 +0530 Subject: [PATCH 22/26] Reverting cli-base --- image/cli/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image/cli/Dockerfile b/image/cli/Dockerfile index a59a84c6b3..2cae56b63d 100644 --- a/image/cli/Dockerfile +++ b/image/cli/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/ibmmas/cli-base:1.13.0-pre.mascore-11096 +FROM quay.io/ibmmas/cli-base:latest ARG VERSION_LABEL # Auto-expire the image in quay.io after 3 weeks From a66c52f96df715a78a93ef75018629919f3c0677 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Wed, 7 Oct 2026 17:01:33 +0530 Subject: [PATCH 23/26] MASCORE-11096 Db2uCluster migration naming cleanup - Rename --cluster-name CLI arg to --db2-cluster-name - Rename all cluster/clusterName locals to db2_cluster/db2ClusterName - Rename method promptForCluster -> promptForDb2Cluster - Update class docstring and user-facing strings to reference Db2uCluster explicitly - Update setParam call to use db2_migration_db2_cluster_name - Rename db2_migration_cluster_name -> db2_migration_db2_cluster_name in pipeline/task j2 - Rename db2_migration_cleanup_old_cluster -> db2_migration_cleanup_old_db2_cluster in pipeline/task j2 - Rename env vars DB2_MIGRATION_CLUSTER_NAME and DB2_MIGRATION_CLEANUP_OLD_CLUSTER - Remove commented-out waitfor-approval task block from pipeline --- python/src/mas/cli/db2_migration/app.py | 96 +++++++++---------- python/src/mas/cli/db2_migration/argParser.py | 4 +- .../pipelines/mas-devops-db2-migration.yml.j2 | 35 ++----- tekton/src/tasks/dependencies/db2.yml.j2 | 12 +-- 4 files changed, 62 insertions(+), 85 deletions(-) diff --git a/python/src/mas/cli/db2_migration/app.py b/python/src/mas/cli/db2_migration/app.py index 3c83348fbc..7d270e33d9 100644 --- a/python/src/mas/cli/db2_migration/app.py +++ b/python/src/mas/cli/db2_migration/app.py @@ -22,7 +22,7 @@ class Db2MigrationApp(BaseApp): - """Application class for DB2 cluster migration""" + """Application class for Db2uCluster to Db2uInstance migration""" def detectDb2uClusters(self, namespace: str) -> List[Dict[str, Any]]: """Detect all Db2uCluster instances in the specified namespace. @@ -35,47 +35,47 @@ def detectDb2uClusters(self, namespace: str) -> List[Dict[str, Any]]: """ try: db2ClusterAPI = self.dynamicClient.resources.get(api_version="db2u.databases.ibm.com/v1", kind="Db2uCluster") - clusters = db2ClusterAPI.get(namespace=namespace) - return clusters.items if clusters else [] + db2_clusters = db2ClusterAPI.get(namespace=namespace) + return db2_clusters.items if db2_clusters else [] except NotFoundError: return [] - def promptForCluster(self, clusters: List[Dict[str, Any]]) -> Dict[str, Any]: - """Prompt user to select a cluster from the detected list. + def promptForDb2Cluster(self, db2_clusters: List[Dict[str, Any]]) -> Dict[str, Any]: + """Prompt user to select a Db2uCluster from the detected list. Args: - clusters (List[Dict[str, Any]]): List of available clusters + db2_clusters (List[Dict[str, Any]]): List of available Db2uCluster resources Returns: - Dict[str, Any]: Selected cluster resource + Dict[str, Any]: Selected Db2uCluster resource """ - if len(clusters) == 1: - cluster = clusters[0] - clusterName = cluster.metadata.name - self.printHighlight(f"Found 1 Db2uCluster: {clusterName}") - return cluster + if len(db2_clusters) == 1: + db2_cluster = db2_clusters[0] + db2ClusterName = db2_cluster.metadata.name + self.printHighlight(f"Found 1 Db2uCluster: {db2ClusterName}") + return db2_cluster - # Multiple clusters - Prompt for selection + # Multiple Db2uClusters - Prompt for selection # self.printH2("Available Db2uClusters") # options = [] - # for i, cluster in enumerate(clusters): - # name = cluster.metadata.name - # version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" - # status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" + # for i, db2_cluster in enumerate(db2_clusters): + # name = db2_cluster.metadata.name + # version = db2_cluster.spec.version if hasattr(db2_cluster.spec, "version") else "unknown" + # status = db2_cluster.status.state if hasattr(db2_cluster, "status") and hasattr(db2_cluster.status, "state") else "unknown" # options.append(f"{name} (version: {version}, status: {status})") - # selectedIndex = self.promptForListSelect("Select cluster to migrate", options) - # return clusters[selectedIndex] + # selectedIndex = self.promptForListSelect("Select Db2uCluster to migrate", options) + # return db2_clusters[selectedIndex] self.printH2("Available Db2uClusters") - for i, cluster in enumerate(clusters): - name = cluster.metadata.name - version = cluster.spec.version if hasattr(cluster.spec, "version") else "unknown" - status = cluster.status.state if hasattr(cluster, "status") and hasattr(cluster.status, "state") else "unknown" + for i, db2_cluster in enumerate(db2_clusters): + name = db2_cluster.metadata.name + version = db2_cluster.spec.version if hasattr(db2_cluster.spec, "version") else "unknown" + status = db2_cluster.status.state if hasattr(db2_cluster, "status") and hasattr(db2_cluster.status, "state") else "unknown" print(f" {i+1}. {name} (version: {version}, status: {status})") - selectedIndex = self.promptForInt("Select cluster to migrate", min=1, max=len(clusters)) - return clusters[selectedIndex - 1] + selectedIndex = self.promptForInt("Select Db2uCluster to migrate", min=1, max=len(db2_clusters)) + return db2_clusters[selectedIndex - 1] def promptForBackup(self) -> bool: """Prompt user whether to perform backup before migration. @@ -109,7 +109,7 @@ def migrate(self, argv: List[str]) -> None: if isInteractive: # Interactive mode - self.printH1("DB2 Cluster Migration") + self.printH1("Db2uCluster to Db2uInstance Migration") # List db2u namespaces with Halo(text="Detecting db2u namespaces", spinner=self.spinner) as h: @@ -135,17 +135,17 @@ def migrate(self, argv: List[str]) -> None: # Prompt for namespace with default namespace = self.promptForString("Enter namespace containing Db2uClusters", default="db2u") - # Detect clusters + # Detect Db2uClusters with Halo(text=f"Detecting Db2uClusters in namespace {namespace}", spinner=self.spinner) as h: - clusters = self.detectDb2uClusters(namespace) - if not clusters: + db2_clusters = self.detectDb2uClusters(namespace) + if not db2_clusters: h.fail(f"No Db2uClusters found in namespace {namespace}") self.fatalError(f"No Db2uClusters found in namespace {namespace}") - h.succeed(f"Found {len(clusters)} Db2uCluster(s)") + h.succeed(f"Found {len(db2_clusters)} Db2uCluster(s)") - # Select cluster - selectedCluster = self.promptForCluster(clusters) - clusterName = selectedCluster.metadata.name + # Select Db2uCluster + selectedDb2Cluster = self.promptForDb2Cluster(db2_clusters) + db2ClusterName = selectedDb2Cluster.metadata.name # Prompt for backup enableBackup = self.promptForBackup() @@ -153,24 +153,24 @@ def migrate(self, argv: List[str]) -> None: else: # Non-interactive mode namespace = args.namespace - clusterName = args.cluster_name + db2ClusterName = args.db2_cluster_name enableBackup = args.backup == "true" if args.backup else True - # Validate cluster exists if name provided - if clusterName: - clusters = self.detectDb2uClusters(namespace) - clusterNames = [c.metadata.name for c in clusters] - if clusterName not in clusterNames: - self.fatalError(f"Cluster {clusterName} not found in namespace {namespace}") + # Validate Db2uCluster exists if name provided + if db2ClusterName: + db2_clusters = self.detectDb2uClusters(namespace) + db2ClusterNames = [c.metadata.name for c in db2_clusters] + if db2ClusterName not in db2ClusterNames: + self.fatalError(f"Db2uCluster {db2ClusterName} not found in namespace {namespace}") else: - # Auto-select if only one cluster - clusters = self.detectDb2uClusters(namespace) - if len(clusters) == 0: + # Auto-select if only one Db2uCluster + db2_clusters = self.detectDb2uClusters(namespace) + if len(db2_clusters) == 0: self.fatalError(f"No Db2uClusters found in namespace {namespace}") - elif len(clusters) == 1: - clusterName = clusters[0].metadata.name + elif len(db2_clusters) == 1: + db2ClusterName = db2_clusters[0].metadata.name else: - self.fatalError("Multiple clusters found. Please specify --cluster-name") + self.fatalError("Multiple Db2uClusters found. Please specify --db2-cluster-name") # Confirmation if not self.noConfirm: @@ -178,7 +178,7 @@ def migrate(self, argv: List[str]) -> None: print_formatted_text( HTML( f"Namespace: {namespace}\n" - f"Cluster: {clusterName}\n" + f"Db2uCluster: {db2ClusterName}\n" f"Backup: {'Enabled' if enableBackup else 'Disabled'}\n" ) ) @@ -188,7 +188,7 @@ def migrate(self, argv: List[str]) -> None: # Set parameters self.setParam("db2_migration_namespace", namespace) - self.setParam("db2_migration_cluster_name", clusterName) + self.setParam("db2_migration_db2_cluster_name", db2ClusterName) self.setParam("db2_migration_backup_enabled", str(enableBackup).lower()) # Prepare pipeline namespace diff --git a/python/src/mas/cli/db2_migration/argParser.py b/python/src/mas/cli/db2_migration/argParser.py index ec69743605..19360ee136 100644 --- a/python/src/mas/cli/db2_migration/argParser.py +++ b/python/src/mas/cli/db2_migration/argParser.py @@ -19,7 +19,7 @@ def format_usage(self): prog = self.prog return ( f"Usage (non-interactive mode):\n" - f" {prog} --namespace NAMESPACE [--cluster-name CLUSTER_NAME]\n" + f" {prog} --namespace NAMESPACE [--db2-cluster-name DB2_CLUSTER_NAME]\n" f" [--backup {{true,false}}] [--no-confirm]\n" f"\n" f"Usage (interactive mode):\n" @@ -58,7 +58,7 @@ def parse_args(self, args=None, namespace=None): migrationArgGroup = db2MigrationArgParser.add_argument_group("Migration Configuration", "Configure the DB2 migration parameters.") migrationArgGroup.add_argument("--namespace", required=False, help="Namespace containing Db2uCluster instances") -migrationArgGroup.add_argument("--cluster-name", required=False, help="Specific Db2uCluster name to migrate") +migrationArgGroup.add_argument("--db2-cluster-name", required=False, help="Specific Db2uCluster name to migrate") migrationArgGroup.add_argument("--backup", required=False, choices=["true", "false"], help="Enable or disable backup before migration") otherArgGroup = db2MigrationArgParser.add_argument_group("More", "Additional options.") diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 index 59d3e373f7..17fefa5313 100644 --- a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -26,7 +26,7 @@ spec: type: string description: Namespace containing Db2uCluster to migrate - - name: db2_migration_cluster_name + - name: db2_migration_db2_cluster_name type: string description: Name of Db2uCluster to migrate @@ -35,7 +35,7 @@ spec: default: "true" description: Whether to backup before migration - - name: db2_migration_cleanup_old_cluster + - name: db2_migration_cleanup_old_db2_cluster type: string default: "false" description: Whether to delete old Db2uCluster CR after migration @@ -46,29 +46,6 @@ spec: description: IBM Entitlement Key tasks: - # 1. Wait for approval (auto-approved) - # ------------------------------------------------------------------------- - #- name: waitfor-approval - # timeout: "0" - # taskRef: - # kind: Task - # name: mas-devops-wait-for-configmap-v2 - # params: - # - name: image_pull_policy - # value: $(params.image_pull_policy) - # - name: configmap_name - # value: approval-db2-migration - # - name: configmap_initial_value - # value: approved - # - name: pipeline_status - # value: Started - # - name: pipeline_name - # value: $(context.pipeline.name) - # - name: pipelinerun_name - # value: $(context.pipelineRun.name) - # - name: pipelinerun_namespace - # value: $(context.pipelineRun.namespace) - # Run DB2 Migration # ------------------------------------------------------------------------- - name: db2-migration @@ -84,12 +61,12 @@ spec: value: $(params.db2_action) - name: db2_migration_namespace value: $(params.db2_migration_namespace) - - name: db2_migration_cluster_name - value: $(params.db2_migration_cluster_name) + - name: db2_migration_db2_cluster_name + value: $(params.db2_migration_db2_cluster_name) - name: db2_migration_backup_enabled value: $(params.db2_migration_backup_enabled) - - name: db2_migration_cleanup_old_cluster - value: $(params.db2_migration_cleanup_old_cluster) + - name: db2_migration_cleanup_old_db2_cluster + value: $(params.db2_migration_cleanup_old_db2_cluster) workspaces: - name: configs workspace: shared-configs diff --git a/tekton/src/tasks/dependencies/db2.yml.j2 b/tekton/src/tasks/dependencies/db2.yml.j2 index aaa0e8a0a9..c6a5e78ec0 100644 --- a/tekton/src/tasks/dependencies/db2.yml.j2 +++ b/tekton/src/tasks/dependencies/db2.yml.j2 @@ -224,7 +224,7 @@ spec: type: string description: Namespace containing Db2uCluster to migrate default: "" - - name: db2_migration_cluster_name + - name: db2_migration_db2_cluster_name type: string description: Name of Db2uCluster to migrate default: "" @@ -232,7 +232,7 @@ spec: type: string description: Whether to backup before migration default: "true" - - name: db2_migration_cleanup_old_cluster + - name: db2_migration_cleanup_old_db2_cluster type: string description: Whether to delete old Db2uCluster CR after migration default: "false" @@ -384,12 +384,12 @@ spec: # DB2 Migration specific - name: DB2_MIGRATION_NAMESPACE value: $(params.db2_migration_namespace) - - name: DB2_MIGRATION_CLUSTER_NAME - value: $(params.db2_migration_cluster_name) + - name: DB2_MIGRATION_DB2_CLUSTER_NAME + value: $(params.db2_migration_db2_cluster_name) - name: DB2_MIGRATION_BACKUP_ENABLED value: $(params.db2_migration_backup_enabled) - - name: DB2_MIGRATION_CLEANUP_OLD_CLUSTER - value: $(params.db2_migration_cleanup_old_cluster) + - name: DB2_MIGRATION_CLEANUP_OLD_DB2_CLUSTER + value: $(params.db2_migration_cleanup_old_db2_cluster) steps: - name: db2 command: From 192446126919fc343b5e44402ae8cd8f98f61141 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Thu, 8 Oct 2026 12:13:37 +0530 Subject: [PATCH 24/26] MASCORE-11096 Fix Tekton API version v1beta1 -> v1 for db2 migration pipeline --- tekton/src/pipelines/mas-devops-db2-migration.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 index 17fefa5313..060f149542 100644 --- a/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 +++ b/tekton/src/pipelines/mas-devops-db2-migration.yml.j2 @@ -1,5 +1,5 @@ --- -apiVersion: tekton.dev/v1beta1 +apiVersion: tekton.dev/v1 kind: Pipeline metadata: name: mas-devops-db2-migration From 5d38e19976c497b11906352f37e226972bf82ad9 Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Thu, 8 Oct 2026 12:23:02 +0530 Subject: [PATCH 25/26] MASCORE-11096 Fix basedpyright error: import parse from jsonpath_ng.ext.parser --- python/src/mas/cli/must_gather/common/crd_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/mas/cli/must_gather/common/crd_processor.py b/python/src/mas/cli/must_gather/common/crd_processor.py index 546c7392f9..d8e2061e95 100644 --- a/python/src/mas/cli/must_gather/common/crd_processor.py +++ b/python/src/mas/cli/must_gather/common/crd_processor.py @@ -21,7 +21,7 @@ from dataclasses import dataclass from typing import Dict, List, Tuple, Optional from kubernetes.dynamic import DynamicClient -from jsonpath_ng.ext import parse +from jsonpath_ng.ext.parser import parse logger = logging.getLogger(__name__) From f56130bdece81eb4afc52e1ad611ce71954918cb Mon Sep 17 00:00:00 2001 From: jainyjoseph Date: Thu, 8 Oct 2026 13:23:04 +0530 Subject: [PATCH 26/26] MASCORE-11096 Trigger rebuild to pick up latest ansible-devops changes