diff --git a/docs/guides/install-aiservice.md b/docs/guides/install-aiservice.md new file mode 100644 index 00000000000..bcd7ec14178 --- /dev/null +++ b/docs/guides/install-aiservice.md @@ -0,0 +1,149 @@ +Installation +=============================================================================== +Usage +------------------------------------------------------------------------------- +For full usage information run `mas aiservice-install --help`
+`mas aiservice-install` is specifically built for installation of Aiservice 9.1.x or above.
+
+For installation of Aiservice 9.0.x have to use `mas install` command. + +Preparation +------------------------------------------------------------------------------- +### IBM Entitlement Key +Access [Container Software Library](https://myibm.ibm.com/products-services/containerlibrary) using your IBMId to obtain your entitlement key. + +### MAS License File +Access [IBM License Key Center](https://licensing.flexnetoperations.com/), on the **Get Keys** menu select **IBM AppPoint Suites**. Select `IBM MAXIMO APPLICATION SUITE AppPOINT LIC` and on the next page fill in the information as below: + +| Field | Content | +| ---------------- | ----------------------------------------------------------------------------- | +| Number of Keys | How many AppPoints to assign to the license file | +| Host ID Type | Set to **Ethernet Address** | +| Host ID | Enter any 12 digit hexadecimal string | +| Hostname | Set to the hostname of your OCP instance, but this can be any value really. | +| Port | Set to **27000** | + + +The other values can be left at their defaults. Finally, click **Generate** and download the license file to your home directory as `entitlement.lic`. + +!!! note + For more information about how to access the IBM License Key Center review the [getting started documentation](https://www.ibm.com/support/pages/system/files/inline-files/GettingStartedEnglish_2020.pdf) available from the IBM support website. + +### OpenShift Cluster +You should already have a target OpenShift cluster ready to install Maximo Application suite into. If you do not already have one then refer to the [OpenShift Container Platform installation overview](https://docs.openshift.com/container-platform/4.15/installing/index.html). + +The CLI also supports OpenShift provisioning in many hyperscaler providers: + +- [AWS](../commands/provision-rosa.md) +- [IBM Cloud](../commands/provision-roks.md) +- [IBM DevIT FYRE (Internal)](../commands/provision-fyre.md) + + +### Operator Catalog Selection +If you have not already determined the catalog version for your installation, refer to the information in the [Operator Catalog](../catalogs/index.md) topic, or contact IBM Support for guidance. + + +Interactive Install +------------------------------------------------------------------------------- +Regardless of whether you are running a connected or disconnected installation, simply run the `mas aiservice-install` command and follow the prompts, the basic structure of the interactive flow is described below. + +We will need the `entitlement.lic` file to perform the installation so we will mount your home directory into the running container. When prompted you will be able to set license file to `/mnt/home/entitlement.lic` - This is a prerequisite step, required only when `sls` has not been installed previously. + +```bash +docker run -ti --rm -v ~:/mnt/home quay.io/ibmmas/cli:@@CLI_LATEST_VERSION@@ mas aiservice-install +``` + +The interactive install will guide you through a series of questioned designed to help you arrive at the best configuration for your scenario, it can be broken down as below: + +
+ + +

If you are not already connected to an OpenShift cluster you will be prompted to provide the server URL & token to make a new connection. If you are already connected to a cluster you will be given the option to change to another cluster

+

You will be presented with a table of available catalogs with information about the different releases of MAS

+

Confirm that you accept the IBM Maximo Application Suite license terms

+
+ +

MAS requires both a `ReadWriteMany` and a `ReadWriteOnce` capable storage class to be available in the cluster. The installer has the ability to recognize certain storage class providers and will default to the most appropriate storage class in these cases:

+
    +
  • IBMCloud Storage (ibmc-block-gold & ibmc-file-gold-gid)
  • +
  • OpenShift Container Storage (ocs-storagecluster-ceph-rbd & ocs-storagecluster-cephfs)
  • +
  • External OpenShift Container Storage (ocs-external-storagecluster-ceph-rbd & ocs-external-storagecluster-cephfs)
  • +
  • NFS Client (nfs-client)
  • +
  • Azure Managed Storage (managed-premium & azurefiles-premium)
  • +
  • AWS Storage (gp3-cs & efs)
  • +
+

The names in brackets represent the `ReadWriteOnce` and `ReadWriteMany` class that will be used, in the case of NFS the same storage class will be used for both `ReadWriteOnce` and `ReadWriteMany` volumes. Even when a recognized storage provider is detected you will be provided with the option to select your own storages classes if you wish.

+

When selecting your own storage classes you will be presented with a list of those available and must select both a `ReadWriteMany` and a `ReadWriteOnce` storage class. Unfortunately there is no way for the install to verify that the storage class selected actually supports the appropriate access mode, refer to the documentation from the storage class provider to determine whether your storage class supports `ReadWriteOnce` and/or `ReadWriteMany`.

+
+ +

Provide the location of your license file, contact information, and IBM entitlement key (if you have set the IBM_ENTITLEMENT_KEY environment variable then this field will be pre-filled with that value already).

+
+ +

Provide the basic information about your Aiservice instance:

+
    +
  • Instance ID
  • +
  • Configure s3 storage, DB, RSL and tenant
  • +
  • Choose to install SLS, DB2, or DRO as a dependency, or opt out and provide alternative information including connection URL and token.
  • +
  • Operational Mode (production or non-production)
  • +
+
+ +

Before the install actually starts you will be presented with a summary of all your choices and a non-interactive command that will allow you to repeat the same installation without going through all the prompts again.

+
+ +
+
+ +Non-Interactive Install +------------------------------------------------------------------------------- +The following command will launch the MAS CLI container image, login to your OpenShift Cluster and start the install of MAS without triggering any prompts. This is how we install MAS in development hundreds of times every single week. + +```bash +IBM_ENTITLEMENT_KEY=xxx + +docker run -e IBM_ENTITLEMENT_KEY -e SUPERUSER_PASSWORD -ti --rm -v ~:/mnt/home quay.io/ibmmas/cli:@@CLI_LATEST_VERSION@@ bash -c " + oc login --token=sha256~xxxx --server=https://xxx && + mas aiservice-install \ + --mas-catalog-version @@MAS_LATEST_CATALOG@@ \ + --aibroker-instance-id aib1 \ + --aibroker-channel 9.1.x \ + \ + --ibm-entitlement-key '${IBM_ENTITLEMENT_KEY}' \ + --license-file /mnt/home/entitlement.lic \ + --uds-email myemail@email.com \ + --uds-firstname John \ + --uds-lastname Barnes \ + \ + --storage-rwo ibmc-block-gold \ + --storage-rwx ibmc-file-gold-gid \ + --storage-pipeline ibmc-file-gold-gid \ + --storage-accessmode ReadWriteMany \ + \ + --accept-license --no-confirm +``` + +More Information +------------------------------------------------------------------------------- +The install is designed to work on any OCP cluster, but has been specifically tested in these environments: + +- IBMCloud ROKS +- IBM DevIT FYRE (internal) + +The engine that performs all tasks is written in Ansible, you can directly use the same automation outside of this CLI if you wish. The code is open source and available in [ibm-mas/ansible-devops](https://github.com/ibm-mas/ansible-devops), the collection is also available to install directly from [Ansible Galaxy](https://galaxy.ansible.com/ibm/mas_devops), the install supports the following actions: + +- IBM Maximo Operator Catalog installation +- Required dependency installation: + - MongoDb (Community Edition) - only needed when want to install SLS. + - IBM Suite License Service (installed instance of SLS also can be used). + - IBM Data Reporter Operator + - Red Hat Certificate Manager + - Minio + - Mariadb + - db2 +- Aiservice installation + +
+ +The installation is performed inside your RedHat OpenShift cluster utilizing [Openshift Pipelines](https://cloud.redhat.com/learn/topics/ci-cd) + +> OpenShift Pipelines is a Kubernetes-native CI/CD solution based on Tekton. It builds on Tekton to provide a CI/CD experience through tight integration with OpenShift and Red Hat developer tools. OpenShift Pipelines is designed to run each step of the CI/CD pipeline in its own container, allowing each step to scale independently to meet the demands of the pipeline. diff --git a/image/cli/app-root/src/finalizer.py b/image/cli/app-root/src/finalizer.py index 5ebef286247..06fc10cf797 100644 --- a/image/cli/app-root/src/finalizer.py +++ b/image/cli/app-root/src/finalizer.py @@ -326,6 +326,12 @@ def getcp4dCompsVersions(): "namespace": f"mas-{instanceId}-visualinspection", "apiVersion": "apps.mas.ibm.com/v1", "kind": "VisualInspectionApp" + }, + "ibm-mas-aibroker": { + "deployment": "ibm-mas-aibroker-operator", + "namespace": f"mas-{instanceId}-aibroker", + "apiVersion": "apps.mas.ibm.com/v1", + "kind": "AiBrokerApp", } } @@ -341,7 +347,8 @@ def getcp4dCompsVersions(): "ibm-mas-optimizer": "S04PSB1R8DR", "ibm-mas-predict": "S04Q53TT5S5", "ibm-mas-visualinspection": "S04PUSAL2A0", - "ibm-mas-mobile": "S0507GG7V6K" + "ibm-mas-mobile": "S0507GG7V6K", + "ibm-mas-aibroker": "S04Q53TT5S5" } for productId in knownProductIds: diff --git a/image/cli/mascli/mas b/image/cli/mascli/mas index 1aa8c0168fa..2548f3a5487 100755 --- a/image/cli/mascli/mas +++ b/image/cli/mascli/mas @@ -212,6 +212,16 @@ case $1 in mas-cli install "$@" ;; + aiservice-install) + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >> $LOGFILE + echo "!! aiservice-install !!" >> $LOGFILE + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >> $LOGFILE + # Take the first parameter off (it will be "aiservice-install") + shift + # Run the new Python-based aiservice-install + mas-cli aiservice-install "$@" + ;; + update) echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >> $LOGFILE echo "!! update !!" >> $LOGFILE diff --git a/image/cli/masfvt/fvt-aiservice.yml b/image/cli/masfvt/fvt-aiservice.yml new file mode 100644 index 00000000000..384fce4faee --- /dev/null +++ b/image/cli/masfvt/fvt-aiservice.yml @@ -0,0 +1,41 @@ +--- +- hosts: localhost + any_errors_fatal: true + vars: + # Image Pull Policy + image_pull_policy: "{{ lookup('env', 'IMAGE_PULL_POLICY') }}" + # MAS Details + mas_app_channel_aibroker: "{{ lookup('env', 'MAS_APP_CHANNEL_AIBROKER') }}" + mas_workspace_id: "{{ lookup('env', 'MAS_WORKSPACE_ID') }}" + + aibroker_instance_id: "{{ lookup('env', 'AIBROKER_INSTANCE_ID') }}" + # FVT Configuration + fvt_image_registry: "{{ lookup('env', 'FVT_IMAGE_REGISTRY') }}" + fvt_artifactory_username: "{{ lookup('env', 'FVT_ARTIFACTORY_USERNAME') }}" + fvt_artifactory_token: "{{ lookup('env', 'FVT_ARTIFACTORY_TOKEN') }}" + fvt_digest_aibroker: "{{ lookup('env', 'FVT_DIGEST_AIBROKER') }}" + ivt_digest_core: "{{ lookup('env', 'IVT_DIGEST_CORE') }}" + # Pipeline Run Info + devops_build_number: "{{ lookup('env', 'DEVOPS_BUILD_NUMBER') | default('0', True) }}" + pipelinerun_name: "{{ lookup('env', 'PIPELINERUN_NAME') | default('mas-fvt-aibroker', True) }}-{{ devops_build_number }}" + pipelinerun_namespace: "{{ lookup('env', 'PIPELINERUN_NAMESPACE') | default('mas-' ~ aibroker_instance_id ~ '-pipelines', True) }}" + tasks: + - name: "Debug" + debug: + msg: + - "pipelinerun_name .................. {{ pipelinerun_name }}" + - "pipelinerun_namespace ............. {{ pipelinerun_namespace }}" + - "" + - "mas_app_channel_aibroker ........... {{ mas_app_channel_aibroker }}" + - "aibroker_instance_id ................... {{ aibroker_instance_id }}" + - "" + - "fvt_image_registry ................ {{ fvt_image_registry }}" + - "fvt_artifactory_username .......... {{ fvt_artifactory_username }}" + - "fvt_artifactory_token ............. {{ fvt_artifactory_token }}" + - "fvt_digest_aibroker ................ {{ fvt_digest_aibroker }}" + - "ivt_digest_core ................... {{ ivt_digest_core }}" + + - name: "Start fvt-aiservice pipeline" + kubernetes.core.k8s: + apply: true + template: templates/mas-fvt-aiservice.yml.j2 diff --git a/image/cli/masfvt/templates/mas-fvt-aiservice.yml.j2 b/image/cli/masfvt/templates/mas-fvt-aiservice.yml.j2 new file mode 100644 index 00000000000..acf09e47abd --- /dev/null +++ b/image/cli/masfvt/templates/mas-fvt-aiservice.yml.j2 @@ -0,0 +1,49 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: "{{ pipelinerun_name }}" + namespace: "{{ pipelinerun_namespace }}" + labels: + tekton.dev/pipeline: mas-fvt-aibroker +spec: + pipelineRef: + name: mas-fvt-aibroker + + serviceAccountName: pipeline + timeouts: + pipeline: "8h" + + params: + # Pull Policy + - name: image_pull_policy + value: "{{ image_pull_policy }}" + # MAS Info + - name: mas_app_channel_aibroker + value: "{{ mas_app_channel_aibroker }}" + - name: mas_instance_id + value: "{{ aibroker_instance_id }}" + - name: mas_workspace_id + value: "{{ mas_workspace_id }}" + # Registry + - name: fvt_image_registry + value: "{{ fvt_image_registry }}" + - name: fvt_artifactory_username + value: "{{ fvt_artifactory_username }}" + - name: fvt_artifactory_token + value: "{{ fvt_artifactory_token }}" + # Digests + - name: fvt_digest_aibroker + value: "{{ fvt_digest_aibroker }}" + - name: ivt_digest_core + value: "{{ ivt_digest_core }}" + + workspaces: + # The generated configuration files + - name: shared-configs + persistentVolumeClaim: + claimName: config-pvc + # PodTemplates configurations + - name: shared-pod-templates + secret: + secretName: pipeline-pod-templates diff --git a/python/src/mas-cli b/python/src/mas-cli index d0d62bd43da..fe0f5b8492b 100644 --- a/python/src/mas-cli +++ b/python/src/mas-cli @@ -15,6 +15,7 @@ from sys import argv from mas.cli import __version__ as VERSION from mas.cli.install.app import InstallApp +from mas.cli.aiservice.install.app import AiServiceInstallApp from mas.cli.update.app import UpdateApp from mas.cli.upgrade.app import UpgradeApp from mas.cli.uninstall.app import UninstallApp @@ -54,6 +55,9 @@ if __name__ == '__main__': if function == "install": app = InstallApp() app.install(argv[2:]) + elif function == "aiservice-install": + app = AiServiceInstallApp() + app.install(argv[2:]) elif function == "uninstall": app = UninstallApp() app.uninstall(argv[2:]) diff --git a/python/src/mas/cli/aiservice/install/__init__.py b/python/src/mas/cli/aiservice/install/__init__.py new file mode 100644 index 00000000000..07779332557 --- /dev/null +++ b/python/src/mas/cli/aiservice/install/__init__.py @@ -0,0 +1,11 @@ +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +from ...cli import BaseApp # noqa: F401 diff --git a/python/src/mas/cli/aiservice/install/app.py b/python/src/mas/cli/aiservice/install/app.py new file mode 100644 index 00000000000..4fb9b68ac76 --- /dev/null +++ b/python/src/mas/cli/aiservice/install/app.py @@ -0,0 +1,810 @@ +#!/usr/bin/env python +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +import logging +from sys import exit +from os import path, getenv +import re +import calendar + +from openshift.dynamic.exceptions import NotFoundError + +from prompt_toolkit import prompt, print_formatted_text, HTML +from prompt_toolkit.completion import WordCompleter + +from tabulate import tabulate + +from halo import Halo + +from ...cli import BaseApp +from .argBuilder import aiServiceInstallArgBuilderMixin +from .argParser import aiServiceinstallArgParser +from .summarizer import aiServiceInstallSummarizerMixin +from .params import requiredParams, optionalParams + +from ...install.catalogs import supportedCatalogs + +# AiService relies on SLS, which in turn depends on MongoDB. +# SLS will utilize the shared MongoDB resource that would be used by MAS if it were deployed within the same OpenShift cluster. +# AiService utilizes two distinct databases: DB2 is employed by the AiBroker component, while MariaDB supports OpenDataHub (ODH). +# By default, AiService will deploy DB2 within the same namespace as MAS (db2u), but it will be configured as a separate DB2 instance. + +from ...install.settings.mongodbSettings import MongoDbSettingsMixin +from ...install.settings.db2Settings import Db2SettingsMixin +from ...install.settings.additionalConfigs import AdditionalConfigsMixin + +from mas.cli.validators import ( + InstanceIDFormatValidator, + StorageClassValidator +) + +from mas.devops.ocp import createNamespace, getStorageClasses +from mas.devops.mas import ( + getCurrentCatalog, + getDefaultStorageClasses +) +from mas.devops.sls import findSLSByNamespace +from mas.devops.data import getCatalog +from mas.devops.tekton import ( + installOpenShiftPipelines, + updateTektonDefinitions, + preparePipelinesNamespace, + prepareInstallSecrets, + testCLI, + launchInstallPipelineForAiservice +) + +logger = logging.getLogger(__name__) + + +def logMethodCall(func): + def wrapper(self, *args, **kwargs): + logger.debug(f">>> InstallApp.{func.__name__}") + result = func(self, *args, **kwargs) + logger.debug(f"<<< InstallApp.{func.__name__}") + return result + return wrapper + + +class AiServiceInstallApp(BaseApp, aiServiceInstallArgBuilderMixin, aiServiceInstallSummarizerMixin, MongoDbSettingsMixin, Db2SettingsMixin, AdditionalConfigsMixin): + @logMethodCall + def processCatalogChoice(self) -> list: + self.catalogDigest = self.chosenCatalog["catalog_digest"] + self.catalogMongoDbVersion = self.chosenCatalog["mongo_extras_version_default"] + applications = { + "Aibroker": "mas_aibroker_version", + } + + self.catalogReleases = {} + self.catalogTable = [] + + # Dynamically fetch the channels from the chosen catalog + # based on mas core + for channel in self.chosenCatalog["mas_core_version"]: + # {"9.1-feature": "9.1.x-feature"} + self.catalogReleases.update({channel.replace('.x', ''): channel}) + + # Generate catalogTable + for application, key in applications.items(): + # Add 9.1-feature channel based off 9.0 to those apps that have not onboarded yet + tempChosenCatalog = self.chosenCatalog[key].copy() + if '9.1.x-feature' not in tempChosenCatalog: + tempChosenCatalog.update({"9.1.x-feature": tempChosenCatalog["9.0.x"]}) + + self.catalogTable.append({"": application} | {key.replace(".x", ""): value for key, value in sorted(tempChosenCatalog.items(), reverse=True)}) + + if self.architecture == "s390x": + summary = [ + "", + "Catalog Details", + f"Catalog Image: icr.io/cpopen/ibm-maximo-operator-catalog:{self.getParam('mas_catalog_version')}", + f"Catalog Digest: {self.catalogDigest}", + f"MAS Releases: {', '.join(sorted(self.catalogReleases, reverse=True))}", + f"MongoDb: {self.catalogMongoDbVersion}", + ] + else: + summary = [ + "", + "Catalog Details", + f"Catalog Image: icr.io/cpopen/ibm-maximo-operator-catalog:{self.getParam('mas_catalog_version')}", + f"Catalog Digest: {self.catalogDigest}", + f"MAS Releases: {', '.join(sorted(self.catalogReleases, reverse=True))}", + f"MongoDb: {self.catalogMongoDbVersion}", + ] + + return summary + + @logMethodCall + def configAibroker(self): + self.printH1("Configure Aibroker Instance") + self.printDescription([ + "Instance ID restrictions:", + " - Must be 3-12 characters long", + " - Must only use lowercase letters, numbers, and hypen (-) symbol", + " - Must start with a lowercase letter", + " - Must end with a lowercase letter or a number" + ]) + self.promptForString("Instance ID", "aibroker_instance_id", validator=InstanceIDFormatValidator()) + + if self.slsMode == 2 and not self.getParam("sls_namespace"): + self.setParam("sls_namespace", f"mas-{self.getParam('aibroker_instance_id')}-sls") + + self.configOperationMode() + + @logMethodCall + def interactiveMode(self, simplified: bool, advanced: bool) -> None: + # Interactive mode + self.interactiveMode = True + + self.storageClassProvider = "custom" + self.installAssist = False + self.installIoT = False + self.installMonitor = False + self.installManage = False + self.installPredict = False + self.installInspection = False + self.installOptimizer = False + self.installFacilities = False + self.installAiBroker = True + self.deployCP4D = False + self.db2SetAffinity = False + self.db2SetTolerations = False + self.slsLicenseFileLocal = None + + if simplified: + self.showAdvancedOptions = False + elif advanced: + self.showAdvancedOptions = True + else: + self.chooseInstallFlavour() + + # Catalog + self.configCatalog() + if not self.devMode: + self.validateCatalogSource() + self.licensePrompt() + + # Storage Classes + self.configStorageClasses() + + # Licensing (SLS and DRO) + self.configSLS() + self.configDRO() + self.configICRCredentials() + + self.configCertManager() + self.configAibroker() + if self.devMode: + self.configAppChannel("aibroker") + + self.aibrokerSettings() + + # Dependencies + self.configMongoDb() + self.setDB2DefaultSettings() + + @logMethodCall + def nonInteractiveMode(self) -> None: + self.interactiveMode = False + + # Set defaults + # --------------------------------------------------------------------- + # Unless a config file named "mongodb-system.yaml" is provided via the additional configs mechanism we will be installing a new MongoDb instance + self.setParam("mongodb_action", "install") + + self.storageClassProvider = "custom" + self.installAssist = False + self.installIoT = False + self.installMonitor = False + self.installManage = False + self.installPredict = False + self.installInspection = False + self.installFacilities = False + self.installOptimizer = False + self.installAiBroker = True + self.deployCP4D = False + self.db2SetAffinity = False + self.db2SetTolerations = False + self.slsLicenseFileLocal = None + + self.approvals = { + "approval_aibroker": {"id": "app-cfg-aibroker"}, # After Aibroker workspace has been configured + } + + self.setDB2DefaultSettings() + + for key, value in vars(self.args).items(): + # These fields we just pass straight through to the parameters and fail if they are not set + if key in requiredParams: + if value is None: + self.fatalError(f"{key} must be set") + self.setParam(key, value) + + # These fields we just pass straight through to the parameters + elif key in optionalParams: + if value is not None: + self.setParam(key, value) + + elif key == "non_prod": + if not value: + self.operationalMode = 1 + self.setParam("environment_type", "production") + else: + self.operationalMode = 2 + self.setParam("mas_annotations", "mas.ibm.com/operationalMode=nonproduction") + self.setParam("environment_type", "non-production") + + elif key == "additional_configs": + self.localConfigDir = value + # If there is a file named mongodb-system.yaml we will use this as a BYO MongoDB datasource + if self.localConfigDir is not None and path.exists(path.join(self.localConfigDir, "mongodb-system.yaml")): + self.setParam("mongodb_action", "byo") + self.setParam("sls_mongodb_cfg_file", "/workspace/additional-configs/mongodb-system.yaml") + + elif key == "pod_templates": + # For the named configurations we will convert into the path + if value in ["best-effort", "guaranteed"]: + self.setParam("mas_pod_templates_dir", path.join(self.templatesDir, "pod-templates", value)) + else: + self.setParam("mas_pod_templates_dir", value) + + # We check for both None and "" values for the application channel parameters + # value = None means the parameter wasn't set at all + # value = "" means the paramerter was explicitly set to "don't install this application" + elif key == "aibroker_channel": + if value is not None and value != "": + self.setParam("mas_app_channel_aibroker", value) + self.installAiBroker = True + + # Manage advanced settings that need extra processing + elif key == "mas_app_settings_server_bundle_size": + if value is not None: + self.setParam(key, value) + if value in ["jms", "snojms"]: + self.setParam("mas_app_settings_persistent_volumes_flag", "true") + + # MongoDB + elif key == "mongodb_namespace": + if value is not None and value != "": + self.setParam(key, value) + self.setParam("sls_mongodb_cfg_file", f"/workspace/configs/mongo-{value}.yml") + + # SLS + elif key == "license_file": + if value is not None and value != "": + self.slsLicenseFileLocal = value + self.setParam("sls_action", "install") + elif key == "dedicated_sls": + if value: + self.setParam("sls_namespace", f"mas-{self.args.aibroker_instance_id}-sls") + + # These settings are used by the CLI rather than passed to the PipelineRun + elif key == "storage_accessmode": + if value is None: + self.fatalError(f"{key} must be set") + self.pipelineStorageAccessMode = value + elif key == "storage_pipeline": + if value is None: + self.fatalError(f"{key} must be set") + self.pipelineStorageClass = value + + elif key.startswith("approval_"): + if key not in self.approvals: + raise KeyError(f"{key} is not a supported approval workflow ID: {self.approvals.keys()}") + + if value != "": + valueParts = value.split(":") + if len(valueParts) != 3: + self.fatalError(f"Unsupported format for {key} ({value}). Expected MAX_RETRIES:RETRY_DELAY:IGNORE_FAILURE") + else: + try: + self.approvals[key]["maxRetries"] = int(valueParts[0]) + self.approvals[key]["retryDelay"] = int(valueParts[1]) + self.approvals[key]["ignoreFailure"] = bool(valueParts[2]) + except ValueError: + self.fatalError(f"Unsupported format for {key} ({value}). Expected int:int:boolean") + + # Arguments that we don't need to do anything with + elif key in ["accept_license", "dev_mode", "skip_pre_check", "skip_grafana_install", "no_confirm", "no_wait_for_pvc", "help", "advanced", "simplified"]: + pass + + elif key == "manual_certificates": + if value is not None: + self.setParam("mas_manual_cert_mgmt", True) + self.manualCertsDir = value + else: + self.setParam("mas_manual_cert_mgmt", False) + self.manualCertsDir = None + + elif key == "enable_ipv6": + self.setParam("enable_ipv6", True) + + # Fail if there's any arguments we don't know how to handle + else: + print(f"Unknown option: {key} {value}") + self.fatalError(f"Unknown option: {key} {value}") + + # Load the catalog information + self.chosenCatalog = getCatalog(self.getParam("mas_catalog_version")) + + # License file is only optional for existing SLS instance + if self.slsLicenseFileLocal is None: + if self.getParam("install_sls_aiservice") != "false": + self.fatalError("--license-file must be set for new SLS install") + + # Once we've processed the inputs, we should validate the catalog source & prompt to accept the license terms + if not self.devMode: + self.validateCatalogSource() + self.licensePrompt() + + @logMethodCall + def install(self, argv): + """ + Install Aiservice + """ + args = aiServiceinstallArgParser.parse_args(args=argv) + + # We use the presence of --mas-instance-id to determine whether + # the CLI is being started in interactive mode or not + instanceId = args.aibroker_instance_id + + # Properties for arguments that control the behavior of the CLI + self.noConfirm = args.no_confirm + self.waitForPVC = not args.no_wait_for_pvc + self.licenseAccepted = args.accept_license + self.devMode = args.dev_mode + + # Set image_pull_policy of the CLI in interactive mode + if args.image_pull_policy and args.image_pull_policy != "": + self.setParam("image_pull_policy", args.image_pull_policy) + + self.approvals = {} + + # Store all args + self.args = args + + # These flags work for setting params in both interactive and non-interactive modes + if args.skip_pre_check: + self.setParam("skip_pre_check", "true") + + if instanceId is None: + self.printH1("Set Target OpenShift Cluster") + # Connect to the target cluster + self.connect() + else: + logger.debug("Aiservice instance ID is set, so we assume already connected to the desired OCP") + self.lookupTargetArchitecture() + + if self.dynamicClient is None: + print_formatted_text(HTML("Error: The Kubernetes dynamic Client is not available. See log file for details")) + exit(1) + + # Perform a check whether the cluster is set up for airgap install, this will trigger an early failure if the cluster is using the now + # deprecated MaximoApplicationSuite ImageContentSourcePolicy instead of the new ImageDigestMirrorSet + self.isAirgap() + + # Configure the installOptions for the appropriate architecture + self.catalogOptions = supportedCatalogs[self.architecture] + + # Basic settings before the user provides any input + self.configICR() + self.deployCP4D = False + + # UDS install has not been supported since the January 2024 catalog update + self.setParam("uds_action", "install-dro") + + # User must either provide the configuration via numerous command line arguments, or the interactive prompts + if instanceId is None: + self.interactiveMode(simplified=args.simplified, advanced=args.advanced) + else: + self.nonInteractiveMode() + + # Set up the sls license file + self.slsLicenseFile() + + # Show a summary of the installation configuration + self.printH1("Non-Interactive Install Command") + self.printDescription([ + "Save and re-use the following script to re-run this install without needing to answer the interactive prompts again", + "", + self.buildCommand() + ]) + + self.displayInstallSummary() + + if not self.noConfirm: + print() + self.printDescription([ + "Please carefully review your choices above, correcting mistakes now is much easier than after the install has begun" + ]) + continueWithInstall = self.yesOrNo("Proceed with these settings") + + # Prepare the namespace and launch the installation pipeline + if self.noConfirm or continueWithInstall: + self.createTektonFileWithDigest() + + self.printH1("Launch Install") + pipelinesNamespace = f"mas-{self.getParam('aibroker_instance_id')}-pipelines" + + if not self.noConfirm: + self.printDescription(["If you are using storage classes that utilize 'WaitForFirstConsumer' binding mode choose 'No' at the prompt below"]) + wait = self.yesOrNo("Wait for PVCs to bind") + else: + wait = False + + with Halo(text='Validating OpenShift Pipelines installation', spinner=self.spinner) as h: + installOpenShiftPipelines(self.dynamicClient) + h.stop_and_persist(symbol=self.successIcon, text="OpenShift Pipelines Operator is installed and ready to use") + + with Halo(text=f'Preparing namespace ({pipelinesNamespace})', spinner=self.spinner) as h: + createNamespace(self.dynamicClient, pipelinesNamespace) + preparePipelinesNamespace( + dynClient=self.dynamicClient, + instanceId=self.getParam("aibroker_instance_id"), + storageClass=self.pipelineStorageClass, + accessMode=self.pipelineStorageAccessMode, + waitForBind=wait, + configureRBAC=(self.getParam("service_account_name") == "") + ) + prepareInstallSecrets( + dynClient=self.dynamicClient, + instanceId=self.getParam("aibroker_instance_id"), + slsLicenseFile=self.slsLicenseFileSecret, + additionalConfigs=self.additionalConfigsSecret, + podTemplates=self.podTemplatesSecret, + certs=self.certsSecret + ) + + self.setupApprovals(pipelinesNamespace) + + h.stop_and_persist(symbol=self.successIcon, text=f"Namespace is ready ({pipelinesNamespace})") + + with Halo(text='Testing availability of MAS CLI image in cluster', spinner=self.spinner) as h: + testCLI() + h.stop_and_persist(symbol=self.successIcon, text="MAS CLI image deployment test completed") + + with Halo(text=f'Installing latest Tekton definitions (v{self.version})', spinner=self.spinner) as h: + updateTektonDefinitions(pipelinesNamespace, self.tektonDefsPath) + h.stop_and_persist(symbol=self.successIcon, text=f"Latest Tekton definitions are installed (v{self.version})") + + with Halo(text=f"Submitting PipelineRun for {self.getParam('aibroker_instance_id')} install", spinner=self.spinner) as h: + pipelineURL = launchInstallPipelineForAiservice(dynClient=self.dynamicClient, params=self.params) + if pipelineURL is not None: + h.stop_and_persist(symbol=self.successIcon, text=f"PipelineRun for {self.getParam('aibroker_instance_id')} install submitted") + print_formatted_text(HTML(f"\nView progress:\n {pipelineURL}\n")) + else: + h.stop_and_persist(symbol=self.failureIcon, text=f"Failed to submit PipelineRun for {self.getParam('aibroker_instance_id')} install, see log file for details") + print() + + @logMethodCall + def setupApprovals(self, namespace: str) -> None: + """ + Ensure the supported approval configmaps are in the expected state for the start of the run: + - not present (if approval is not required) + - present with the chosen state field initialized to "" + """ + for approval in self.approvals.values(): + if "maxRetries" in approval: + # Enable this approval workload + logger.debug(f"Approval workflow for {approval['id']} will be enabled during install ({approval['maxRetries']} / {approval['retryDelay']}s / {approval['ignoreFailure']})") + self.initializeApprovalConfigMap(namespace, approval['id'], True, approval['maxRetries'], approval['retryDelay'], approval['ignoreFailure']) + + @logMethodCall + def chooseInstallFlavour(self) -> None: + # We don't have any configuration as Advanced options right now in Aibroker settings + # we can remove this chooseInstallFlavour - if we want... + self.printH1("Choose Install Mode") + self.printDescription([ + "There are two flavours of the interactive install to choose from: Simplified and Advanced. The simplified option will present fewer dialogs, but you lose the ability to configure the following aspects of the installation:", + " - Configure dedicated License (AppPoints)" + ]) + self.showAdvancedOptions = self.yesOrNo("Show advanced installation options") + + def aibrokerSettings(self) -> None: + if self.installAiBroker: + self.printH2("AI Service Settings - Storage, WatsonX, MariaDB details") + self.printDescription(["Customise AI Broker details"]) + self.promptForString("Storage provider", "mas_aibroker_storage_provider") + self.promptForString("Storage access key", "mas_aibroker_storage_accesskey") + self.promptForString("Storage secret key", "mas_aibroker_storage_secretkey", isPassword=True) + self.promptForString("Storage host", "mas_aibroker_storage_host") + self.promptForString("Storage port", "mas_aibroker_storage_port") + self.promptForString("Storage ssl", "mas_aibroker_storage_ssl") + self.promptForString("Storage region", "mas_aibroker_storage_region") + self.promptForString("Storage pipelines bucket", "mas_aibroker_storage_pipelines_bucket") + self.promptForString("Storage tenants bucket", "mas_aibroker_storage_tenants_bucket") + self.promptForString("Storage templates bucket", "mas_aibroker_storage_templates_bucket") + + self.promptForString("Watsonxai api key", "mas_aibroker_watsonxai_apikey", isPassword=True) + self.promptForString("Watsonxai machine learning url", "mas_aibroker_watsonxai_url") + self.promptForString("Watsonxai project id", "mas_aibroker_watsonxai_project_id") + + self.promptForString("Database host", "mas_aibroker_db_host") + self.promptForString("Database port", "mas_aibroker_db_port") + self.promptForString("Database user", "mas_aibroker_db_user") + self.promptForString("Database name", "mas_aibroker_db_database") + self.promptForString("Database Secretname", "mas_aibroker_db_secret_name", isPassword=True) + self.promptForString("Database password", "mas_aibroker_db_secret_value", isPassword=True) + + if self.getParam("mas_app_channel_aibroker") != "9.0.x": + self.promptForString("Mariadb username", "mariadb_user") + self.promptForString("Mariadb password", "mariadb_password", isPassword=True) + self.promptForString("Tenant entitlement type", "tenant_entitlement_type") + self.promptForString("Tenant start date", "tenant_entitlement_start_date") + self.promptForString("Tenant end date", "tenant_entitlement_end_date") + self.promptForString("S3 bucket prefix", "mas_aibroker_s3_bucket_prefix") + self.promptForString("S3 endpoint url", "mas_aibroker_s3_endpoint_url") + self.promptForString("S3 bucket prefix (tenant level)", "mas_aibroker_tenant_s3_bucket_prefix") + self.promptForString("S3 region (tenant level)", "mas_aibroker_tenant_s3_region") + self.promptForString("S3 endpoint url (tenant level)", "mas_aibroker_tenant_s3_endpoint_url") + self.promptForString("S3 access key (tenant level)", "mas_aibroker_tenant_s3_access_key", isPassword=True) + self.promptForString("S3 secret key (tenant level)", "mas_aibroker_tenant_s3_secret_key", isPassword=True) + self.promptForString("RSL url", "rsl_url") + self.promptForString("ORG Id of RSL", "rsl_org_id") + self.promptForString("Token for RSL", "rsl_token", isPassword=True) + self.yesOrNo("Install minio", "install_minio_aiservice") + if self.getParam("install_minio_aiservice") == "true": + self.promptForString("minio root username", "minio_root_user") + self.promptForString("minio root password", "minio_root_password", isPassword=True) + self.yesOrNo("Install SLS", "install_sls_aiservice") + if self.getParam("install_sls_aiservice") != "true": + self.promptForString("SLS secret name", "mas_aibroker_sls_secret_name") + self.promptForString("SLS registration key", "mas_aibroker_sls_registration_key") + self.promptForString("SLS URL", "mas_aibroker_sls_url") + self.promptForString("SLS CA certificate", "mas_aibroker_sls_ca_cert") + self.yesOrNo("Install DRO", "install_dro_aiservice") + if self.getParam("install_dro_aiservice") != "true": + self.promptForString("DRO secret name", "mas_aibroker_dro_secret_name") + self.promptForString("DRO API key", "mas_aibroker_dro_api_key") + self.promptForString("DRO URL", "mas_aibroker_dro_url") + self.promptForString("DRO CA certificate", "mas_aibroker_dro_ca_cert") + self.yesOrNo("Install DB2", "install_db2_aiservice") + if self.getParam("install_db2_aiservice") != "true": + self.promptForString("DB2 username", "mas_aibroker_db2_username") + self.promptForString("DB2 password", "mas_aibroker_db2_password") + self.promptForString("DB2 JDBC URL", "mas_aibroker_db2_jdbc_url") + self.promptForString("DB2 SSL enabled (yes/no)", "mas_aibroker_db2_ssl_enabled") + self.promptForString("DB2 CA certificate", "mas_aibroker_db2_ca_cert") + # self.promptForString("Environment type", "environment_type") + + # These are all candidates to centralise in a new mixin used by both install and aiservice-install + + @logMethodCall + def configICR(self): + if self.devMode: + self.setParam("mas_icr_cp", getenv("MAS_ICR_CP", "docker-na-public.artifactory.swg-devops.com/wiotp-docker-local")) + self.setParam("mas_icr_cpopen", getenv("MAS_ICR_CPOPEN", "docker-na-public.artifactory.swg-devops.com/wiotp-docker-local/cpopen")) + self.setParam("sls_icr_cpopen", getenv("SLS_ICR_CPOPEN", "docker-na-public.artifactory.swg-devops.com/wiotp-docker-local/cpopen")) + else: + self.setParam("mas_icr_cp", getenv("MAS_ICR_CP", "cp.icr.io/cp")) + self.setParam("mas_icr_cpopen", getenv("MAS_ICR_CPOPEN", "icr.io/cpopen")) + self.setParam("sls_icr_cpopen", getenv("SLS_ICR_CPOPEN", "icr.io/cpopen")) + + @logMethodCall + def configICRCredentials(self): + self.printH1("Configure IBM Container Registry") + self.promptForString("IBM entitlement key", "ibm_entitlement_key", isPassword=True) + if self.devMode: + self.promptForString("Artifactory username", "artifactory_username") + self.promptForString("Artifactory token", "artifactory_token", isPassword=True) + + @logMethodCall + def configCertManager(self): + # Only install of Red Hat Cert-Manager has been supported since the January 2025 catalog update + self.setParam("cert_manager_provider", "redhat") + self.setParam("cert_manager_action", "install") + + def formatCatalog(self, name: str) -> str: + # Convert "v9-241107-amd64" into "November 2024 Update (v9-241107-amd64)" + date = name.split("-")[1] + month = int(date[2:4]) + monthName = calendar.month_name[month] + year = date[:2] + return f" - {monthName} 20{year} Update\n https://ibm-mas.github.io/cli/catalogs/{name}" + + @logMethodCall + def configCatalog(self): + self.printH1("IBM Maximo Operator Catalog Selection") + if self.devMode: + self.promptForString("Select catalog source", "mas_catalog_version", default="v9-master-amd64") + else: + catalogInfo = getCurrentCatalog(self.dynamicClient) + + if catalogInfo is None: + self.printDescription([ + "The catalog you choose dictates the version of everything that is installed, with Maximo Application Suite this is the only version you need to remember; all other versions are determined by this choice.", + "Older catalogs can still be used, but we recommend using an older version of the CLI that aligns with the release date of the catalog.", + " - Learn more: https://ibm-mas.github.io/cli/catalogs/", + "" + ]) + print("Supported Catalogs:") + for catalog in self.catalogOptions: + catalogString = self.formatCatalog(catalog) + print_formatted_text(HTML(f"{catalogString}")) + print() + + catalogCompleter = WordCompleter(self.catalogOptions) + catalogSelection = self.promptForString("Select catalog", completer=catalogCompleter) + self.setParam("mas_catalog_version", catalogSelection) + else: + self.printDescription([ + f"The IBM Maximo Operator Catalog is already installed in this cluster ({catalogInfo['catalogId']}). If you wish to install MAS using a newer version of the catalog please first update the catalog using mas update." + ]) + self.setParam("mas_catalog_version", catalogInfo["catalogId"]) + + self.chosenCatalog = getCatalog(self.getParam("mas_catalog_version")) + catalogSummary = self.processCatalogChoice() + self.printDescription(catalogSummary) + self.printDescription([ + "", + "Two types of release are available:", + " - GA releases of Maximo Application Suite are supported under IBM's standard 3+1+3 support lifecycle policy.", + " - 'Feature' releases allow early access to new features for evaluation in non-production environments and are only supported through to the next GA release.", + "" + ]) + + print(tabulate(self.catalogTable, headers="keys", tablefmt="simple_grid")) + + releaseCompleter = WordCompleter(sorted(self.catalogReleases, reverse=True)) + releaseSelection = self.promptForString("Select release", completer=releaseCompleter) + + self.setParam("mas_app_channel_aibroker", self.catalogReleases[releaseSelection]) + + @logMethodCall + def validateCatalogSource(self): + catalogsAPI = self.dynamicClient.resources.get(api_version="operators.coreos.com/v1alpha1", kind="CatalogSource") + try: + catalog = catalogsAPI.get(name="ibm-operator-catalog", namespace="openshift-marketplace") + catalogDisplayName = catalog.spec.displayName + + m = re.match(r".+(?Pv[89]-(?P[0-9]+)-amd64)", catalogDisplayName) + if m: + # catalogId = v8-yymmdd-amd64 + # catalogVersion = yymmdd + catalogId = m.group("catalogId") + elif re.match(r".+v8-amd64", catalogDisplayName): + catalogId = "v8-amd64" + else: + self.fatalError(f"IBM Maximo Operator Catalog is already installed on this cluster. However, it is not possible to identify its version. If you wish to install a new MAS instance using the {self.getParam('mas_catalog_version')} catalog please first run 'mas update' to switch to this catalog, this will ensure the appropriate actions are performed as part of the catalog update") + + if catalogId != self.getParam("mas_catalog_version"): + self.fatalError(f"IBM Maximo Operator Catalog {catalogId} is already installed on this cluster, if you wish to install a new MAS instance using the {self.getParam('mas_catalog_version')} catalog please first run 'mas update' to switch to this catalog, this will ensure the appropriate actions are performed as part of the catalog update") + except NotFoundError: + # There's no existing catalog installed + pass + + # TODO: update licenses for aiservice 9.1.x + @logMethodCall + def licensePrompt(self): + if not self.licenseAccepted: + self.printH1("License Terms") + self.printDescription([ + "To continue with the installation, you must accept the license terms:", + self.licenses[f"aibroker-{self.getParam('mas_app_channel_aibroker')}"] + ]) + + if self.noConfirm: + self.fatalError("You must accept the license terms with --accept-license when using the --no-confirm flag") + else: + if not self.yesOrNo("Do you accept the license terms"): + exit(1) + + @logMethodCall + def configStorageClasses(self): + self.printH1("Configure Storage Class Usage") + self.printDescription([ + "Maximo Application Suite and it's dependencies require storage classes that support ReadWriteOnce (RWO) and ReadWriteMany (RWX) access modes:", + " - ReadWriteOnce volumes can be mounted as read-write by multiple pods on a single node.", + " - ReadWriteMany volumes can be mounted as read-write by multiple pods across many nodes.", + "" + ]) + defaultStorageClasses = getDefaultStorageClasses(self.dynamicClient) + if defaultStorageClasses.provider is not None: + print_formatted_text(HTML(f"Storage provider auto-detected: {defaultStorageClasses.providerName}")) + print_formatted_text(HTML(f" - Storage class (ReadWriteOnce): {defaultStorageClasses.rwo}")) + print_formatted_text(HTML(f" - Storage class (ReadWriteMany): {defaultStorageClasses.rwx}")) + self.storageClassProvider = defaultStorageClasses.provider + self.params["storage_class_rwo"] = defaultStorageClasses.rwo + self.params["storage_class_rwx"] = defaultStorageClasses.rwx + + overrideStorageClasses = False + if "storage_class_rwx" in self.params and self.params["storage_class_rwx"] != "": + overrideStorageClasses = not self.yesOrNo("Use the auto-detected storage classes") + + if "storage_class_rwx" not in self.params or self.params["storage_class_rwx"] == "" or overrideStorageClasses: + self.storageClassProvider = "custom" + + self.printDescription([ + "Select the ReadWriteOnce and ReadWriteMany storage classes to use from the list below:", + "Enter 'none' for the ReadWriteMany storage class if you do not have a suitable class available in the cluster, however this will limit what can be installed" + ]) + for storageClass in getStorageClasses(self.dynamicClient): + print_formatted_text(HTML(f" - {storageClass.metadata.name}")) + + self.params["storage_class_rwo"] = prompt(HTML('ReadWriteOnce (RWO) storage class '), validator=StorageClassValidator(), validate_while_typing=False) + self.params["storage_class_rwx"] = prompt(HTML('ReadWriteMany (RWX) storage class '), validator=StorageClassValidator(), validate_while_typing=False) + + # Configure storage class for pipeline PVC + # We prefer to use ReadWriteMany, but we can cope with ReadWriteOnce if necessary + if self.isSNO() or self.params["storage_class_rwx"] == "none": + self.pipelineStorageClass = self.getParam("storage_class_rwo") + self.pipelineStorageAccessMode = "ReadWriteOnce" + else: + self.pipelineStorageClass = self.getParam("storage_class_rwx") + self.pipelineStorageAccessMode = "ReadWriteMany" + + @logMethodCall + def configSLS(self) -> None: + self.printH1("Configure AppPoint Licensing") + self.printDescription( + [ + "By default the MAS instance will be configured to use a cluster-shared License, this provides a shared pool of AppPoints available to all MAS instances on the cluster.", + "", + ] + ) + + self.slsMode = 1 + self.slsLicenseFileLocal = None + + if self.showAdvancedOptions: + self.printDescription( + [ + "Alternatively you may choose to install using a dedicated license only available to this MAS instance.", + " 1. Install MAS with Cluster-Shared License (AppPoints)", + " 2. Install MAS with Dedicated License (AppPoints)", + ] + ) + self.slsMode = self.promptForInt("SLS Mode", default=1) + + if self.slsMode not in [1, 2]: + self.fatalError(f"Invalid selection: {self.slsMode}") + + if not (self.slsMode == 2 and not self.getParam("sls_namespace")): + sls_namespace = "ibm-sls" if self.slsMode == 1 else self.getParam("sls_namespace") + if findSLSByNamespace(sls_namespace, dynClient=self.dynamicClient): + print_formatted_text(HTML(f"SLS auto-detected: {sls_namespace}")) + print() + if not self.yesOrNo("Upload/Replace the license file"): + self.setParam("sls_action", "gencfg") + return + + self.slsLicenseFileLocal = self.promptForFile("License file", mustExist=True, envVar="SLS_LICENSE_FILE_LOCAL") + self.setParam("sls_action", "install") + + @logMethodCall + def configDRO(self) -> None: + self.promptForString("Contact e-mail address", "uds_contact_email") + self.promptForString("Contact first name", "uds_contact_firstname") + self.promptForString("Contact last name", "uds_contact_lastname") + + if self.showAdvancedOptions: + self.promptForString("IBM Data Reporter Operator (DRO) Namespace", "dro_namespace", default="redhat-marketplace") + + @logMethodCall + def configAppChannel(self, appId): + self.params[f"mas_app_channel_{appId}"] = prompt(HTML('Custom channel for Aibroker ')) + + @logMethodCall + def configOperationMode(self): + self.printH1("Configure Operational Mode") + self.printDescription([ + "Maximo Application Suite can be installed in a non-production mode for internal development and testing, this setting cannot be changed after installation:", + " - All applications, add-ons, and solutions have 0 (zero) installation AppPoints in non-production installations.", + " - These specifications are also visible in the metrics that are shared with IBM and in the product UI.", + "", + " 1. Production", + " 2. Non-Production" + ]) + self.operationalMode = self.promptForInt("Operational Mode", default=1) + if self.operationalMode == 1: + self.setParam("environment_type", "production") + else: + self.setParam("environment_type", "non-production") diff --git a/python/src/mas/cli/aiservice/install/argBuilder.py b/python/src/mas/cli/aiservice/install/argBuilder.py new file mode 100644 index 00000000000..aed12679c80 --- /dev/null +++ b/python/src/mas/cli/aiservice/install/argBuilder.py @@ -0,0 +1,232 @@ +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +import logging + +logger = logging.getLogger(__name__) + + +class aiServiceInstallArgBuilderMixin(): + def buildCommand(self) -> str: + # MAS Catalog Selection & Entitlement + # ----------------------------------------------------------------------------- + newline = " \\\n" + command = "export IBM_ENTITLEMENT_KEY=x\n" + if self.getParam('ibmcloud_apikey') != "": + command += "export IBMCLOUD_APIKEY=x\n" + if self.getParam('aws_access_key_id') != "": + command += "export AWS_ACCESS_KEY_ID=x\n" + if self.getParam('secret_access_key') != "": + command += "export SECRET_ACCESS_KEY=x\n" + if self.getParam('artifactory_username') != "": + command += "export ARTIFACTORY_USERNAME=x\nexport ARTIFACTORY_TOKEN=x\n" + + command += f"mas aiservice-install --mas-catalog-version {self.getParam('mas_catalog_version')}" + + # MAS Advanced Configuration + # ----------------------------------------------------------------------------- + + if self.localConfigDir is not None: + command += f" --additional-configs \"{self.localConfigDir}\"{newline}" + + # Storage + # ----------------------------------------------------------------------------- + command += f" --storage-class-rwo \"{self.getParam('storage_class_rwo')}\"" + command += f" --storage-class-rwx \"{self.getParam('storage_class_rwx')}\"{newline}" + command += f" --storage-pipeline \"{self.pipelineStorageClass}\"" + command += f" --storage-accessmode \"{self.pipelineStorageAccessMode}\"{newline}" + + # IBM Suite License Service + # ----------------------------------------------------------------------------- + if self.getParam("sls_namespace") and self.getParam("sls_namespace") != "ibm-sls": + if self.getParam("aibroker_instance_id") and self.getParam("sls_namespace") == f"mas-{self.getParam('mas_instance_id')}-sls": + command += " --dedicated-sls" + else: + command += f" --sls-namespace \"{self.getParam('sls_namespace')}\"" + if self.slsLicenseFileLocal: + command += f" --license-file \"{self.slsLicenseFileLocal}\"" + if self.getParam("sls_namespace") and self.getParam("sls_namespace") != "ibm-sls" or self.slsLicenseFileLocal: + command += newline + + # IBM Data Reporting Operator (DRO) + # ----------------------------------------------------------------------------- + command += f" --uds-email \"{self.getParam('uds_contact_email')}\"" + command += f" --uds-firstname \"{self.getParam('uds_contact_firstname')}\"" + command += f" --uds-lastname \"{self.getParam('uds_contact_lastname')}\"{newline}" + if self.getParam('dro_namespace') != "": + command += f" --dro-namespace \"{self.getParam('dro_namespace')}\"{newline}" + + # MongoDb Community Operator + # ----------------------------------------------------------------------------- + if self.getParam('mongodb_namespace') != "": + command += f" --mongodb-namespace \"{self.getParam('mongodb_namespace')}\"{newline}" + + # Aibroker Channel + # ----------------------------------------------------------------------------- + if self.installAiBroker: + command += f" --aibroker-channel \"{self.getParam('mas_app_channel_aibroker')}\"{newline}" + + # IBM Db2 Universal Operator + # ----------------------------------------------------------------------------- + if self.getParam('db2_action_system') == "install" or self.getParam('db2_action_manage') == "install" or self.getParam('db2_action_facilities') == "install": + if self.getParam('db2_action_system') == "install": + command += f" --db2-system{newline}" + if self.getParam('db2_action_manage') == "install": + command += f" --db2-manage{newline}" + if self.getParam('db2_action_facilities') == "install": + command += f" --db2-facilities{newline}" + + if self.getParam('db2_channel') != "": + command += f" --db2-channel \"{self.getParam('db2_channel')}\"{newline}" + if self.getParam('db2_namespace') != "": + command += f" --db2-namespace \"{self.getParam('db2_namespace')}\"{newline}" + + if self.getParam('db2_type') != "": + command += f" --db2-type \"{self.getParam('db2_type')}\"{newline}" + if self.getParam('db2_timezone') != "": + command += f" --db2-timezone \"{self.getParam('db2_timezone')}\"{newline}" + + if self.getParam('db2_affinity_key') != "": + command += f" --db2-affinity-key \"{self.getParam('db2_affinity_key')}\"{newline}" + if self.getParam('db2_affinity_value') != "": + command += f" --db2-affinity_value \"{self.getParam('db2_affinity_value')}\"{newline}" + + if self.getParam('db2_tolerate_key') != "": + command += f" --db2-tolerate-key \"{self.getParam('db2_tolerate_key')}\"{newline}" + if self.getParam('db2_tolerate_value') != "": + command += f" --db2-tolerate-value \"{self.getParam('db2_tolerate_value')}\"{newline}" + if self.getParam('db2_tolerate_effect') != "": + command += f" --db2-tolerate-effect \"{self.getParam('db2_tolerate_effect')}\"{newline}" + + if self.getParam('db2_cpu_requests') != "": + command += f" --db2-cpu-requests \"{self.getParam('db2_cpu_requests')}\"{newline}" + if self.getParam('db2_cpu_limits') != "": + command += f" --db2-cpu-limits \"{self.getParam('db2_cpu_limits')}\"{newline}" + + if self.getParam('db2_memory_requests') != "": + command += f" --db2-memory-requests \"{self.getParam('db2_memory_requests')}\"{newline}" + if self.getParam('db2_memory_limits') != "": + command += f" --db2-memory-limits \"{self.getParam('db2_memory_limits')}\"{newline}" + + if self.getParam('db2_backup_storage_size') != "": + command += f" --db2-backup-storage \"{self.getParam('db2_backup_storage_size')}\"{newline}" + if self.getParam('db2_data_storage_size') != "": + command += f" --db2-data-storage \"{self.getParam('db2_data_storage_size')}\"{newline}" + if self.getParam('db2_logs_storage_size') != "": + command += f" --db2-logs-storage \"{self.getParam('db2_logs_storage_size')}\"{newline}" + if self.getParam('db2_meta_storage_size') != "": + command += f" --db2-meta-storage \"{self.getParam('db2_meta_storage_size')}\"{newline}" + if self.getParam('db2_temp_storage_size') != "": + command += f" --db2-temp-storage \"{self.getParam('db2_temp_storage_size')}\"{newline}" + + # Development Mode + # ----------------------------------------------------------------------------- + if self.getParam('artifactory_username') != "": + command += f" --artifactory-username $ARTIFACTORY_USERNAME --artifactory-token $ARTIFACTORY_TOKEN{newline}" + + # Approvals + # ----------------------------------------------------------------------------- + if self.getParam('approval_aibroker') != "": + command += f" --approval-aibroker \"{self.getParam('approval_aibroker')}\"{newline}" + + # More Options + # ----------------------------------------------------------------------------- + if self.devMode: + command += f" --dev-mode{newline}" + if not self.waitForPVC: + command += f" --no-wait-for-pvc{newline}" + if self.getParam('skip_pre_check') is True: + command += f" --skip-pre-check{newline}" + if self.getParam('skip_grafana_install') is True: + command += f" --skip-grafana-install{newline}" + if self.getParam('image_pull_policy') != "": + command += f" --image-pull-policy {self.getParam('image_pull_policy')}{newline}" + if self.getParam('service_account_name') != "": + command += f" --service-account {self.getParam('service_account_name')}{newline}" + + # Aibroker Advanced Settings + # ----------------------------------------------------------------------------- + if self.getParam('mas_aibroker_storage_provider') != "": + command += f" --mas-aibroker-storage-provider \"{self.getParam('mas_aibroker_storage_provider')}\"{newline}" + if self.getParam('mas_aibroker_storage_accesskey') != "": + command += f" --mas-aibroker-storage-accesskey \"{self.getParam('mas_aibroker_storage_accesskey')}\"{newline}" + if self.getParam('mas_aibroker_storage_secretkey') != "": + command += f" --mas-aibroker-storage-secretkey \"{self.getParam('mas_aibroker_storage_secretkey')}\"{newline}" + if self.getParam('mas_aibroker_storage_host') != "": + command += f" --mas-aibroker-storage-host \"{self.getParam('mas_aibroker_storage_host')}\"{newline}" + if self.getParam('mas_aibroker_storage_port') != "": + command += f" --mas-aibroker-storage-port \"{self.getParam('mas_aibroker_storage_port')}\"{newline}" + if self.getParam('mas_aibroker_storage_ssl') != "": + command += f" --mas-aibroker-storage-ssl \"{self.getParam('mas_aibroker_storage_ssl')}\"{newline}" + if self.getParam('mas_aibroker_storage_region') != "": + command += f" --mas-aibroker-storage-region \"{self.getParam('mas_aibroker_storage_region')}\"{newline}" + if self.getParam('mas_aibroker_storage_pipelines_bucket') != "": + command += f" --mas-aibroker-storage-pipelines-bucket \"{self.getParam('mas_aibroker_storage_pipelines_bucket')}\"{newline}" + if self.getParam('mas_aibroker_storage_tenants_bucket') != "": + command += f" --mas-aibroker-storage-tenants-bucket \"{self.getParam('mas_aibroker_storage_tenants_bucket')}\"{newline}" + if self.getParam('mas_aibroker_storage_templates_bucket') != "": + command += f" --mas-aibroker-storage-templates-bucket \"{self.getParam('mas_aibroker_storage_templates_bucket')}\"{newline}" + if self.getParam('mas_aibroker_tenant_name') != "": + command += f" --mas-aibroker-tenant-name \"{self.getParam('mas_aibroker_tenant_name')}\"{newline}" + if self.getParam('mas_aibroker_watsonxai_apikey') != "": + command += f" --mas-aibroker-watsonxai-apikey \"{self.getParam('mas_aibroker_watsonxai_apikey')}\"{newline}" + if self.getParam('mas_aibroker_watsonxai_url') != "": + command += f" --mas-aibroker-watsonxai-url \"{self.getParam('mas_aibroker_watsonxai_url')}\"{newline}" + if self.getParam('mas_aibroker_watsonxai_project_id') != "": + command += f" --mas-aibroker-watsonxai-project-id \"{self.getParam('mas_aibroker_watsonxai_project_id')}\"{newline}" + if self.getParam('mas_aibroker_watsonx_action') != "": + command += f" --mas-aibroker-watsonx-action \"{self.getParam('mas_aibroker_watsonx_action')}\"{newline}" + if self.getParam('mas_aibroker_db_host') != "": + command += f" --mas-aibroker-db-host \"{self.getParam('mas_aibroker_db_host')}\"{newline}" + if self.getParam('mas_aibroker_db_port') != "": + command += f" --mas-aibroker-db-port \"{self.getParam('mas_aibroker_db_port')}\"{newline}" + if self.getParam('mas_aibroker_db_user') != "": + command += f" --mas-aibroker-db-user \"{self.getParam('mas_aibroker_db_user')}\"{newline}" + if self.getParam('mas_aibroker_db_database') != "": + command += f" --mas-aibroker-db-database \"{self.getParam('mas_aibroker_db_database')}\"{newline}" + if self.getParam('mas_aibroker_db_secret_name') != "": + command += f" --mas-aibroker-db-secret-name \"{self.getParam('mas_aibroker_db_secret_name')}\"{newline}" + if self.getParam('mas_aibroker_db_secret_key') != "": + command += f" --mas-aibroker-db-secret-key \"{self.getParam('mas_aibroker_db_secret_key')}\"{newline}" + if self.getParam('mas_aibroker_db_secret_value') != "": + command += f" --mas-aibroker-db-secret-value \"{self.getParam('mas_aibroker_db_secret_value')}\"{newline}" + if self.getParam('mariadb_user') != "": + command += f" --mariadb-user \"{self.getParam('mariadb_user')}\"{newline}" + if self.getParam('mariadb_password') != "": + command += f" --mariadb-password \"{self.getParam('mariadb_password')}\"{newline}" + if self.getParam('minio_root_user') != "": + command += f" --minio-root-user \"{self.getParam('minio_root_user')}\"{newline}" + if self.getParam('minio_root_password') != "": + command += f" --minio-root-password \"{self.getParam('minio_root_password')}\"{newline}" + if self.getParam('tenant_entitlement_type') != "": + command += f" --tenant-entitlement-type \"{self.getParam('tenant_entitlement_type')}\"{newline}" + if self.getParam('tenant_entitlement_start_date') != "": + command += f" --tenant-entitlement-start-date \"{self.getParam('tenant_entitlement_start_date')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --tenant-entitlement-end-date \"{self.getParam('tenant_entitlement_end_date')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-s3-bucket-prefix \"{self.getParam('mas_aibroker_s3_bucket_prefix')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-s3-endpoint-url \"{self.getParam('mas_aibroker_s3_endpoint_url')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-s3-region \"{self.getParam('mas_aibroker_s3_region')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-tenant-s3-bucket-prefix \"{self.getParam('mas_aibroker_tenant_s3_bucket_prefix')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-tenant-s3-region \"{self.getParam('mas_aibroker_tenant_s3_region')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-tenant-s3-endpoint-url \"{self.getParam('mas_aibroker_tenant_s3_endpoint_url')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-tenant-s3-access-key \"{self.getParam('mas_aibroker_tenant_s3_access_key')}\"{newline}" + if self.getParam('tenant_entitlement_end_date') != "": + command += f" --mas-aibroker-tenant-s3-secret-key \"{self.getParam('mas_aibroker_tenant_s3_secret_key')}\"{newline}" + + command += " --accept-license --no-confirm" + return command diff --git a/python/src/mas/cli/aiservice/install/argParser.py b/python/src/mas/cli/aiservice/install/argParser.py new file mode 100644 index 00000000000..f01f9a76bbf --- /dev/null +++ b/python/src/mas/cli/aiservice/install/argParser.py @@ -0,0 +1,742 @@ +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +import argparse +from os import path + +from ... import __version__ as packageVersion +from ...cli import getHelpFormatter + + +def isValidFile(parser, arg) -> str: + if not path.exists(arg): + parser.error(f"Error: The file {arg} does not exist") + else: + return arg + + +aiServiceinstallArgParser = argparse.ArgumentParser( + prog="mas install-aiservice", + description="\n".join([ + f"IBM Maximo Application Suite Admin CLI v{packageVersion}", + "Install Aiservice by configuring and launching the Tekton Pipeline.\n", + "Interactive Mode:", + "Omitting the --instance-id option will trigger an interactive prompt" + ]), + epilog="Refer to the online documentation for more information: https://ibm-mas.github.io/cli/", + formatter_class=getHelpFormatter(), + add_help=False +) + +# MAS Catalog Selection & Entitlement +# ----------------------------------------------------------------------------- +catArgGroup = aiServiceinstallArgParser.add_argument_group("MAS Catalog Selection & Entitlement") +catArgGroup.add_argument( + "-c", "--mas-catalog-version", + required=False, + help="IBM Maximo Operator Catalog to install" +) +catArgGroup.add_argument( + "--mas-catalog-digest", + required=False, + help="IBM Maximo Operator Catalog Digest, only required when installing development catalog sources" +) +catArgGroup.add_argument( + "--ibm-entitlement-key", + required=False, + help="IBM entitlement key" +) + +# Aibroker Basic Configuration +# ----------------------------------------------------------------------------- +masArgGroup = aiServiceinstallArgParser.add_argument_group("Aibroker Basic Configuration") +masArgGroup.add_argument( + "-i", "--aibroker-instance-id", + required=False, + help="Aibroker Instance ID" +) + +# MAS Advanced Configuration +# ----------------------------------------------------------------------------- +masAdvancedArgGroup = aiServiceinstallArgParser.add_argument_group("MAS Advanced Configuration") +masAdvancedArgGroup.add_argument( + "--additional-configs", + required=False, + help="Path to a directory containing additional configuration files to be applied" +) +masAdvancedArgGroup.add_argument( + "--non-prod", + required=False, + help="Install MAS in non-production mode", + action="store_true" +) + +# Storage +# ----------------------------------------------------------------------------- +storageArgGroup = aiServiceinstallArgParser.add_argument_group("Storage") +storageArgGroup.add_argument( + "--storage-class-rwo", + required=False, + help="ReadWriteOnce (RWO) storage class (e.g. ibmc-block-gold)" +) +storageArgGroup.add_argument( + "--storage-class-rwx", + required=False, + help="ReadWriteMany (RWX) storage class (e.g. ibmc-file-gold-gid)" +) +storageArgGroup.add_argument( + "--storage-pipeline", + required=False, + help="Install pipeline storage class (e.g. ibmc-file-gold-gid)" +) +storageArgGroup.add_argument( + "--storage-accessmode", + required=False, + help="Install pipeline storage class access mode (ReadWriteMany or ReadWriteOnce)", + choices=["ReadWriteMany", "ReadWriteOnce"] +) + +# IBM Suite License Service +# ----------------------------------------------------------------------------- +slsArgGroup = aiServiceinstallArgParser.add_argument_group("IBM Suite License Service") +slsArgGroup.add_argument( + "--license-file", + required=False, + help="Path to MAS license file", + type=lambda x: isValidFile(aiServiceinstallArgParser, x) +) +slsArgGroup.add_argument( + "--sls-namespace", + required=False, + help="Customize the SLS install namespace", + default="ibm-sls" +) +slsArgGroup.add_argument( + "--dedicated-sls", + action="store_true", + default=False, + help="Set the SLS namespace to mas--sls" +) + +# IBM Data Reporting Operator (DRO) +# ----------------------------------------------------------------------------- +droArgGroup = aiServiceinstallArgParser.add_argument_group("IBM Data Reporting Operator (DRO)") +droArgGroup.add_argument( + "--uds-email", + dest="uds_contact_email", + required=False, + help="Contact e-mail address" +) +droArgGroup.add_argument( + "--uds-firstname", + dest="uds_contact_firstname", + required=False, + help="Contact first name" +) +droArgGroup.add_argument( + "--uds-lastname", + dest="uds_contact_lastname", + required=False, + help="Contact last name" +) +droArgGroup.add_argument( + "--dro-namespace", + required=False, + help="" +) + +# MongoDb Community Operator +# ----------------------------------------------------------------------------- +mongoArgGroup = aiServiceinstallArgParser.add_argument_group("MongoDb Community Operator") +mongoArgGroup.add_argument( + "--mongodb-namespace", + required=False, + help="" +) + +# MAS Applications +# ----------------------------------------------------------------------------- +masAppsArgGroup = aiServiceinstallArgParser.add_argument_group("MAS Applications") + +masAppsArgGroup.add_argument( + "--aibroker-channel", + required=False, + help="Subscription channel for Maximo Ai Broker" +) + +# AI Broker +# ----------------------------------------------------------------------------- +aibrokerArgGroup = aiServiceinstallArgParser.add_argument_group("Maximo AI Broker") +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-provider", + dest="mas_aibroker_storage_provider", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-accesskey", + dest="mas_aibroker_storage_accesskey", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-secretkey", + dest="mas_aibroker_storage_secretkey", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-host", + dest="mas_aibroker_storage_host", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-port", + dest="mas_aibroker_storage_port", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-ssl", + dest="mas_aibroker_storage_ssl", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-region", + dest="mas_aibroker_storage_region", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-pipelines-bucket", + dest="mas_aibroker_storage_pipelines_bucket", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-tenants-bucket", + dest="mas_aibroker_storage_tenants_bucket", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-storage-templates-bucket", + dest="mas_aibroker_storage_templates_bucket", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-name", + dest="mas_aibroker_tenant_name", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-watsonxai-apikey", + dest="mas_aibroker_watsonxai_apikey", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-watsonxai-url", + dest="mas_aibroker_watsonxai_url", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-watsonxai-project-id", + dest="mas_aibroker_watsonxai_project_id", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-watsonx-action", + dest="mas_aibroker_watsonx_action", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-host", + dest="mas_aibroker_db_host", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-port", + dest="mas_aibroker_db_port", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-user", + dest="mas_aibroker_db_user", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-database", + dest="mas_aibroker_db_database", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-secret-name", + dest="mas_aibroker_db_secret_name", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-secret-key", + dest="mas_aibroker_db_secret_key", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db-secret-value", + dest="mas_aibroker_db_secret_value", + required=False, + help="Customize Manage database encryption keys" +) +aibrokerArgGroup.add_argument( + "--minio-root-user", + dest="minio_root_user", + required=False, + help="Root user for minio" +) +aibrokerArgGroup.add_argument( + "--minio-root-password", + dest="minio_root_password", + required=False, + help="Password for minio rootuser" +) +aibrokerArgGroup.add_argument( + "--mariadb-user", + dest="mariadb_user", + required=False, + help="Mariadb user name" +) +aibrokerArgGroup.add_argument( + "--mariadb-password", + dest="mariadb_password", + required=False, + help="Password for mariadb user" +) +aibrokerArgGroup.add_argument( + "--tenant-entitlement-type", + dest="tenant_entitlement_type", + required=False, + help="Type of aibroker tenant" +) +aibrokerArgGroup.add_argument( + "--tenant-entitlement-start-date", + dest="tenant_entitlement_start_date", + required=False, + help="Start date for Aibroker tenant" +) +aibrokerArgGroup.add_argument( + "--tenant-entitlement-end-date", + dest="tenant_entitlement_end_date", + required=False, + help="End date for Aibroker tenant" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-s3-bucket-prefix", + dest="mas_aibroker_s3_bucket_prefix", + required=False, + help="s3 bucker prefix" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-s3-endpoint-url", + dest="mas_aibroker_s3_endpoint_url", + required=False, + help="endpoint url for s3" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-s3-region", + dest="mas_aibroker_s3_region", + required=False, + help="region for s3" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-s3-bucket-prefix", + dest="mas_aibroker_tenant_s3_bucket_prefix", + required=False, + help="s3 bucker prefix ( tenant level )" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-s3-region", + dest="mas_aibroker_tenant_s3_region", + required=False, + help="s3 region ( tenant level )" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-s3-endpoint-url", + dest="mas_aibroker_tenant_s3_endpoint_url", + required=False, + help="endpoint url for s3 ( tenant level )" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-s3-access-key", + dest="mas_aibroker_tenant_s3_access_key", + required=False, + help="access key for s3 ( tenant level )" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-tenant-s3-secret-key", + dest="mas_aibroker_tenant_s3_secret_key", + required=False, + help="secret key for s3 ( tenant level )" +) +aibrokerArgGroup.add_argument( + "--rsl-url", + dest="rsl_url", + required=False, + help="rsl url" +) +aibrokerArgGroup.add_argument( + "--rsl-org-id", + dest="rsl_org_id", + required=False, + help="org id for rsl" +) +aibrokerArgGroup.add_argument( + "--rsl-token", + dest="rsl_token", + required=False, + help="token for rsl" +) +aibrokerArgGroup.add_argument( + "--install-minio-aiservice", + dest="install_minio_aiservice", + required=False, + help="flag for install minio" +) +aibrokerArgGroup.add_argument( + "--install-sls-aiservice", + dest="install_sls_aiservice", + required=False, + help="flag for install sls" +) +aibrokerArgGroup.add_argument( + "--install-dro-aiservice", + dest="install_dro_aiservice", + required=False, + help="flag for install dro" +) +aibrokerArgGroup.add_argument( + "--install-db2-aiservice", + dest="install_db2_aiservice", + required=False, + help="flag for install db2" +) + +aibrokerArgGroup.add_argument( + "--mas-aibroker-dro-secret-name", + dest="mas_aibroker_dro_secret_name", + required=False, + help="DRO secret name" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-dro-api-key", + dest="mas_aibroker_dro_api_key", + required=False, + help="DRO API key" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-dro-url", + dest="mas_aibroker_dro_url", + required=False, + help="DRO URL" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-dro-ca-cert", + dest="mas_aibroker_dro_ca_cert", + required=False, + help="DRO CA certificate" +) + +aibrokerArgGroup.add_argument( + "--mas-aibroker-db2-username", + dest="mas_aibroker_db2_username", + required=False, + help="DB2 username" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db2-password", + dest="mas_aibroker_db2_password", + required=False, + help="DB2 password" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db2-jdbc-url", + dest="mas_aibroker_db2_jdbc_url", + required=False, + help="DB2 JDBC URL" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db2-ssl-enabled", + dest="mas_aibroker_db2_ssl_enabled", + required=False, + help="DB2 SSL enabled" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-db2-ca-cert", + dest="mas_aibroker_db2_ca_cert", + required=False, + help="DB2 CA certificate" +) + +aibrokerArgGroup.add_argument( + "--mas-aibroker-sls-secret-name", + dest="mas_aibroker_sls_secret_name", + required=False, + help="SLS secret name" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-sls-registration-key", + dest="mas_aibroker_sls_registration_key", + required=False, + help="SLS registration key" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-sls-url", + dest="mas_aibroker_sls_url", + required=False, + help="SLS URL" +) +aibrokerArgGroup.add_argument( + "--mas-aibroker-sls-ca-cert", + dest="mas_aibroker_sls_ca_cert", + required=False, + help="SLS CA certificate" +) + +aibrokerArgGroup.add_argument( + "--environment-type", + dest="environment_type", + required=False, + default="non-production", + help="Environment type (default: non-production)" +) +# IBM Db2 Universal Operator +# ----------------------------------------------------------------------------- +db2ArgGroup = aiServiceinstallArgParser.add_argument_group("IBM Db2 Universal Operator") +db2ArgGroup.add_argument( + "--db2-namespace", + required=False, + help="Change namespace where Db2u instances will be created" +) +db2ArgGroup.add_argument( + "--db2-channel", + required=False, + help="Subscription channel for Db2u" +) +db2ArgGroup.add_argument( + "--db2-system", + dest="db2_action_system", + required=False, + help="Install a shared Db2u instance for MAS (required by IoT & Monitor, supported by Manage)", + action="store_const", + const="install" +) +db2ArgGroup.add_argument( + "--db2-manage", + dest="db2_action_manage", + required=False, + help="Install a dedicated Db2u instance for Maximo Manage (supported by Manage)", + action="store_const", + const="install" +) +db2ArgGroup.add_argument( + "--db2-facilities", + dest="db2_action_facilities", + required=False, + help="Install a dedicated Db2u instance for Maximo Real Estate and Facilities (supported by Facilities)", + action="store_const", + const="install" +) +db2ArgGroup.add_argument( + "--db2-type", + required=False, + help="Choose the type of the Manage dedicated Db2u instance. Available options are `db2wh` (default) or `db2oltp`" +) +db2ArgGroup.add_argument( + "--db2-timezone", + required=False, + help="" +) +db2ArgGroup.add_argument( + "--db2-affinity-key", + required=False, + help="Set a node label to declare affinity to" +) +db2ArgGroup.add_argument( + "--db2-affinity-value", + required=False, + help="Set the value of the node label to affine with" +) +db2ArgGroup.add_argument( + "--db2-tolerate-key", + required=False, + help="Set a node taint to tolerate" +) +db2ArgGroup.add_argument( + "--db2-tolerate-value", + required=False, + help="Set the value of the taint to tolerate" +) +db2ArgGroup.add_argument( + "--db2-tolerate-effect", + required=False, + help="Set the effect that will be tolerated (NoSchedule, PreferNoSchedule, or NoExecute)" +) +db2ArgGroup.add_argument( + "--db2-cpu-requests", + required=False, + help="Customize Db2 CPU request" +) +db2ArgGroup.add_argument( + "--db2-cpu-limits", + required=False, + help="Customize Db2 CPU limit" +) +db2ArgGroup.add_argument( + "--db2-memory-requests", + required=False, + help="Customize Db2 memory request" +) +db2ArgGroup.add_argument( + "--db2-memory-limits", + required=False, + help="Customize Db2 memory limit" +) +db2ArgGroup.add_argument( + "--db2-backup-storage", + dest="db2_backup_storage_size", + required=False, + help="Customize Db2 storage capacity" +) +db2ArgGroup.add_argument( + "--db2-data-storage", + dest="db2_data_storage_size", + required=False, + help="Customize Db2 storage capacity" +) +db2ArgGroup.add_argument( + "--db2-logs-storage", + dest="db2_logs_storage_size", + required=False, + help="Customize Db2 storage capacity" +) +db2ArgGroup.add_argument( + "--db2-meta-storage", + dest="db2_meta_storage_size", + required=False, + help="Customize Db2 storage capacity" +) +db2ArgGroup.add_argument( + "--db2-temp-storage", + dest="db2_temp_storage_size", + required=False, + help="Customize Db2 storage capacity" +) + + +# Development Mode +# ----------------------------------------------------------------------------- +devArgGroup = aiServiceinstallArgParser.add_argument_group("Development Mode") +devArgGroup.add_argument( + "--artifactory-username", + required=False, + help="Username for access to development builds on Artifactory" +) +devArgGroup.add_argument( + "--artifactory-token", + required=False, + help="API Token for access to development builds on Artifactory" +) + +# Approvals +# ----------------------------------------------------------------------------- +approvalsGroup = aiServiceinstallArgParser.add_argument_group("Integrated Approval Workflow (MAX_RETRIES:RETRY_DELAY:IGNORE_FAILURE)") +approvalsGroup.add_argument( + "--approval-aibroker", + default="", + help="Require approval after the Aibroker has been configured" +) + +# More Options +# ----------------------------------------------------------------------------- +otherArgGroup = aiServiceinstallArgParser.add_argument_group("More") +otherArgGroup.add_argument( + "--advanced", + action="store_true", + default=False, + help="Show advanced install options (in interactve mode)" +) +otherArgGroup.add_argument( + "--simplified", + action="store_true", + default=False, + help="Don't show advanced install options (in interactve mode)" +) +otherArgGroup.add_argument( + "--accept-license", + action="store_true", + default=False, + help="Accept all license terms without prompting" +) +otherArgGroup.add_argument( + "--dev-mode", + required=False, + action="store_true", + default=False, + help="Configure installation for development mode", +) +otherArgGroup.add_argument( + "--no-wait-for-pvc", + required=False, + action="store_true", + help="Disable the wait for pipeline PVC to bind before starting the pipeline" +) +otherArgGroup.add_argument( + "--skip-pre-check", + required=False, + action="store_true", + help="Disable the 'pre-install-check' at the start of the install pipeline" +) +otherArgGroup.add_argument( + "--no-confirm", + required=False, + action="store_true", + default=False, + help="Launch the upgrade without prompting for confirmation", +) +otherArgGroup.add_argument( + "--image-pull-policy", + dest="image_pull_policy", + required=False, + help="Manually set the image pull policy used in the Tekton Pipeline", +) +otherArgGroup.add_argument( + "--service-account", + dest="service_account_name", + required=False, + help="Run the install pipeline under a custom service account (also disables creation of the default 'pipeline' service account)", +) + +otherArgGroup.add_argument( + "-h", "--help", + action="help", + default=False, + help="Show this help message and exit", +) diff --git a/python/src/mas/cli/aiservice/install/params.py b/python/src/mas/cli/aiservice/install/params.py new file mode 100644 index 00000000000..93b463d141b --- /dev/null +++ b/python/src/mas/cli/aiservice/install/params.py @@ -0,0 +1,120 @@ +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +requiredParams = [ + # MAS + "mas_catalog_version", + # Storage classes + "storage_class_rwo", + "storage_class_rwx", + # Entitlement + "ibm_entitlement_key", + # DRO + "uds_contact_email", + "uds_contact_firstname", + "uds_contact_lastname" +] + +optionalParams = [ + # Pipeline + "image_pull_policy", + "service_account_name", + # Catalogue + "mas_catalog_digest", + # SLS + "sls_namespace", + # DRO + "dro_namespace", + # Db2 + "db2_action_system", + "db2_action_manage", + "db2_action_facilities", + "db2_type", + "db2_timezone", + "db2_namespace", + "db2_channel", + "db2_affinity_key", + "db2_affinity_value", + "db2_tolerate_key", + "db2_tolerate_value", + "db2_tolerate_effect", + "db2_cpu_requests", + "db2_cpu_limits", + "db2_memory_requests", + "db2_memory_limits", + "db2_backup_storage_size", + "db2_data_storage_size", + "db2_logs_storage_size", + "db2_meta_storage_size", + "db2_temp_storage_size", + # Dev Mode + "artifactory_username", + "artifactory_token", + # Aibroker + "mas_aibroker_storage_provider", + "mas_aibroker_storage_accesskey", + "mas_aibroker_storage_secretkey", + "mas_aibroker_storage_host", + "mas_aibroker_storage_port", + "mas_aibroker_storage_ssl", + "mas_aibroker_storage_region", + "mas_aibroker_storage_pipelines_bucket", + "mas_aibroker_storage_tenants_bucket", + "mas_aibroker_storage_templates_bucket", + "mas_aibroker_tenant_name", + "mas_aibroker_watsonxai_apikey", + "mas_aibroker_watsonxai_url", + "mas_aibroker_watsonxai_project_id", + "mas_aibroker_watsonx_action", + "mas_aibroker_db_host", + "mas_aibroker_db_port", + "mas_aibroker_db_user", + "mas_aibroker_db_database", + "mas_aibroker_db_secret_name", + "mas_aibroker_db_secret_key", + "mas_aibroker_db_secret_value", + "aibroker_instance_id", + "mariadb_user", + "mariadb_password", + "minio_root_user", + "minio_root_password", + "tenant_entitlement_type", + "tenant_entitlement_start_date", + "tenant_entitlement_end_date", + "mas_aibroker_s3_bucket_prefix", + "mas_aibroker_s3_region", + "mas_aibroker_s3_endpoint_url", + "mas_aibroker_tenant_s3_bucket_prefix", + "mas_aibroker_tenant_s3_region", + "mas_aibroker_tenant_s3_endpoint_url", + "mas_aibroker_tenant_s3_access_key", + "mas_aibroker_tenant_s3_secret_key", + "rsl_url", + "rsl_org_id", + "rsl_token", + "install_minio_aiservice", + "install_sls_aiservice", + "install_dro_aiservice", + "install_db2_aiservice", + "mas_aibroker_dro_secret_name", + "mas_aibroker_dro_api_key", + "mas_aibroker_dro_url", + "mas_aibroker_dro_ca_cert", + "mas_aibroker_db2_username", + "mas_aibroker_db2_password", + "mas_aibroker_db2_jdbc_url", + "mas_aibroker_db2_ssl_enabled", + "mas_aibroker_db2_ca_cert", + "mas_aibroker_sls_secret_name", + "mas_aibroker_sls_registration_key", + "mas_aibroker_sls_url", + "mas_aibroker_sls_ca_cert", + "environment_type", +] diff --git a/python/src/mas/cli/aiservice/install/summarizer.py b/python/src/mas/cli/aiservice/install/summarizer.py new file mode 100644 index 00000000000..fa88d89f992 --- /dev/null +++ b/python/src/mas/cli/aiservice/install/summarizer.py @@ -0,0 +1,193 @@ +# ***************************************************************************** +# Copyright (c) 2024, 2025 IBM Corporation and other Contributors. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# ***************************************************************************** + +import logging +import yaml +from prompt_toolkit import print_formatted_text, HTML +from mas.devops.ocp import getConsoleURL + +logger = logging.getLogger(__name__) + + +class aiServiceInstallSummarizerMixin(): + def ocpSummary(self) -> None: + self.printH2("Pipeline Configuration") + self.printParamSummary("Service Account", "service_account_name") + self.printParamSummary("Image Pull Policy", "image_pull_policy") + self.printSummary("Skip Pre-Install Healthcheck", "Yes" if self.getParam('skip_pre_check') == "true" else "No") + + self.printH2("OpenShift Container Platform") + self.printSummary("Worker Node Architecture", self.architecture) + self.printSummary("Storage Class Provider", self.storageClassProvider) + self.printParamSummary("ReadWriteOnce Storage Class", "storage_class_rwo") + self.printParamSummary("ReadWriteMany Storage Class", "storage_class_rwx") + + self.printParamSummary("Certificate Manager", "cert_manager_provider") + self.printParamSummary("Cluster Ingress Certificate Secret", "ocp_ingress_tls_secret_name") + + def masSummary(self) -> None: + + self.printH2("IBM Maximo Application Suite") + + print() + self.printParamSummary("Catalog Version", "mas_catalog_version") + # We only list the digest if it's specified (primary use case is when running development builds in airgap environments) + if self.getParam("mas_catalog_digest" != ""): + self.printParamSummary("Catalog Digest", "mas_catalog_digest") + self.printParamSummary("Subscription Channel", "mas_channel") + + print() + self.printParamSummary("IBM Entitled Registry", "mas_icr_cp") + self.printParamSummary("IBM Open Registry", "mas_icr_cpopen") + + print() + if self.localConfigDir is not None: + self.printSummary("Additional Config", self.localConfigDir) + else: + self.printSummary("Additional Config", "Not Configured") + + def aibrokerSummary(self) -> None: + if self.installAiBroker: + self.printSummary("AI Broker", self.params["mas_app_channel_aibroker"]) + print_formatted_text(HTML(" + Maximo AI Broker Settings")) + self.printParamSummary(" + Aibroker Instance Id", "aibroker_instance_id") + self.printParamSummary(" + Storage provider", "mas_aibroker_storage_provider") + if self.getParam("mas_aibroker_storage_provider") == "minio": + self.printParamSummary(" + minio root username", "minio_root_user") + if self.getParam("mas_app_channel_aibroker") != "9.0.x": + self.printParamSummary(" + Mariadb username", "mariadb_user") + self.printParamSummary(" + Mariadb password", "mariadb_password") + self.printParamSummary(" + Storage access key", "mas_aibroker_storage_accesskey") + self.printParamSummary(" + Storage host", "mas_aibroker_storage_host") + self.printParamSummary(" + Storage port", "mas_aibroker_storage_port") + self.printParamSummary(" + Storage ssl", "mas_aibroker_storage_ssl") + self.printParamSummary(" + Storage region", "mas_aibroker_storage_region") + self.printParamSummary(" + Storage pipelines bucket", "mas_aibroker_storage_pipelines_bucket") + self.printParamSummary(" + Storage tenants bucket", "mas_aibroker_storage_tenants_bucket") + self.printParamSummary(" + Storage templates bucket", "mas_aibroker_storage_templates_bucket") + self.printParamSummary(" + Watsonxai machine learning url", "mas_aibroker_watsonxai_url") + self.printParamSummary(" + Watsonxai project id", "mas_aibroker_watsonxai_project_id") + self.printParamSummary(" + Database host", "mas_aibroker_db_host") + self.printParamSummary(" + Database port", "mas_aibroker_db_port") + self.printParamSummary(" + Database user", "mas_aibroker_db_user") + self.printParamSummary(" + Database name", "mas_aibroker_db_database") + if self.getParam("mas_app_channel_aibroker") != "9.0.x": + self.printParamSummary(" + Tenant entitlement type", "tenant_entitlement_type") + self.printParamSummary(" + Tenant start date", "tenant_entitlement_start_date") + self.printParamSummary(" + Tenant end date", "tenant_entitlement_end_date") + self.printParamSummary(" + Tenant end date", "tenant_entitlement_end_date") + self.printParamSummary(" + S3 bucket prefix", "mas_aibroker_s3_bucket_prefix") + self.printParamSummary(" + S3 endpoint url", "mas_aibroker_s3_endpoint_url") + self.printParamSummary(" + S3 bucket prefix (tenant level)", "mas_aibroker_tenant_s3_bucket_prefix") + self.printParamSummary(" + S3 region (tenant level)", "mas_aibroker_tenant_s3_region") + self.printParamSummary(" + S3 endpoint url (tenant level)", "mas_aibroker_tenant_s3_endpoint_url") + self.printParamSummary(" + RSL url", "rsl_url") + self.printParamSummary(" + ORG Id of RSL", "rsl_org_id") + self.printParamSummary(" + Token for RSL", "rsl_token") + self.printParamSummary(" + Install minio", "install_minio_aiservice") + self.printParamSummary(" + Install SLS", "install_sls_aiservice") + if self.getParam("install_sls_aiservice") != "true": + self.printParamSummary(" + SLS secret name", "mas_aibroker_sls_secret_name") + self.printParamSummary(" + SLS registration key", "mas_aibroker_sls_registration_key") + self.printParamSummary(" + SLS URL", "mas_aibroker_sls_url") + self.printParamSummary(" + Install DRO", "install_dro_aiservice") + if self.getParam("install_dro_aiservice") != "true": + self.printParamSummary(" + DRO secret name", "mas_aibroker_dro_secret_name") + self.printParamSummary(" + DRO API key", "mas_aibroker_dro_api_key") + self.printParamSummary(" + DRO URL", "mas_aibroker_dro_url") + self.printParamSummary(" + Install DB2", "install_db2_aiservice") + if self.getParam("install_db2_aiservice") != "true": + self.printParamSummary(" + DB2 username", "mas_aibroker_db2_username") + self.printParamSummary(" + DB2 JDBC URL", "mas_aibroker_db2_jdbc_url") + self.printParamSummary(" + DB2 SSL enabled", "mas_aibroker_db2_ssl_enabled") + self.printParamSummary(" + Environment type", "environment_type") + + else: + self.printSummary("AI Broker", "Do Not Install") + + def db2Summary(self) -> None: + if self.getParam("db2_action_system") == "install" or self.getParam("db2_action_manage") == "install": + self.printH2("IBM Db2 Univeral Operator Configuration") + self.printSummary("System Instance", "Install" if self.getParam("db2_action_system") == "install" else "Do Not Install") + self.printSummary("Dedicated Manage Instance", "Install" if self.getParam("db2_action_manage") == "install" else "Do Not Install") + self.printParamSummary(" - Type", "db2_type") + self.printParamSummary(" - Timezone", "db2_timezone") + print() + self.printParamSummary("Install Namespace", "db2_namespace") + self.printParamSummary("Subscription Channel", "db2_channel") + print() + self.printParamSummary("CPU Request", "db2_cpu_requests") + self.printParamSummary("CPU Limit", "db2_cpu_limits") + self.printParamSummary("Memory Request", "db2_memory_requests") + self.printParamSummary("Memory Limit ", "db2_memory_limits") + print() + self.printParamSummary("Meta Storage", "db2_meta_storage_size") + self.printParamSummary("Data Storage", "db2_data_storage_size") + self.printParamSummary("Backup Storage", "db2_backup_storage_size") + self.printParamSummary("Temp Storage", "db2_temp_storage_size") + self.printParamSummary("Transaction Logs Storage", "db2_logs_storage_size") + print() + if self.getParam('db2_affinity_key') != "": + self.printSummary("Node Affinity", f"{self.getParam('db2_affinity_key')}={self.getParam('db2_affinity_value')}") + else: + self.printSummary("Node Affinity", "None") + + if self.getParam('db2_tolerate_key') != "": + self.printSummary("Node Tolerations", f"{self.getParam('db2_tolerate_key')}={self.getParam('db2_tolerate_value')} @ {self.getParam('db2_tolerate_effect')}") + else: + self.printSummary("Node Tolerations", "None") + + def droSummary(self) -> None: + self.printH2("IBM Data Reporter Operator (DRO) Configuration") + self.printParamSummary("Contact e-mail", "uds_contact_email") + self.printParamSummary("First name", "uds_contact_firstname") + self.printParamSummary("Last name", "uds_contact_lastname") + self.printParamSummary("Install Namespace", "dro_namespace") + + def slsSummary(self) -> None: + self.printH2("IBM Suite License Service") + self.printParamSummary("Namespace", "sls_namespace") + if self.getParam("sls_action") == "install": + self.printSummary("Subscription Channel", "3.x") + self.printParamSummary("IBM Open Registry", "sls_icr_cpopen") + if self.slsLicenseFileLocal: + self.printSummary("License File", self.slsLicenseFileLocal) + + def mongoSummary(self) -> None: + self.printH2("MongoDb") + if self.getParam("mongodb_action") == "install": + self.printSummary("Type", "MongoCE Operator") + self.printParamSummary("Install Namespace", "mongodb_namespace") + elif self.getParam("mongodb_action") == "byo": + self.printSummary("Type", "BYO (mongodb-system.yaml)") + else: + self.fatalError(f"Unexpected value for mongodb_action parameter: {self.getParam('mongodb_action')}") + + def displayInstallSummary(self) -> None: + self.printH1("Review Settings") + self.printDescription([ + "Connected to:", + f" - {getConsoleURL(self.dynamicClient)}" + ]) + + logger.debug("PipelineRun parameters:") + logger.debug(yaml.dump(self.params, default_flow_style=False)) + + # Cluster Config & Dependencies + self.ocpSummary() + self.droSummary() + self.slsSummary() + self.masSummary() + self.printH2("IBM Maximo Application Suite Application - Aiservice") + self.aibrokerSummary() + + # Application Dependencies + self.mongoSummary() + self.db2Summary() diff --git a/python/src/mas/cli/cli.py b/python/src/mas/cli/cli.py index c9c88482afb..3588d0a288e 100644 --- a/python/src/mas/cli/cli.py +++ b/python/src/mas/cli/cli.py @@ -205,6 +205,7 @@ def __init__(self): "9.0.x": " - https://ibm.biz/MAS90-License\n - https://ibm.biz/MaximoIT90-License\n - https://ibm.biz/MAXArcGIS90-License", "9.1.x-feature": " - https://ibm.biz/MAS90-License\n - https://ibm.biz/MaximoIT90-License\n - https://ibm.biz/MAXArcGIS90-License\n\nBe aware, this channel subscription is supported for non-production use only. \nIt allows early access to new features for evaluation in non-production environments. \nThis subscription is offered alongside and in parallel with our normal maintained streams. \nWhen using this subscription, IBM Support will only accept cases for the latest available bundle deployed in a non-production environment. \nSeverity must be either 3 or 4 and cases cannot be escalated. \nPlease refer to IBM documentation for more details.\n", "9.1.x": " - https://ibm.biz/MAS91-License\n - https://ibm.biz/MAXIT91-License\n - https://ibm.biz/MAXESRI91-License", + "aibroker-9.1.x": " - https://ibm.biz/MAS91-License", } self.upgrade_path = { diff --git a/python/src/mas/cli/install/app.py b/python/src/mas/cli/install/app.py index ed9d9804704..14cd02c68e9 100644 --- a/python/src/mas/cli/install/app.py +++ b/python/src/mas/cli/install/app.py @@ -666,10 +666,6 @@ def configApps(self): if self.installInspection: self.configAppChannel("visualinspection") - self.installAiBroker = self.yesOrNo("Install AI Broker") - if self.installAiBroker: - self.configAppChannel("aibroker") - if isVersionEqualOrAfter('9.1.0', self.getParam("mas_channel")) and self.getParam("mas_channel") != '9.1.x-feature': self.installFacilities = self.yesOrNo("Install Real Estate and Facilities") if self.installFacilities: @@ -908,7 +904,6 @@ def interactiveMode(self, simplified: bool, advanced: bool) -> None: self.optimizerSettings() self.predictSettings() self.assistSettings() - self.aibrokerSettings() self.facilitiesSettings() # Dependencies @@ -939,7 +934,6 @@ def nonInteractiveMode(self) -> None: self.installPredict = False self.installInspection = False self.installOptimizer = False - self.installAiBroker = False self.installFacilities = False self.deployCP4D = False self.db2SetAffinity = False @@ -1038,10 +1032,6 @@ def nonInteractiveMode(self) -> None: if value is not None and value != "": self.setParam("mas_app_channel_visualinspection", value) self.installInspection = True - elif key == "aibroker_channel": - if value is not None and value != "": - self.setParam("mas_app_channel_aibroker", value) - self.installAiBroker = True elif key == "optimizer_channel": if value is not None and value != "": self.setParam("mas_app_channel_optimizer", value) diff --git a/python/src/mas/cli/install/argParser.py b/python/src/mas/cli/install/argParser.py index dc9499c4439..934c59b97aa 100644 --- a/python/src/mas/cli/install/argParser.py +++ b/python/src/mas/cli/install/argParser.py @@ -372,153 +372,12 @@ def isValidFile(parser, arg) -> str: choices=["full", "limited"], help="Install plan for Maximo Optimizer" ) -masAppsArgGroup.add_argument( - "--aibroker-channel", - required=False, - help="Subscription channel for Maximo Ai Broker" -) masAppsArgGroup.add_argument( "--facilities-channel", required=False, help="Subscription channel for Maximo Real Estate and Facilities" ) -# AI Broker -# ----------------------------------------------------------------------------- -aibrokerArgGroup = installArgParser.add_argument_group("Maximo AI Broker") -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-provider", - dest="mas_aibroker_storage_provider", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-accesskey", - dest="mas_aibroker_storage_accesskey", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-secretkey", - dest="mas_aibroker_storage_secretkey", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-host", - dest="mas_aibroker_storage_host", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-port", - dest="mas_aibroker_storage_port", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-ssl", - dest="mas_aibroker_storage_ssl", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-region", - dest="mas_aibroker_storage_region", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-pipelines-bucket", - dest="mas_aibroker_storage_pipelines_bucket", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-tenants-bucket", - dest="mas_aibroker_storage_tenants_bucket", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-storage-templates-bucket", - dest="mas_aibroker_storage_templates_bucket", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-tenant-name", - dest="mas_aibroker_tenant_name", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-watsonxai-apikey", - dest="mas_aibroker_watsonxai_apikey", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-watsonxai-url", - dest="mas_aibroker_watsonxai_url", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-watsonxai-project-id", - dest="mas_aibroker_watsonxai_project_id", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-watsonx-action", - dest="mas_aibroker_watsonx_action", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-host", - dest="mas_aibroker_db_host", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-port", - dest="mas_aibroker_db_port", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-user", - dest="mas_aibroker_db_user", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-database", - dest="mas_aibroker_db_database", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-secret-name", - dest="mas_aibroker_db_secret_name", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-secret-key", - dest="mas_aibroker_db_secret_key", - required=False, - help="Customize Manage database encryption keys" -) -aibrokerArgGroup.add_argument( - "--mas-aibroker-db-secret-value", - dest="mas_aibroker_db_secret_value", - required=False, - help="Customize Manage database encryption keys" -) - # Arcgis # ----------------------------------------------------------------------------- arcgisArgGroup = installArgParser.add_argument_group("Maximo Location Services for Esri (arcgis)") diff --git a/python/src/mas/cli/install/settings/manageSettings.py b/python/src/mas/cli/install/settings/manageSettings.py index 2c3fd941668..5ea3c4ab0fa 100644 --- a/python/src/mas/cli/install/settings/manageSettings.py +++ b/python/src/mas/cli/install/settings/manageSettings.py @@ -271,29 +271,3 @@ def manageSettingsOther(self) -> None: self.manageSettingsTimezone() self.manageSettingsLanguages() self.manageSettingsCP4D() - - def aibrokerSettings(self) -> None: - if self.installAiBroker: - self.printH2("Maximo AI Broker Settings - Storage, WatsonX, MariaDB details") - self.printDescription(["Customise AI Broker details"]) - self.promptForString("Storage provider", "mas_aibroker_storage_provider") - self.promptForString("Storage access key", "mas_aibroker_storage_accesskey") - self.promptForString("Storage secret key", "mas_aibroker_storage_secretkey") - self.promptForString("Storage host", "mas_aibroker_storage_host") - self.promptForString("Storage port", "mas_aibroker_storage_port") - self.promptForString("Storage ssl", "mas_aibroker_storage_ssl") - self.promptForString("Storage region", "mas_aibroker_storage_region") - self.promptForString("Storage pipelines bucket", "mas_aibroker_storage_pipelines_bucket") - self.promptForString("Storage tenants bucket", "mas_aibroker_storage_tenants_bucket") - self.promptForString("Storage templates bucket", "mas_aibroker_storage_templates_bucket") - - self.promptForString("Watsonxai api key", "mas_aibroker_watsonxai_apikey") - self.promptForString("Watsonxai machine learning url", "mas_aibroker_watsonxai_url") - self.promptForString("Watsonxai project id", "mas_aibroker_watsonxai_project_id") - - self.promptForString("Database host", "mas_aibroker_db_host") - self.promptForString("Database port", "mas_aibroker_db_port") - self.promptForString("Database user", "mas_aibroker_db_user") - self.promptForString("Database name", "mas_aibroker_db_database") - self.promptForString("Database Secretname", "mas_aibroker_db_secret_name") - self.promptForString("Database password", "mas_aibroker_db_secret_value") diff --git a/python/src/mas/cli/install/summarizer.py b/python/src/mas/cli/install/summarizer.py index 513c9ae38ae..720abf99d28 100644 --- a/python/src/mas/cli/install/summarizer.py +++ b/python/src/mas/cli/install/summarizer.py @@ -153,32 +153,6 @@ def inspectionSummary(self) -> None: else: self.printSummary("Visual Inspection", "Do Not Install") - def aibrokerSummary(self) -> None: - if self.installAiBroker: - self.printSummary("AI Broker", self.params["mas_app_channel_aibroker"]) - print_formatted_text(HTML(" + Maximo AI Broker Settings")) - self.printParamSummary(" + Storage provider", "mas_aibroker_storage_provider") - self.printParamSummary(" + Storage access key", "mas_aibroker_storage_accesskey") - self.printParamSummary(" + Storage secret key", "mas_aibroker_storage_secretkey") - self.printParamSummary(" + Storage host", "mas_aibroker_storage_host") - self.printParamSummary(" + Storage port", "mas_aibroker_storage_port") - self.printParamSummary(" + Storage ssl", "mas_aibroker_storage_ssl") - self.printParamSummary(" + Storage region", "mas_aibroker_storage_region") - self.printParamSummary(" + Storage pipelines bucket", "mas_aibroker_storage_pipelines_bucket") - self.printParamSummary(" + Storage tenants bucket", "mas_aibroker_storage_tenants_bucket") - self.printParamSummary(" + Storage templates bucket", "mas_aibroker_storage_templates_bucket") - self.printParamSummary(" + Watsonxai api key", "mas_aibroker_watsonxai_apikey") - self.printParamSummary(" + Watsonxai machine learning url", "mas_aibroker_watsonxai_url") - self.printParamSummary(" + Watsonxai project id", "mas_aibroker_watsonxai_project_id") - self.printParamSummary(" + Database host", "mas_aibroker_db_host") - self.printParamSummary(" + Database port", "mas_aibroker_db_port") - self.printParamSummary(" + Database user", "mas_aibroker_db_user") - self.printParamSummary(" + Database name", "mas_aibroker_db_database") - self.printParamSummary(" + Database Secretname", "mas_aibroker_db_secret_name") - self.printParamSummary(" + Database password", "mas_aibroker_db_secret_value") - else: - self.printSummary("AI Broker", "Do Not Install") - def manageSummary(self) -> None: if self.installManage: self.printSummary(f"{'Manage foundation' if self.getParam('is_full_manage') == 'false' else 'Manage'}", self.params["mas_app_channel_manage"]) @@ -407,7 +381,6 @@ def displayInstallSummary(self) -> None: self.optimizerSummary() self.assistSummary() self.inspectionSummary() - self.aibrokerSummary() self.facilitiesSummary() # Application Dependencies diff --git a/tekton/generate-tekton-pipelines.yml b/tekton/generate-tekton-pipelines.yml index 3a69891bb02..966bbe46bad 100644 --- a/tekton/generate-tekton-pipelines.yml +++ b/tekton/generate-tekton-pipelines.yml @@ -29,6 +29,7 @@ with_items: # Main Pipelines - install + - install-aiservice - update - upgrade - rollback @@ -47,6 +48,7 @@ - fvt-monitor - fvt-optimizer - fvt-predict + - fvt-aiservice - fvt-sls - fvt-visualinspection - fvt-launcher diff --git a/tekton/generate-tekton-tasks.yml b/tekton/generate-tekton-tasks.yml index ec6007891cd..8c16b1f2366 100644 --- a/tekton/generate-tekton-tasks.yml +++ b/tekton/generate-tekton-tasks.yml @@ -47,13 +47,17 @@ # ------------------------------------------------------------------------- - name: Generate Tasks (AI Broker) ansible.builtin.template: - src: "{{ task_src_dir }}/aibroker/{{ item }}.yml.j2" + src: "{{ task_src_dir }}/aiservice/{{ item }}.yml.j2" dest: "{{ task_target_dir }}/{{ item }}.yaml" with_items: - - aibroker + - minio + - mariadb + - aiservice - kmodels - odh - + - aiservice-tenant + - aiservice-post-verify + # 4. Generate Tasks (FVT) # ------------------------------------------------------------------------- - name: Generate Tasks (FVT) @@ -104,6 +108,7 @@ - launchfvt-monitor - launchfvt-optimizer - launchfvt-predict + - launchfvt-aiservice - launchfvt-visualinspection - launchfvt-upgrade-post - launchfvt-upgrade-pre diff --git a/tekton/src/params/install-aibroker.yml.j2 b/tekton/src/params/install-aiservice.yml.j2 similarity index 51% rename from tekton/src/params/install-aibroker.yml.j2 rename to tekton/src/params/install-aiservice.yml.j2 index 05c300958b5..b8fd7d42c3c 100644 --- a/tekton/src/params/install-aibroker.yml.j2 +++ b/tekton/src/params/install-aiservice.yml.j2 @@ -1,6 +1,10 @@ # MAS Application Configuration - IBM Maximo AI Broker # ----------------------------------------------------------------------------- # - name: mas_aibroker_channel +- name: aibroker_instance_id + type: string + description: Aibroker instance id + default: "" - name: mas_app_channel_aibroker type: string description: Default channel for IBM Maximo AI Broker @@ -121,3 +125,151 @@ type: string description: database secret value for IBM Maximo AI Broker default: "" +- name: minio_root_user + type: string + description: root user for minio + default: "" +- name: minio_root_password + type: string + description: password for minio root user + default: "" +- name: mariadb_user + type: string + description: user for mariadb + default: "" +- name: mariadb_password + type: string + description: password for mariadb user + default: "" +- name: tenant_entitlement_type + type: string + description: type of tenant + default: "" +- name: tenant_entitlement_start_date + type: string + description: define start date for tenant + default: "" +- name: tenant_entitlement_end_date + type: string + description: define end date for tenant + default: "" +- name: install_db2_aiservice + type: string + description: flag for install db2 + default: "true" +- name: db2_action_aibroker + type: string + description: install dedicated db2 for aibroker + default: "install" +- name: mas_aibroker_s3_bucket_prefix + type: string + description: s3 bucker prefix + default: "" +- name: mas_aibroker_s3_region + type: string + description: s3 region + default: "" +- name: mas_aibroker_s3_endpoint_url + type: string + description: endpoint url for s3 + default: "" +- name: mas_aibroker_tenant_s3_bucket_prefix + type: string + description: s3 bucker prefix ( tenant level ) + default: "" +- name: mas_aibroker_tenant_s3_region + type: string + description: s3 region ( tenant level ) + default: "" +- name: mas_aibroker_tenant_s3_endpoint_url + type: string + description: endpoint url for s3 ( tenant level ) + default: "" +- name: mas_aibroker_tenant_s3_access_key + type: string + description: access key for s3 ( tenant level ) + default: "" +- name: mas_aibroker_tenant_s3_secret_key + type: string + description: secret key for s3 ( tenant level ) + default: "" +- name: rsl_url + type: string + description: url of RSL + default: "" +- name: rsl_org_id + type: string + description: org id for RSL + default: "" +- name: rsl_token + type: string + description: token for RSL + default: "" +- name: install_minio_aiservice + type: string + description: flag for install minio + default: "true" +- name: install_sls_aiservice + type: string + description: flag for install sls + default: "true" +- name: install_dro_aiservice + type: string + description: flag for install dro + default: "true" +- name: mas_aibroker_dro_secret_name + type: string + description: DRO secret name + default: "" +- name: mas_aibroker_dro_api_key + type: string + description: DRO API key + default: "" +- name: mas_aibroker_dro_url + type: string + description: DRO URL + default: "" +- name: mas_aibroker_dro_ca_cert + type: string + description: DRO CA certificate + default: "" +- name: mas_aibroker_db2_username + type: string + description: DB2 username + default: "" +- name: mas_aibroker_db2_password + type: string + description: DB2 password + default: "" +- name: mas_aibroker_db2_jdbc_url + type: string + description: DB2 JDBC URL + default: "" +- name: mas_aibroker_db2_ssl_enabled + type: string + description: DB2 SSL enabled + default: "false" +- name: mas_aibroker_db2_ca_cert + type: string + description: DB2 CA certificate + default: "" +- name: mas_aibroker_sls_secret_name + type: string + description: SLS secret name + default: "" +- name: mas_aibroker_sls_registration_key + type: string + description: SLS registration key + default: "" +- name: mas_aibroker_sls_url + type: string + description: SLS URL + default: "" +- name: mas_aibroker_sls_ca_cert + type: string + description: SLS CA certificate + default: "" +- name: environment_type + type: string + description: Environment type + default: "" diff --git a/tekton/src/params/install.yml.j2 b/tekton/src/params/install.yml.j2 index 288d8b4ac39..5694643a11c 100644 --- a/tekton/src/params/install.yml.j2 +++ b/tekton/src/params/install.yml.j2 @@ -687,9 +687,9 @@ description: controls the workload size of predict containers default: "small" -# Dependencies - IBM Maximo AI Broker +# Dependencies - IBM Maximo AI Service # ----------------------------------------------------------------------------- -{{ lookup('template', params_src_dir ~ '/install-aibroker.yml.j2') }} +{{ lookup('template', params_src_dir ~ '/install-aiservice.yml.j2') }} # MAS Application Configuration - Facilities # TODO: Fix type for storage sizes and max conn pool size diff --git a/tekton/src/pipelines/fvt-aiservice.yml.j2 b/tekton/src/pipelines/fvt-aiservice.yml.j2 new file mode 100644 index 00000000000..fa646da4dbc --- /dev/null +++ b/tekton/src/pipelines/fvt-aiservice.yml.j2 @@ -0,0 +1,132 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: mas-fvt-aibroker +spec: + workspaces: + # The generated configuration files + - name: shared-configs + + params: + # Tekton Pipeline image pull policy (for ibmmas/cli images) + - name: image_pull_policy + type: string + default: IfNotPresent + description: Pull policy for pipeline container images + + # MAS Configuration + - name: mas_app_channel_aibroker + type: string + default: "" + - name: mas_instance_id + type: string + default: "" + - name: mas_workspace_id + type: string + default: "" + + # FVT Configuration + - name: fvt_image_registry + type: string + default: "" + - name: fvt_artifactory_username + type: string + default: "" + - name: fvt_artifactory_token + type: string + default: "" + + # Image Digests + - name: fvt_digest_aibroker + type: string + description: FVT Digest - AIBroker + default: "" + - name: ivt_digest_core + type: string + description: IVT Digest - Core + default: "" + + tasks: + # 1. aibroker scan + - name: imagescan-aibroker + {{ lookup('template', 'taskdefs/ivt-core/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/ivt-core/common/params.yml.j2') | indent(8) }} + - name: product_id + value: ibm-mas-aibroker + - name: product_channel + value: $(params.mas_app_channel_aibroker) + - name: fvt_test_suite + value: imagescan + when: + - input: "$(params.ivt_digest_core)" + operator: notin + values: [""] + + # 2. Aibroker base test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-tenant-entitlement + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: tenant_entitlement + + # 3. Aibroker mcc test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-mcc + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: mcc + runAfter: + - fvt-aiservice-tenant-entitlement + + # 5. Aibroker fmea test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-fmea + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: fmea + runAfter: + - fvt-aiservice-tenant-entitlement + + # 4. Aibroker pcc test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-pcc + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: pcc + runAfter: + - fvt-aiservice-mcc + - fvt-aiservice-fmea + + # 6. Aibroker similarity test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-similarity + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: similarity + runAfter: + - fvt-aiservice-mcc + - fvt-aiservice-fmea + + # 7. Aibroker nl2oslc test suite + # ----------------------------------------------------------------------------- + - name: fvt-aiservice-nl2oslc + {{ lookup('template', 'taskdefs/fvt-aiservice/common/taskref.yml.j2') | indent(6) }} + params: + {{ lookup('template', 'taskdefs/fvt-aiservice/common/params.yml.j2') | indent(8) }} + - name: fvt_test_suite + value: nl2oslc + runAfter: + - fvt-aiservice-pcc + - fvt-aiservice-similarity diff --git a/tekton/src/pipelines/fvt-launcher.yml.j2 b/tekton/src/pipelines/fvt-launcher.yml.j2 index 2de816c0edd..aaa40433b98 100644 --- a/tekton/src/pipelines/fvt-launcher.yml.j2 +++ b/tekton/src/pipelines/fvt-launcher.yml.j2 @@ -21,6 +21,10 @@ spec: - name: mas_instance_id type: string + - name: skip_mas_install + type: string + default: "false" + - name: sync_with_install type: string default: "false" @@ -81,6 +85,10 @@ spec: type: string default: "false" description: "Set this to 'true' to enable launch of the Predict FVT pipeline after app-cfg-predict completes" + - name: launchfvt_aibroker + type: string + default: "false" + description: "Set this to 'true' to enable launch of the AIBroker FVT pipeline after app-cfg-aibroker completes" - name: launchfvt_visualinspection type: string default: "false" @@ -156,6 +164,9 @@ spec: - input: $(params.sync_with_install) operator: in values: ["true", "True"] + - input: $(params.skip_mas_install) + operator: notin + values: ["true", "True"] - name: launchfvt-sls timeout: "0" @@ -207,6 +218,10 @@ spec: - input: $(params.sync_with_install) operator: in values: ["true", "True"] + - input: $(params.skip_mas_install) + operator: notin + values: ["true", "True"] + runAfter: - launchfvt-core - launchfvt-sls @@ -1006,6 +1021,75 @@ spec: runAfter: - start-uninstall + # 1. Application FVT - AIBroker + # ------------------------------------------------------------------------- + # We start the AIBroker part of the FVT launcher + - name: waitfor-aibroker + timeout: "0" + taskRef: + kind: Task + name: mas-devops-wait-for-configmap + params: + - name: image_pull_policy + value: $(params.image_pull_policy) + - name: namespace + value: $(context.taskRun.namespace) + - name: configmap_name + value: approval-app-cfg-aibroker + - name: configmap_target_value + value: pending + # 25 retries at 5 minute intervals = 2 hours + - name: delay + value: "300" + - name: max_retries + value: "25" + - name: ignore_failure + value: "False" + when: + - input: $(params.launchfvt_aibroker) + operator: in + values: ["true", "True"] + - input: $(params.sync_with_install) + operator: in + values: ["true", "True"] + + - name: launchfvt-aibroker + timeout: "0" + params: + - name: image_pull_policy + value: $(params.image_pull_policy) + - name: pipelinerun_name + value: "$(params.mas_instance_id)-fvt-aibroker" + taskRef: + kind: Task + name: mas-launchfvt-aibroker + when: + - input: $(params.launchfvt_aibroker) + operator: in + values: ["true", "True"] + runAfter: + - waitfor-aibroker + + - name: approval-aibroker + timeout: "0" + taskRef: + kind: Task + name: mas-devops-update-configmap + params: + - name: image_pull_policy + value: $(params.image_pull_policy) + - name: configmap_name + value: approval-app-cfg-aibroker + - name: configmap_value + value: approved + when: + - input: $(params.sync_with_install) + operator: in + values: ["true", "True"] + runAfter: + - launchfvt-aibroker + + # Finally # --------------------------------------------------------------------------- finally: diff --git a/tekton/src/pipelines/install-aiservice.yml.j2 b/tekton/src/pipelines/install-aiservice.yml.j2 new file mode 100644 index 00000000000..13e13f19d1d --- /dev/null +++ b/tekton/src/pipelines/install-aiservice.yml.j2 @@ -0,0 +1,146 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: aiservice-install +spec: + workspaces: + # The generated configuration files + - name: shared-configs + # Any pre-generated configs that will be copied into the shared-configs workspace during suite-install + - name: shared-additional-configs + # The SLS entitlement key file that will be installed during install-sls. + - name: shared-entitlement + # Pre-generated certificates that will be copied into certs folder of shared-configs workspace to be used by suite-certs task + - name: shared-certificates + # PodTemplates configurations + - name: shared-pod-templates + + params: + # 1. Common Parameters + # ------------------------------------------------------------------------- + {{ lookup('template', params_src_dir ~ '/common.yml.j2') | indent(4) }} + + # 2. Installation + # ------------------------------------------------------------------------- + {{ lookup('template', params_src_dir ~ '/install-common.yml.j2') | indent(4) }} + {{ lookup('template', params_src_dir ~ '/install.yml.j2') | indent(4) }} + + tasks: + # Content + # ------- + # 1. Setup Cluster-wide Dependencies inc. Grafana, ECK, and Turbonomic + # 2. Install Universal Dependencies + # 3. Install & Configure Aibroker + + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/cluster-setup/ocp-verify.yml.j2', template_vars={'name': 'pre-install-check', 'devops_suite_name': 'pre-install-check'}) | indent(4) }} + + # 1. Setup Cluster-wide Dependencies & Configure Cluster Monitoring + # ------------------------------------------------------------------------- + # 1.1 IBM Operator Catalog + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/cluster-setup/ibm-catalogs.yml.j2') | indent(4) }} + runAfter: + - pre-install-check + + # 1.2 Red Hat Certificate Manager + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/cluster-setup/cert-manager.yml.j2') | indent(4) }} + runAfter: + - ibm-catalogs + + # 1.3 Configure Turbonomic + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/cluster-setup/turbonomic.yml.j2') | indent(4) }} + runAfter: + - pre-install-check + + # 2. Install Universal Dependencies + # ------------------------------------------------------------------------- + # 2.1 MongoDb + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/dependencies/mongo.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - cert-manager + when: + - input: "$(params.install_sls_aiservice)" + operator: notin + values: ["false", "False"] + + # 2.2 IBM SLS + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/dependencies/sls.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - mongodb + when: + - input: "$(params.install_sls_aiservice)" + operator: notin + values: ["false", "False"] + + # 2.3 DRO + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/dependencies/uds.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - ibm-catalogs + when: + - input: "$(params.install_dro_aiservice)" + operator: notin + values: ["false", "False"] + + # 3 Install and configure AI Broker + # ------------------------------------------------------------------------- + # 3.1 Install minio storage class + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/minio.yml.j2') | indent(4) }} + runAfter: + - uds + - sls + + # 3.2 Install mariabd + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/mariadb.yml.j2') | indent(4) }} + runAfter: + - sls + - uds + + # 3.3 Install db2 + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/dependencies/db2.yml.j2', template_vars={'suffix': 'aibroker'}) | indent(4) }} + runAfter: + - mariadb + + # 3.4 Install Opendatahub + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/odh.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - minio + - mariadb + + # 3.5 Install Kmodels + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/kmodels.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - odh + + # 3.6 Install and Configure Aibroker + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/aiservice.yml.j2', template_vars={'application': 'aibroker'}) | indent(4) }} + runAfter: + - kmodels + - db2-aibroker + + # 3.7 Aibroker tenant creation + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/aiservice-tenant.yml.j2') | indent(4) }} + runAfter: + - aibroker + + # 3.7 Aibroker Post Verification + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/aiservice-post-verify.yml.j2') | indent(4) }} + runAfter: + - aibroker-tenant + + finally: + # Update synchronization configmap + # ------------------------------------------------------------------------- + - name: sync-install + timeout: "0" + taskRef: + kind: Task + name: mas-devops-update-configmap + params: + - name: image_pull_policy + value: $(params.image_pull_policy) + - name: configmap_name + value: sync-install + - name: configmap_value + # An aggregate status of all the pipelineTasks under the tasks section (excluding the finally section). + # This variable is only available in the finally tasks and can have any one of the values (Succeeded, Failed, Completed, or None) + value: $(tasks.status) diff --git a/tekton/src/pipelines/install.yml.j2 b/tekton/src/pipelines/install.yml.j2 index b84de1e7aa5..8405067b630 100644 --- a/tekton/src/pipelines/install.yml.j2 +++ b/tekton/src/pipelines/install.yml.j2 @@ -355,22 +355,22 @@ spec: # 14 Install and configure AI Broker # ------------------------------------------------------------------------- # 14.1 Install Opendatahub - {{ lookup('template', pipeline_src_dir ~ '/taskdefs/apps/odh.yml.j2') | indent(4) }} + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/odh.yml.j2') | indent(4) }} runAfter: - suite-verify # 14.2 Install Kmodels - {{ lookup('template', pipeline_src_dir ~ '/taskdefs/apps/kmodels.yml.j2') | indent(4) }} + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/kmodels.yml.j2') | indent(4) }} runAfter: - odh # 14.3 Install Aibroker app - {{ lookup('template', pipeline_src_dir ~ '/taskdefs/apps/aibroker-app.yml.j2') | indent(4) }} + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/aiservice-app.yml.j2') | indent(4) }} runAfter: - kmodels # 14.4 Configure Aibroker - {{ lookup('template', pipeline_src_dir ~ '/taskdefs/apps/aibroker.yml.j2') | indent(4) }} + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/aiservice/aiservice.yml.j2') | indent(4) }} runAfter: - app-install-aibroker diff --git a/tekton/src/pipelines/taskdefs/apps/aibroker-app.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/aiservice-app.yml.j2 similarity index 100% rename from tekton/src/pipelines/taskdefs/apps/aibroker-app.yml.j2 rename to tekton/src/pipelines/taskdefs/aiservice/aiservice-app.yml.j2 diff --git a/tekton/src/pipelines/taskdefs/aiservice/aiservice-post-verify.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/aiservice-post-verify.yml.j2 new file mode 100644 index 00000000000..a72f06307d3 --- /dev/null +++ b/tekton/src/pipelines/taskdefs/aiservice/aiservice-post-verify.yml.j2 @@ -0,0 +1,25 @@ +- name: aibroker-post-verify + timeout: "0" + params: + {{ lookup('template', pipeline_src_dir ~ '/taskdefs/common/cli-params.yml.j2') | indent(4) }} + - name: devops_suite_name + value: app-aibroker-cfg + + - name: mas_instance_id + value: $(params.aibroker_instance_id) + - name: mas_app_id + value: aibroker + - name: mas_workspace_id + value: "$(params.mas_workspace_id)" + + taskRef: + name: aibroker-post-verify + kind: Task + # Only install aibroker if a channel has been chosen + when: + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: [""] + workspaces: + - name: configs + workspace: shared-configs \ No newline at end of file diff --git a/tekton/src/pipelines/taskdefs/aiservice/aiservice-tenant.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/aiservice-tenant.yml.j2 new file mode 100644 index 00000000000..2f7af682d60 --- /dev/null +++ b/tekton/src/pipelines/taskdefs/aiservice/aiservice-tenant.yml.j2 @@ -0,0 +1,122 @@ +- name: aibroker-tenant + timeout: "0" + params: + {{ lookup('template', 'taskdefs/common/cli-params.yml.j2') | indent(4) }} + - name: devops_suite_name + value: aibroker + - name: mas_instance_id + value: $(params.aibroker_instance_id) + - name: mas_app_id + value: aibroker + - name: artifactory_username + value: $(params.artifactory_username) + - name: artifactory_token + value: $(params.artifactory_token) + - name: mas_app_channel + value: "$(params.mas_app_channel_aibroker)" + - name: ibm_entitlement_key + value: $(params.ibm_entitlement_key) + - name: custom_labels + value: $(params.custom_labels) + - name: mas_aibroker_storage_provider + value: $(params.mas_aibroker_storage_provider) + - name: mas_aibroker_storage_accesskey + value: $(params.mas_aibroker_storage_accesskey) + - name: mas_aibroker_storage_secretkey + value: $(params.mas_aibroker_storage_secretkey) + - name: mas_aibroker_storage_host + value: $(params.mas_aibroker_storage_host) + - name: mas_aibroker_storage_port + value: $(params.mas_aibroker_storage_port) + - name: mas_aibroker_storage_ssl + value: $(params.mas_aibroker_storage_ssl) + - name: mas_aibroker_storage_region + value: $(params.mas_aibroker_storage_region) + - name: mas_aibroker_storage_pipelines_bucket + value: $(params.mas_aibroker_storage_pipelines_bucket) + - name: mas_aibroker_storage_tenants_bucket + value: $(params.mas_aibroker_storage_tenants_bucket) + - name: mas_aibroker_storage_templates_bucket + value: $(params.mas_aibroker_storage_templates_bucket) + - name: mas_aibroker_watsonxai_apikey + value: $(params.mas_aibroker_watsonxai_apikey) + - name: mas_aibroker_watsonxai_url + value: $(params.mas_aibroker_watsonxai_url) + - name: mas_aibroker_watsonxai_project_id + value: $(params.mas_aibroker_watsonxai_project_id) + - name: mas_aibroker_watsonx_action + value: $(params.mas_aibroker_watsonx_action) + - name: mas_aibroker_s3_action + value: $(params.mas_aibroker_s3_action) + - name: mas_aibroker_apikey_action + value: $(params.mas_aibroker_apikey_action) + - name: mas_aibroker_tenant_name + value: $(params.mas_aibroker_tenant_name) + - name: tenant_entitlement_type + value: $(params.tenant_entitlement_type) + - name: tenant_entitlement_start_date + value: $(params.tenant_entitlement_start_date) + - name: tenant_entitlement_end_date + value: $(params.tenant_entitlement_end_date) + - name: mas_icr_cp + value: $(params.mas_icr_cp) + - name: mas_icr_cpopen + value: $(params.mas_icr_cpopen) + - name: mas_aibroker_tenant_s3_bucket_prefix + value: $(params.mas_aibroker_tenant_s3_bucket_prefix) + - name: mas_aibroker_tenant_s3_region + value: $(params.mas_aibroker_tenant_s3_region) + - name: mas_aibroker_tenant_s3_endpoint_url + value: $(params.mas_aibroker_tenant_s3_endpoint_url) + - name: mas_aibroker_tenant_s3_access_key + value: $(params.mas_aibroker_tenant_s3_access_key) + - name: mas_aibroker_tenant_s3_secret_key + value: $(params.mas_aibroker_tenant_s3_secret_key) + - name: rsl_url + value: $(params.rsl_url) + - name: rsl_org_id + value: $(params.rsl_org_id) + - name: rsl_token + value: $(params.rsl_token) + - name: mas_aibroker_dro_secret_name + value: $(params.mas_aibroker_dro_secret_name) + - name: mas_aibroker_dro_api_key + value: $(params.mas_aibroker_dro_api_key) + - name: mas_aibroker_dro_url + value: $(params.mas_aibroker_dro_url) + - name: mas_aibroker_dro_ca_cert + value: $(params.mas_aibroker_dro_ca_cert) + - name: mas_aibroker_db2_username + value: $(params.mas_aibroker_db2_username) + - name: mas_aibroker_db2_password + value: $(params.mas_aibroker_db2_password) + - name: mas_aibroker_db2_jdbc_url + value: $(params.mas_aibroker_db2_jdbc_url) + - name: mas_aibroker_db2_ssl_enabled + value: $(params.mas_aibroker_db2_ssl_enabled) + - name: mas_aibroker_db2_ca_cert + value: $(params.mas_aibroker_db2_ca_cert) + - name: mas_aibroker_sls_secret_name + value: $(params.mas_aibroker_sls_secret_name) + - name: mas_aibroker_sls_registration_key + value: $(params.mas_aibroker_sls_registration_key) + - name: mas_aibroker_sls_url + value: $(params.mas_aibroker_sls_url) + - name: mas_aibroker_sls_ca_cert + value: $(params.mas_aibroker_sls_ca_cert) + - name: environment_type + value: $(params.environment_type) + + taskRef: + name: mas-devops-aibroker-tenant + kind: Task + when: + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: [""] + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: ["9.0.x"] + workspaces: + - name: configs + workspace: shared-configs \ No newline at end of file diff --git a/tekton/src/pipelines/taskdefs/apps/aibroker.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/aiservice.yml.j2 similarity index 52% rename from tekton/src/pipelines/taskdefs/apps/aibroker.yml.j2 rename to tekton/src/pipelines/taskdefs/aiservice/aiservice.yml.j2 index e43dbd383ac..04de2fa076f 100644 --- a/tekton/src/pipelines/taskdefs/apps/aibroker.yml.j2 +++ b/tekton/src/pipelines/taskdefs/aiservice/aiservice.yml.j2 @@ -4,8 +4,14 @@ {{ lookup('template', 'taskdefs/common/cli-params.yml.j2') | indent(4) }} - name: devops_suite_name value: aibroker + - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} + - name: mas_app_id value: aibroker - name: artifactory_username @@ -52,6 +58,55 @@ value: $(params.mas_aibroker_apikey_action) - name: mas_aibroker_tenant_name value: $(params.mas_aibroker_tenant_name) + - name: mas_icr_cp + value: $(params.mas_icr_cp) + - name: mas_icr_cpopen + value: $(params.mas_icr_cpopen) + - name: mas_aibroker_s3_bucket_prefix + value: $(params.mas_aibroker_s3_bucket_prefix) + - name: mas_aibroker_s3_endpoint_url + value: $(params.mas_aibroker_s3_endpoint_url) + - name: mas_aibroker_db_host + value: $(params.mas_aibroker_db_host) + - name: mas_aibroker_db_port + value: $(params.mas_aibroker_db_port) + - name: mas_aibroker_db_user + value: $(params.mas_aibroker_db_user) + - name: mas_aibroker_db_database + value: $(params.mas_aibroker_db_database) + - name: mas_aibroker_db_secret_name + value: $(params.mas_aibroker_db_secret_name) + - name: mas_aibroker_db_secret_value + value: $(params.mas_aibroker_db_secret_value) + - name: mas_aibroker_dro_secret_name + value: $(params.mas_aibroker_dro_secret_name) + - name: mas_aibroker_dro_api_key + value: $(params.mas_aibroker_dro_api_key) + - name: mas_aibroker_dro_url + value: $(params.mas_aibroker_dro_url) + - name: mas_aibroker_dro_ca_cert + value: $(params.mas_aibroker_dro_ca_cert) + - name: mas_aibroker_db2_username + value: $(params.mas_aibroker_db2_username) + - name: mas_aibroker_db2_password + value: $(params.mas_aibroker_db2_password) + - name: mas_aibroker_db2_jdbc_url + value: $(params.mas_aibroker_db2_jdbc_url) + - name: mas_aibroker_db2_ssl_enabled + value: $(params.mas_aibroker_db2_ssl_enabled) + - name: mas_aibroker_db2_ca_cert + value: $(params.mas_aibroker_db2_ca_cert) + - name: mas_aibroker_sls_secret_name + value: $(params.mas_aibroker_sls_secret_name) + - name: mas_aibroker_sls_registration_key + value: $(params.mas_aibroker_sls_registration_key) + - name: mas_aibroker_sls_url + value: $(params.mas_aibroker_sls_url) + - name: mas_aibroker_sls_ca_cert + value: $(params.mas_aibroker_sls_ca_cert) + - name: environment_type + value: $(params.environment_type) + taskRef: name: mas-devops-aibroker kind: Task @@ -59,3 +114,6 @@ - input: "$(params.mas_app_channel_aibroker)" operator: notin values: [""] + workspaces: + - name: configs + workspace: shared-configs diff --git a/tekton/src/pipelines/taskdefs/apps/kmodels.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/kmodels.yml.j2 similarity index 90% rename from tekton/src/pipelines/taskdefs/apps/kmodels.yml.j2 rename to tekton/src/pipelines/taskdefs/aiservice/kmodels.yml.j2 index 81e2416d80f..977a8347a1c 100644 --- a/tekton/src/pipelines/taskdefs/apps/kmodels.yml.j2 +++ b/tekton/src/pipelines/taskdefs/aiservice/kmodels.yml.j2 @@ -4,7 +4,11 @@ - name: devops_suite_name value: kmodels - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} - name: mas_app_id value: aibroker - name: artifactory_username @@ -49,6 +53,10 @@ value: $(params.mas_aibroker_pipeline_steps_tag) - name: mas_aibroker_tenant_name value: $(params.mas_aibroker_tenant_name) + - name: mas_icr_cp + value: $(params.mas_icr_cp) + - name: mas_icr_cpopen + value: $(params.mas_icr_cpopen) taskRef: name: mas-devops-kmodels diff --git a/tekton/src/pipelines/taskdefs/aiservice/mariadb.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/mariadb.yml.j2 new file mode 100644 index 00000000000..790f7eb994e --- /dev/null +++ b/tekton/src/pipelines/taskdefs/aiservice/mariadb.yml.j2 @@ -0,0 +1,36 @@ +- name: mariadb + timeout: "0" + params: + {{ lookup('template', 'taskdefs/common/cli-params.yml.j2') | indent(4) }} + - name: devops_suite_name + value: aibroker + - name: mas_instance_id + value: $(params.aibroker_instance_id) + - name: mas_app_id + value: aibroker + - name: artifactory_username + value: $(params.artifactory_username) + - name: artifactory_token + value: $(params.artifactory_token) + - name: mas_app_channel + value: "$(params.mas_app_channel_aibroker)" + - name: ibm_entitlement_key + value: $(params.ibm_entitlement_key) + - name: mas_aibroker_db_user + value: $(params.mas_aibroker_db_user) + - name: mas_aibroker_db_secret_value + value: $(params.mas_aibroker_db_secret_value) + - name: mas_aibroker_db_database + value: $(params.mas_aibroker_db_database) + - name: mariadb_user + value: $(params.mariadb_user) + - name: mariadb_password + value: $(params.mariadb_password) + + taskRef: + name: mas-devops-mariadb + kind: Task + when: + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: [""] diff --git a/tekton/src/pipelines/taskdefs/aiservice/minio.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/minio.yml.j2 new file mode 100644 index 00000000000..d9375de7d4c --- /dev/null +++ b/tekton/src/pipelines/taskdefs/aiservice/minio.yml.j2 @@ -0,0 +1,42 @@ +- name: minio + timeout: "0" + params: + {{ lookup('template', 'taskdefs/common/cli-params.yml.j2') | indent(4) }} + - name: devops_suite_name + value: aibroker + - name: mas_instance_id + value: $(params.aibroker_instance_id) + - name: mas_app_id + value: aibroker + - name: artifactory_username + value: $(params.artifactory_username) + - name: artifactory_token + value: $(params.artifactory_token) + - name: mas_app_channel + value: "$(params.mas_app_channel_aibroker)" + - name: ibm_entitlement_key + value: $(params.ibm_entitlement_key) + - name: mas_aibroker_storage_provider + value: $(params.mas_aibroker_storage_provider) + - name: mas_aibroker_storage_accesskey + value: $(params.mas_aibroker_storage_accesskey) + - name: mas_aibroker_storage_secretkey + value: $(params.mas_aibroker_storage_secretkey) + - name: minio_root_user + value: $(params.minio_root_user) + - name: minio_root_password + value: $(params.minio_root_password) + + taskRef: + name: mas-devops-minio + kind: Task + when: + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: [""] + - input: "$(params.mas_aibroker_storage_provider)" + operator: in + values: ["minio"] + - input: "$(params.install_minio_aiservice)" + operator: notin + values: ["false", "False"] \ No newline at end of file diff --git a/tekton/src/pipelines/taskdefs/apps/odh.yml.j2 b/tekton/src/pipelines/taskdefs/aiservice/odh.yml.j2 similarity index 94% rename from tekton/src/pipelines/taskdefs/apps/odh.yml.j2 rename to tekton/src/pipelines/taskdefs/aiservice/odh.yml.j2 index 0eff62743a3..1b220f29e01 100644 --- a/tekton/src/pipelines/taskdefs/apps/odh.yml.j2 +++ b/tekton/src/pipelines/taskdefs/aiservice/odh.yml.j2 @@ -5,7 +5,11 @@ value: odh - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} - name: mas_app_id value: aibroker - name: artifactory_username diff --git a/tekton/src/pipelines/taskdefs/dependencies/db2.yml.j2 b/tekton/src/pipelines/taskdefs/dependencies/db2.yml.j2 index c2a7f54156b..f3f9bbac5f9 100644 --- a/tekton/src/pipelines/taskdefs/dependencies/db2.yml.j2 +++ b/tekton/src/pipelines/taskdefs/dependencies/db2.yml.j2 @@ -6,7 +6,11 @@ value: dependencies-db2-{{ suffix }} - name: mas_instance_id +{% if suffix == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} # Entitlement - name: ibm_entitlement_key @@ -31,6 +35,9 @@ value: $(params.db2_type) - name: db2_timezone value: $(params.db2_timezone) +{% endif %} +{% if suffix == "aibroker" %} + value: aibroker {% endif %} - name: db2_namespace value: $(params.db2_namespace) diff --git a/tekton/src/pipelines/taskdefs/dependencies/mongo.yml.j2 b/tekton/src/pipelines/taskdefs/dependencies/mongo.yml.j2 index a43e4f78be0..375ea99d804 100644 --- a/tekton/src/pipelines/taskdefs/dependencies/mongo.yml.j2 +++ b/tekton/src/pipelines/taskdefs/dependencies/mongo.yml.j2 @@ -6,7 +6,11 @@ value: dependencies-mongodb - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} - name: mongodb_namespace value: $(params.mongodb_namespace) diff --git a/tekton/src/pipelines/taskdefs/dependencies/sls.yml.j2 b/tekton/src/pipelines/taskdefs/dependencies/sls.yml.j2 index e9758ad9f1d..c3467779ddd 100644 --- a/tekton/src/pipelines/taskdefs/dependencies/sls.yml.j2 +++ b/tekton/src/pipelines/taskdefs/dependencies/sls.yml.j2 @@ -6,7 +6,11 @@ value: dependencies-sls - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} - name: artifactory_username value: $(params.artifactory_username) diff --git a/tekton/src/pipelines/taskdefs/dependencies/uds.yml.j2 b/tekton/src/pipelines/taskdefs/dependencies/uds.yml.j2 index d04b4213d13..105f028711a 100644 --- a/tekton/src/pipelines/taskdefs/dependencies/uds.yml.j2 +++ b/tekton/src/pipelines/taskdefs/dependencies/uds.yml.j2 @@ -4,8 +4,14 @@ {{ lookup('template', pipeline_src_dir ~ '/taskdefs/common/cli-params.yml.j2') | indent(4) }} - name: uds_action value: "$(params.uds_action)" + - name: mas_instance_id +{% if application is defined and application == "aibroker" %} + value: $(params.aibroker_instance_id) +{% else %} value: $(params.mas_instance_id) +{% endif %} + - name: custom_labels value: $(params.custom_labels) - name: ocp_ingress_tls_secret_name diff --git a/tekton/src/pipelines/taskdefs/fvt-aiservice/common/params.yml.j2 b/tekton/src/pipelines/taskdefs/fvt-aiservice/common/params.yml.j2 new file mode 100644 index 00000000000..91a8db02c90 --- /dev/null +++ b/tekton/src/pipelines/taskdefs/fvt-aiservice/common/params.yml.j2 @@ -0,0 +1,16 @@ +- name: mas_instance_id + value: $(params.mas_instance_id) +- name: mas_workspace_id + value: $(params.mas_workspace_id) +- name: fvt_image_registry + value: $(params.fvt_image_registry) +- name: fvt_image_namespace + value: fvt-aibroker +- name: fvt_image_name + value: fvt-ibm-mas-aibroker +- name: fvt_image_digest + value: $(params.fvt_digest_aibroker) +- name: product_channel + value: $(params.mas_app_channel_aibroker) +- name: product_id + value: ibm-mas-aibroker \ No newline at end of file diff --git a/tekton/src/pipelines/taskdefs/fvt-aiservice/common/taskref.yml.j2 b/tekton/src/pipelines/taskdefs/fvt-aiservice/common/taskref.yml.j2 new file mode 100644 index 00000000000..902da8f3e02 --- /dev/null +++ b/tekton/src/pipelines/taskdefs/fvt-aiservice/common/taskref.yml.j2 @@ -0,0 +1,14 @@ +timeout: "2h" +taskRef: + kind: Task + name: mas-fvt-run-suite +when: + - input: "$(params.fvt_digest_aibroker)" + operator: notin + values: [""] + - input: "$(params.mas_app_channel_aibroker)" + operator: notin + values: [""] +workspaces: + - name: configs + workspace: shared-configs \ No newline at end of file diff --git a/tekton/src/tasks/aiservice/aiservice-post-verify.yml.j2 b/tekton/src/tasks/aiservice/aiservice-post-verify.yml.j2 new file mode 100644 index 00000000000..682343690f3 --- /dev/null +++ b/tekton/src/tasks/aiservice/aiservice-post-verify.yml.j2 @@ -0,0 +1,78 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: aibroker-post-verify +spec: + params: + {{ lookup('template', task_src_dir ~ '/common/cli-params.yml.j2') | indent(4) }} + + # MAS Details + - name: mas_instance_id + type: string + description: Instance ID + + # Application Workspace - identifier + - name: mas_app_id + type: string + description: Maximo Application Suite Application ID + - name: mas_workspace_id + type: string + description: Maximo Application Suite Workspace ID + + stepTemplate: + env: + {{ lookup('template', task_src_dir ~ '/common/cli-env.yml.j2') | indent(6) }} + + # MAS Details + - name: MAS_CONFIG_DIR + value: /workspace/configs + - name: MAS_INSTANCE_ID + value: $(params.mas_instance_id) + + # Application Workspace - Identifer + - name: MAS_APP_ID + value: $(params.mas_app_id) + - name: MAS_WORKSPACE_ID + value: $(params.mas_workspace_id) + + steps: + + # If configmap/approval-app-cfg-$(params.mas_app_id) exists then set STATUS=pending and wait for it to be changed to "approved" + - name: app-cfg-post-verify + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) + command: + - /opt/app-root/src/wait-for-configmap.sh + env: + - name: NAMESPACE + value: $(context.taskRun.namespace) + - name: CONFIGMAP_NAME + value: approval-app-cfg-$(params.mas_app_id) + - name: CONFIGMAP_INITIAL_VALUE + value: pending + - name: CONFIGMAP_TARGET_VALUE + value: approved + - name: DELAY + valueFrom: + configMapKeyRef: + name: approval-app-cfg-$(params.mas_app_id) + key: DELAY + optional: true + - name: MAX_RETRIES + valueFrom: + configMapKeyRef: + name: approval-app-cfg-$(params.mas_app_id) + key: MAX_RETRIES + optional: true + - name: IGNORE_FAILURE + valueFrom: + configMapKeyRef: + name: approval-app-cfg-$(params.mas_app_id) + key: IGNORE_FAILURE + optional: true + + workspaces: + - name: configs + - name: pod-templates + optional: true diff --git a/tekton/src/tasks/aiservice/aiservice-tenant.yml.j2 b/tekton/src/tasks/aiservice/aiservice-tenant.yml.j2 new file mode 100644 index 00000000000..510a828d432 --- /dev/null +++ b/tekton/src/tasks/aiservice/aiservice-tenant.yml.j2 @@ -0,0 +1,361 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: mas-devops-aibroker-tenant +spec: + params: + {{ lookup('template', task_src_dir ~ '/common/cli-params.yml.j2') | indent(4) }} + + # Pre-Release Support + - name: artifactory_username + default: '' + type: string + description: Required to use development MAS builds + - name: artifactory_token + default: '' + type: string + description: Required to use development MAS builds + + # Entitlement + - name: ibm_entitlement_key + type: string + + # MAS Details + - name: mas_instance_id + type: string + description: Instance ID + + # Artifactory details + - name: mas_icr_cp + type: string + default: "" + - name: mas_icr_cpopen + type: string + default: "" + + # Application - Operator + # - name: mas_aibroker_channel + - name: mas_app_channel + type: string + description: Catalog channel for the application operator subscription + + # Application - Operand + - name: mas_app_bindings_jdbc + default: "" + type: string + + - name: mas_app_plan + type: string + description: Application installation plan + default: "" + + # Custom Label Support + - name: custom_labels + type: string + description: Optional MAS custom labels, comma separated list of key=value pairs + default: "" + + # AI Broker actions + - name: mas_aibroker_s3_action + type: string + description: + default: "This option is optional" + - name: mas_aibroker_apikey_action + type: string + description: + default: "This option is optional" + + # WatsonX params + - name: mas_aibroker_watsonxai_apikey + type: string + description: + default: "This option is mandatory" + - name: mas_aibroker_watsonxai_url + type: string + description: + default: "This option is mandatory" + - name: mas_aibroker_watsonxai_project_id + type: string + description: + default: "This option is mandatory" + - name: mas_aibroker_watsonx_action + type: string + description: + default: "This option is mandatory" + + # AI Broker storage + - name: mas_aibroker_storage_provider + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_accesskey + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_secretkey + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_host + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_port + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_ssl + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_region + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_pipelines_bucket + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_tenants_bucket + type: string + default: "This option is mandatory" + default: "" + - name: mas_aibroker_storage_templates_bucket + type: string + default: "This option is mandatory" + default: "" + + # Ai Broker Tenant + - name: tenant_entitlement_type + type: string + - name: tenant_entitlement_start_date + type: string + - name: tenant_entitlement_end_date + type: string + - name: mas_aibroker_tenant_s3_bucket_prefix + type: string + - name: mas_aibroker_tenant_s3_region + type: string + - name: mas_aibroker_tenant_s3_endpoint_url + type: string + - name: mas_aibroker_tenant_s3_access_key + type: string + - name: mas_aibroker_tenant_s3_secret_key + type: string + - name: rsl_url + type: string + - name: rsl_org_id + type: string + - name: rsl_token + type: string + + - name: mas_aibroker_dro_secret_name + type: string + description: DRO secret name + default: "" + - name: mas_aibroker_dro_api_key + type: string + description: DRO API key + default: "" + - name: mas_aibroker_dro_url + type: string + description: DRO URL + default: "" + - name: mas_aibroker_dro_ca_cert + type: string + description: DRO CA certificate + default: "" + - name: mas_aibroker_db2_username + type: string + description: DB2 username + default: "" + - name: mas_aibroker_db2_password + type: string + description: DB2 password + default: "" + - name: mas_aibroker_db2_jdbc_url + type: string + description: DB2 JDBC URL + default: "" + - name: mas_aibroker_db2_ssl_enabled + type: string + description: DB2 SSL enabled + default: "false" + - name: mas_aibroker_db2_ca_cert + type: string + description: DB2 CA certificate + default: "" + - name: mas_aibroker_sls_secret_name + type: string + description: SLS secret name + default: "" + - name: mas_aibroker_sls_registration_key + type: string + description: SLS registration key + default: "" + - name: mas_aibroker_sls_url + type: string + description: SLS URL + default: "" + - name: mas_aibroker_sls_ca_cert + type: string + description: SLS CA certificate + default: "" + - name: environment_type + type: string + description: Environment type + default: "" + + stepTemplate: + env: + {{ lookup('template', task_src_dir ~ '/common/cli-env.yml.j2') | indent(6) }} + + # Pre-Release Support + - name: ARTIFACTORY_USERNAME + value: $(params.artifactory_username) + - name: ARTIFACTORY_TOKEN + value: $(params.artifactory_token) + + # Entitlement + - name: IBM_ENTITLEMENT_KEY + value: $(params.ibm_entitlement_key) + + # MAS Details + - name: MAS_INSTANCE_ID + value: $(params.mas_instance_id) + + # Application - Operator + # - name: MAS_AIBROKER_CHANNEL + # value: $(params.mas_aibroker_channel) + - name: MAS_APP_CHANNEL + value: $(params.mas_app_channel) + - name: MAS_CONFIG_DIR + value: /workspace/configs + + # Artifactory Details + - name: MAS_ICR_CP + value: $(params.mas_icr_cp) + - name: MAS_ICR_CPOPEN + value: $(params.mas_icr_cpopen) + + # Custom Label Support + - name: CUSTOM_LABELS + value: $(params.custom_labels) + + # AI Broker action for create/delete s3 secret + - name: MAS_AIBROKER_S3_ACTION + value: $(params.mas_aibroker_s3_action) + + # AI Broker action for create/delete api key + - name: MAS_AIBROKER_APIKEY_ACTION + value: $(params.mas_aibroker_apikey_action) + + # WatsonX api key + - name: MAS_AIBROKER_WATSONXAI_APIKEY + value: $(params.mas_aibroker_watsonxai_apikey) + + # WatsonX url + - name: MAS_AIBROKER_WATSONXAI_URL + value: $(params.mas_aibroker_watsonxai_url) + + # WatsonX project id + - name: MAS_AIBROKER_WATSONXAI_PROJECT_ID + value: $(params.mas_aibroker_watsonxai_project_id) + + # WatsonX action + - name: MAS_AIBROKER_WATSONX_ACTION + value: $(params.mas_aibroker_watsonx_action) + + # Opendatahub storage details + - name: MAS_AIBROKER_STORAGE_PROVIDER + value: $(params.mas_aibroker_storage_provider) + - name: MAS_AIBROKER_STORAGE_ACCESSKEY + value: $(params.mas_aibroker_storage_accesskey) + - name: MAS_AIBROKER_STORAGE_SECRETKEY + value: $(params.mas_aibroker_storage_secretkey) + - name: MAS_AIBROKER_STORAGE_HOST + value: $(params.mas_aibroker_storage_host) + - name: MAS_AIBROKER_STORAGE_PORT + value: $(params.mas_aibroker_storage_port) + - name: MAS_AIBROKER_STORAGE_SSL + value: $(params.mas_aibroker_storage_ssl) + - name: MAS_AIBROKER_STORAGE_REGION + value: $(params.mas_aibroker_storage_region) + - name: MAS_AIBROKER_STORAGE_PIPELINES_BUCKET + value: $(params.mas_aibroker_storage_pipelines_bucket) + - name: MAS_AIBROKER_STORAGE_TENANTS_BUCKET + value: $(params.mas_aibroker_storage_tenants_bucket) + - name: MAS_AIBROKER_STORAGE_TEMPLATES_BUCKET + value: $(params.mas_aibroker_storage_templates_bucket) + + # Ai Broker Tenant + - name: MAS_AIBROKER_TENANT_ENTITLEMENT_TYPE + value: $(params.tenant_entitlement_type) + - name: MAS_AIBROKER_TENANT_ENTITLEMENT_START_DATE + value: $(params.tenant_entitlement_start_date) + - name: MAS_AIBROKER_TENANT_ENTITLEMENT_END_DATE + value: $(params.tenant_entitlement_end_date) + - name: MAS_AIBROKER_TENANT_S3_REGION + value: $(params.mas_aibroker_tenant_s3_region) + - name: MAS_AIBROKER_TENANT_S3_BUCKET_PREFIX + value: $(params.mas_aibroker_tenant_s3_bucket_prefix) + - name: MAS_AIBROKER_TENANT_S3_ENDPOINT_URL + value: $(params.mas_aibroker_tenant_s3_endpoint_url) + - name: MAS_AIBROKER_TENANT_S3_ACCESS_KEY + value: $(params.mas_aibroker_tenant_s3_access_key) + - name: MAS_AIBROKER_TENANT_S3_SECRET_KEY + value: $(params.mas_aibroker_tenant_s3_secret_key) + - name: RSL_URL + value: $(params.rsl_url) + - name: RSL_ORG_ID + value: $(params.rsl_org_id) + - name: RSL_TOKEN + value: $(params.rsl_token) + + # Ai Broker Saas + - name: MAS_AIBROKER_SAAS + value: 'true' + + # DRO, DB2, SLS vars + - name: MAS_AIBROKER_DRO_SECRET_NAME + value: $(params.mas_aibroker_dro_secret_name) + - name: MAS_AIBROKER_DRO_API_KEY + value: $(params.mas_aibroker_dro_api_key) + - name: MAS_AIBROKER_DRO_URL + value: $(params.mas_aibroker_dro_url) + - name: MAS_AIBROKER_DRO_CA_CERT + value: $(params.mas_aibroker_dro_ca_cert) + - name: MAS_AIBROKER_DB2_USERNAME + value: $(params.mas_aibroker_db2_username) + - name: MAS_AIBROKER_DB2_PASSWORD + value: $(params.mas_aibroker_db2_password) + - name: MAS_AIBROKER_DB2_JDBC_URL + value: $(params.mas_aibroker_db2_jdbc_url) + - name: MAS_AIBROKER_DB2_SSL_ENABLED + value: $(params.mas_aibroker_db2_ssl_enabled) + - name: MAS_AIBROKER_DB2_CA_CERT + value: $(params.mas_aibroker_db2_ca_cert) + - name: MAS_AIBROKER_SLS_SECRET_NAME + value: $(params.mas_aibroker_sls_secret_name) + - name: MAS_AIBROKER_SLS_REGISTRATION_KEY + value: $(params.mas_aibroker_sls_registration_key) + - name: MAS_AIBROKER_SLS_URL + value: $(params.mas_aibroker_sls_url) + - name: MAS_AIBROKER_SLS_CA_CERT + value: $(params.mas_aibroker_sls_ca_cert) + - name: ENVIRONMENT_TYPE + value: $(params.environment_type) + + steps: + - name: aibroker-tenant + command: + - /opt/app-root/src/run-role.sh + - aibroker_tenant + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) + workingDir: /workspace/configs + + workspaces: + - name: configs + optional: true diff --git a/tekton/src/tasks/aibroker/aibroker.yml.j2 b/tekton/src/tasks/aiservice/aiservice.yml.j2 similarity index 55% rename from tekton/src/tasks/aibroker/aibroker.yml.j2 rename to tekton/src/tasks/aiservice/aiservice.yml.j2 index 01ab0cb9dbe..3fd20e45327 100644 --- a/tekton/src/tasks/aibroker/aibroker.yml.j2 +++ b/tekton/src/tasks/aiservice/aiservice.yml.j2 @@ -42,6 +42,14 @@ spec: description: Application installation plan default: "" + # Artifactory details + - name: mas_icr_cp + type: string + default: "" + - name: mas_icr_cpopen + type: string + default: "" + # Custom Label Support - name: custom_labels type: string @@ -117,6 +125,89 @@ spec: type: string default: "This option is mandatory" default: "" + - name: mas_aibroker_s3_bucket_prefix + type: string + default: "" + - name: mas_aibroker_s3_endpoint_url + type: string + default: "" + - name: mas_aibroker_db_host + type: string + default: "" + - name: mas_aibroker_db_port + type: string + default: "" + - name: mas_aibroker_db_user + type: string + default: "" + - name: mas_aibroker_db_database + type: string + default: "" + - name: mas_aibroker_db_secret_name + type: string + default: "" + - name: mas_aibroker_db_secret_key + type: string + default: "" + - name: mas_aibroker_db_secret_value + type: string + default: "" + - name: mas_aibroker_dro_secret_name + type: string + description: DRO secret name + default: "" + - name: mas_aibroker_dro_api_key + type: string + description: DRO API key + default: "" + - name: mas_aibroker_dro_url + type: string + description: DRO URL + default: "" + - name: mas_aibroker_dro_ca_cert + type: string + description: DRO CA certificate + default: "" + - name: mas_aibroker_db2_username + type: string + description: DB2 username + default: "" + - name: mas_aibroker_db2_password + type: string + description: DB2 password + default: "" + - name: mas_aibroker_db2_jdbc_url + type: string + description: DB2 JDBC URL + default: "" + - name: mas_aibroker_db2_ssl_enabled + type: string + description: DB2 SSL enabled + default: "false" + - name: mas_aibroker_db2_ca_cert + type: string + description: DB2 CA certificate + default: "" + - name: mas_aibroker_sls_secret_name + type: string + description: SLS secret name + default: "" + - name: mas_aibroker_sls_registration_key + type: string + description: SLS registration key + default: "" + - name: mas_aibroker_sls_url + type: string + description: SLS URL + default: "" + - name: mas_aibroker_sls_ca_cert + type: string + description: SLS CA certificate + default: "" + - name: environment_type + type: string + description: Environment type + default: "" stepTemplate: env: @@ -141,6 +232,16 @@ spec: # value: $(params.mas_aibroker_channel) - name: MAS_APP_CHANNEL value: $(params.mas_app_channel) + - name: MAS_CONFIG_DIR + value: /workspace/configs + - name: MAS_APP_ID + value: aibroker + + # Artifactory Details + - name: MAS_ICR_CP + value: $(params.mas_icr_cp) + - name: MAS_ICR_CPOPEN + value: $(params.mas_icr_cpopen) # Custom Label Support - name: CUSTOM_LABELS @@ -191,6 +292,58 @@ spec: value: $(params.mas_aibroker_storage_tenants_bucket) - name: MAS_AIBROKER_STORAGE_TEMPLATES_BUCKET value: $(params.mas_aibroker_storage_templates_bucket) + - name: MAS_AIBROKER_S3_BUCKET_PREFIX + value: $(params.mas_aibroker_s3_bucket_prefix) + - name: MAS_AIBROKER_S3_ENDPOINT_URL + value: $(params.mas_aibroker_s3_endpoint_url) + + # Ai Broker Saas + - name: MAS_AIBROKER_SAAS + value: 'true' + + # MariaDB details + - name: MAS_AIBROKER_DB_HOST + value: $(params.mas_aibroker_db_host) + - name: MAS_AIBROKER_DB_PORT + value: $(params.mas_aibroker_db_port) + - name: MAS_AIBROKER_DB_USER + value: $(params.mas_aibroker_db_user) + - name: MAS_AIBROKER_DB_DATABASE + value: $(params.mas_aibroker_db_database) + - name: MAS_AIBROKER_DB_SECRET_NAME + value: $(params.mas_aibroker_db_secret_name) + - name: MAS_AIBROKER_DB_SECRET_VALUE + value: $(params.mas_aibroker_db_secret_value) + + # DRO, DB2, SLS vars + - name: MAS_AIBROKER_DRO_SECRET_NAME + value: $(params.mas_aibroker_dro_secret_name) + - name: MAS_AIBROKER_DRO_API_KEY + value: $(params.mas_aibroker_dro_api_key) + - name: MAS_AIBROKER_DRO_URL + value: $(params.mas_aibroker_dro_url) + - name: MAS_AIBROKER_DRO_CA_CERT + value: $(params.mas_aibroker_dro_ca_cert) + - name: MAS_AIBROKER_DB2_USERNAME + value: $(params.mas_aibroker_db2_username) + - name: MAS_AIBROKER_DB2_PASSWORD + value: $(params.mas_aibroker_db2_password) + - name: MAS_AIBROKER_DB2_JDBC_URL + value: $(params.mas_aibroker_db2_jdbc_url) + - name: MAS_AIBROKER_DB2_SSL_ENABLED + value: $(params.mas_aibroker_db2_ssl_enabled) + - name: MAS_AIBROKER_DB2_CA_CERT + value: $(params.mas_aibroker_db2_ca_cert) + - name: MAS_AIBROKER_SLS_SECRET_NAME + value: $(params.mas_aibroker_sls_secret_name) + - name: MAS_AIBROKER_SLS_REGISTRATION_KEY + value: $(params.mas_aibroker_sls_registration_key) + - name: MAS_AIBROKER_SLS_URL + value: $(params.mas_aibroker_sls_url) + - name: MAS_AIBROKER_SLS_CA_CERT + value: $(params.mas_aibroker_sls_ca_cert) + - name: ENVIRONMENT_TYPE + value: $(params.environment_type) steps: - name: aibroker @@ -199,3 +352,8 @@ spec: - aibroker image: quay.io/ibmmas/cli:latest imagePullPolicy: $(params.image_pull_policy) + workingDir: /workspace/configs + + workspaces: + - name: configs + optional: true diff --git a/tekton/src/tasks/aibroker/kmodels.yml.j2 b/tekton/src/tasks/aiservice/kmodels.yml.j2 similarity index 93% rename from tekton/src/tasks/aibroker/kmodels.yml.j2 rename to tekton/src/tasks/aiservice/kmodels.yml.j2 index 20ba3fa174b..057cb4b2404 100644 --- a/tekton/src/tasks/aibroker/kmodels.yml.j2 +++ b/tekton/src/tasks/aiservice/kmodels.yml.j2 @@ -47,6 +47,14 @@ spec: description: Optional MAS custom labels, comma separated list of key=value pairs default: "" + # Artifactory details + - name: mas_icr_cp + type: string + default: "" + - name: mas_icr_cpopen + type: string + default: "" + # Kmodels details - name: mas_aibroker_controller_tag type: string @@ -137,6 +145,12 @@ spec: - name: CUSTOM_LABELS value: $(params.custom_labels) + # Artifactory Details + - name: MAS_ICR_CP + value: $(params.mas_icr_cp) + - name: MAS_ICR_CPOPEN + value: $(params.mas_icr_cpopen) + # Kmodels details - name: MAS_AIBROKER_CONTROLLER_TAG value: $(params.mas_aibroker_controller_tag) @@ -169,6 +183,11 @@ spec: - name: MAS_AIBROKER_STORAGE_REGION value: $(params.mas_aibroker_storage_region) + # Ai Broker Saas + - name: MAS_AIBROKER_SAAS + value: 'true' + + steps: - name: kmodels command: diff --git a/tekton/src/tasks/aiservice/mariadb.yml.j2 b/tekton/src/tasks/aiservice/mariadb.yml.j2 new file mode 100644 index 00000000000..344460435e5 --- /dev/null +++ b/tekton/src/tasks/aiservice/mariadb.yml.j2 @@ -0,0 +1,88 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: mas-devops-mariadb +spec: + params: + {{ lookup('template', task_src_dir ~ '/common/cli-params.yml.j2') | indent(4) }} + + # Pre-Release Support + - name: artifactory_username + default: '' + type: string + description: Required to use development MAS builds + - name: artifactory_token + default: '' + type: string + description: Required to use development MAS builds + + # Entitlement + - name: ibm_entitlement_key + type: string + + # MAS Details + - name: mas_instance_id + type: string + description: Instance ID + + - name: mariadb_user + type: string + description: user for mariadb + - name: mariadb_password + type: string + description: password for mariadb user + - name: mas_aibroker_db_user + type: string + description: database user for IBM Maximo AI Broker + default: "" + - name: mas_aibroker_db_database + type: string + description: database name for IBM Maximo AI Broker + default: "" + - name: mas_aibroker_db_secret_value + type: string + description: database secret value for IBM Maximo AI Broker + default: "" + + stepTemplate: + env: + {{ lookup('template', task_src_dir ~ '/common/cli-env.yml.j2') | indent(6) }} + + # Pre-Release Support + - name: ARTIFACTORY_USERNAME + value: $(params.artifactory_username) + - name: ARTIFACTORY_TOKEN + value: $(params.artifactory_token) + + # Entitlement + - name: IBM_ENTITLEMENT_KEY + value: $(params.ibm_entitlement_key) + + # MAS Details + - name: MAS_INSTANCE_ID + value: $(params.mas_instance_id) + + - name: MARIADB_NAMESPACE + value: mariadb + + - name: MARIADB_INSTANCE_NAME + value: mariadb-instance + + - name: MARIADB_USER + value: $(params.mariadb_user) + + - name: MARIADB_PASSWORD + value: $(params.mariadb_password) + + - name: MARIADB_DATABASE + value: $(params.mas_aibroker_db_database) + + + steps: + - name: mariadb + command: + - /opt/app-root/src/run-role.sh + - mariadb + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) diff --git a/tekton/src/tasks/aiservice/minio.yml.j2 b/tekton/src/tasks/aiservice/minio.yml.j2 new file mode 100644 index 00000000000..8a4e6301932 --- /dev/null +++ b/tekton/src/tasks/aiservice/minio.yml.j2 @@ -0,0 +1,73 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: mas-devops-minio +spec: + params: + {{ lookup('template', task_src_dir ~ '/common/cli-params.yml.j2') | indent(4) }} + + # Pre-Release Support + - name: artifactory_username + default: '' + type: string + description: Required to use development MAS builds + - name: artifactory_token + default: '' + type: string + description: Required to use development MAS builds + + # Entitlement + - name: ibm_entitlement_key + type: string + + # MAS Details + - name: mas_instance_id + type: string + description: Instance ID + + # Minio credentials + - name: minio_root_user + type: string + description: root user for minio + default: "" + - name: minio_root_password + type: string + + stepTemplate: + env: + {{ lookup('template', task_src_dir ~ '/common/cli-env.yml.j2') | indent(6) }} + + # Pre-Release Support + - name: ARTIFACTORY_USERNAME + value: $(params.artifactory_username) + - name: ARTIFACTORY_TOKEN + value: $(params.artifactory_token) + + # Entitlement + - name: IBM_ENTITLEMENT_KEY + value: $(params.ibm_entitlement_key) + + # MAS Details + - name: MAS_INSTANCE_ID + value: $(params.mas_instance_id) + + - name: MINIO_NAMESPACE + value: minio + + - name: MINIO_INSTANCE_NAME + value: minio + + - name: MINIO_ROOT_USER + value: $(params.minio_root_user) + + - name: MINIO_ROOT_PASSWORD + value: $(params.minio_root_password) + + steps: + - name: minio + command: + - /opt/app-root/src/run-role.sh + - minio + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) diff --git a/tekton/src/tasks/aibroker/odh.yml.j2 b/tekton/src/tasks/aiservice/odh.yml.j2 similarity index 100% rename from tekton/src/tasks/aibroker/odh.yml.j2 rename to tekton/src/tasks/aiservice/odh.yml.j2 diff --git a/tekton/src/tasks/fvt-launcher/launchfvt-aiservice.yml.j2 b/tekton/src/tasks/fvt-launcher/launchfvt-aiservice.yml.j2 new file mode 100644 index 00000000000..bf721b3ff43 --- /dev/null +++ b/tekton/src/tasks/fvt-launcher/launchfvt-aiservice.yml.j2 @@ -0,0 +1,120 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Task +metadata: + name: mas-launchfvt-aibroker +spec: + params: + # Control the image pull policy for the FVT container image + - name: image_pull_policy + type: string + default: IfNotPresent + + - name: pipelinerun_name + type: string + + steps: + - name: start-pipelinerun + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) + command: + - ansible-playbook + - /masfvt/fvt-aiservice.yml + env: + - name: IMAGE_PULL_POLICY + value: $(params.image_pull_policy) + - name: PIPELINERUN_NAME + value: $(params.pipelinerun_name) + - name: PIPELINERUN_NAMESPACE + value: $(context.taskRun.namespace) + + # Lookups from mas-devops + # ----------------------------------------------------------------------- + - name: DEVOPS_BUILD_NUMBER + valueFrom: + secretKeyRef: + name: mas-devops + key: DEVOPS_BUILD_NUMBER + optional: true + - name: AIBROKER_INSTANCE_ID + valueFrom: + secretKeyRef: + name: mas-devops + key: MAS_INSTANCE_ID + optional: false + + # Lookups from mas-fvt + # ----------------------------------------------------------------------- + - name: FVT_IMAGE_REGISTRY + valueFrom: + secretKeyRef: + name: mas-fvt + key: FVT_IMAGE_REGISTRY + optional: false + - name: FVT_ARTIFACTORY_USERNAME + valueFrom: + secretKeyRef: + name: mas-fvt + key: FVT_ARTIFACTORY_USERNAME + optional: false + - name: FVT_ARTIFACTORY_TOKEN + valueFrom: + secretKeyRef: + name: mas-fvt + key: FVT_ARTIFACTORY_TOKEN + optional: false + + # Lookups from mas-fvt-aibroker + # ----------------------------------------------------------------------- + # Framework Information + - name: MAS_APP_CHANNEL_AIBROKER + valueFrom: + secretKeyRef: + name: mas-fvt-aibroker + key: MAS_APP_CHANNEL_AIBROKER + optional: false + - name: MAS_WORKSPACE_ID + valueFrom: + secretKeyRef: + name: mas-fvt-aibroker + key: MAS_WORKSPACE_ID + optional: false + # Digests + - name: FVT_DIGEST_AIBROKER + valueFrom: + secretKeyRef: + name: mas-fvt-aibroker + key: FVT_DIGEST_AIBROKER + optional: false + - name: IVT_DIGEST_CORE + valueFrom: + secretKeyRef: + name: mas-fvt-aibroker + key: IVT_DIGEST_CORE + optional: false + + - name: wait-for-pipelinerun + image: quay.io/ibmmas/cli:latest + imagePullPolicy: $(params.image_pull_policy) + # 50 retries at 5 minute intervals = 4 hours + command: + - /opt/app-root/src/wait-for-tekton.sh + env: + - name: TYPE + value: pipelinerun + - name: NAME + value: $(params.pipelinerun_name) + - name: SUFFIX + valueFrom: + secretKeyRef: + name: mas-devops + key: DEVOPS_BUILD_NUMBER + optional: true + - name: NAMESPACE + value: $(context.taskRun.namespace) + - name: DELAY + value: "300" + - name: MAX_RETRIES + value: "50" + - name: IGNORE_FAILURE + value: "True" diff --git a/tekton/src/tasks/fvt/fvt-run-suite.yml.j2 b/tekton/src/tasks/fvt/fvt-run-suite.yml.j2 index c5e8b796428..fc00d585d75 100644 --- a/tekton/src/tasks/fvt/fvt-run-suite.yml.j2 +++ b/tekton/src/tasks/fvt/fvt-run-suite.yml.j2 @@ -161,6 +161,9 @@ spec: - name: CYPRESS_BASE_URL value: $(params.fvt_base_url) + - name: TEST_SUITE + value: $(params.fvt_test_suite) + # Test Data - name: DDP_APIKEY valueFrom: