# Control-M Self-Hosted -> SaaS Import Script

This script migrates a Control-M self-hosted **package TAR** (an EM export plus zero-or-more Control-M/Server exports) into a Control-M SaaS tenant. It uploads the export(s) to S3 via pre-signed URLs and drives the EM and CTM migration endpoints on the tenant's AAPI.

## Prerequisites

1. Python 3 with `pip` / `pip3` installed.
2. Install the script dependencies once:

   ```bash
   pip install -r requirements.txt
   ```

   (or `pip3 install -r requirements.txt`)

3. A Control-M SaaS tenant AAPI URL and API token (`x-api-key`).
4. A valid package TAR produced by the export script (see [Supported export TAR format](#supported-export-tar-format)).

## Quick Start

### Interactive mode

The wrapper prompts for the required inputs:

* Linux: `run_saas_migration_import.sh`
* Windows: `run_saas_migration_import.bat`

### Non-interactive mode

* Linux:

  ```bash
  run_saas_migration_import.sh \
      --tenant_aapi_url=<MANDATORY> \
      --aapi_token=<MANDATORY> \
      --export_file_location=<MANDATORY> \
      [optional flags]
  ```

* Windows:

  ```bat
  run_saas_migration_import.bat ^
      --tenant_aapi_url=<MANDATORY> ^
      --aapi_token=<MANDATORY> ^
      --export_file_location="<MANDATORY>" ^
      [optional flags]
  ```

You can also invoke the Python script directly:

```bash
python3 scripts/saas_migration_import.py --tenant_aapi_url=... --aapi_token=... --export_file_location=...
```

## CLI Flags

### Mandatory

| Flag | Description |
| --- | --- |
| `--tenant_aapi_url` | Control-M SaaS tenant AAPI URL, e.g. `mytenant-aapi.us1.ctmsaas.com`. Used to build `https://<url>/automation-api/migration/em` and `.../migration/ctms`. |
| `--aapi_token` | Control-M SaaS tenant AAPI token. Sent as the `x-api-key` HTTP header. |
| `--export_file_location` | Path to the package TAR to import. Must follow the [package TAR layout](#supported-export-tar-format). |

### Optional

| Flag | Default | Description |
| --- | --- | --- |
| `--python_exe` | `python3` | Python interpreter used by the wrapper script. |
| `--pre_sign_url_exp` | `10` | Expiration in minutes for the generated S3 pre-signed URL. |
| `--log_level` | `debug` | Log file verbosity. One of `debug`, `info`, `warning`, `error`. (Stdout is always `info`.) |
| `--logs_dir` | `logs` | Directory where the log file is written. Created if missing. |
| `--skip_tar_checksum_validation` | `false` | Skip the TAR checksum validation step on the package TAR. |
| `--skip_archive_rules` | `false` | Skip importing Workload Archiving Rules during the EM migration. |
| `--skip_push_definition` | `false` | Skip pushing job definitions into Control-M SaaS CTM servers. |
| `--skip_em_migration` | `false` | Skip the EM migration. The script then runs only the CTM migration(s) found in the package TAR. |
| `--skip_ctm_migration` | `false` | Skip the CTM migration(s). The script then runs only the EM migration. |
| `--ctm_server` | _none_ | Run the migration only for the given Control-M/Server name, using its per-server export TAR under `servers/<name>/` inside the package TAR. Implies skipping the EM migration and skipping every other CTM in the TAR. Cannot be combined with `--skip_ctm_migration=true` or `--em_ctm`. |
| `--em_ctm` | _none_ | Run a single-CTM migration using **only the inner EM TAR** (no per-server CTM tarballs required). Sets the `ctmName` HTTP header on the EM `/import` GET call so the SaaS backend treats the run as single-CTM mode (`isSingleCtmMode=true`) and dispatches to the single-CTM handlers. Cannot be combined with `--ctm_server`, `--skip_em_migration=true`, or `--data`. |
| `--max_parallel_ctms` | `2` | Maximum number of CTM migrations to run in parallel after the EM migration completes. Must be a positive integer. |
| `--interactive_mode` | `false` | When `true`, the script prints a migration summary and waits for user confirmation before starting. |

Boolean flags accept any of: `true|false|yes|no|y|n|t|f|on|off|1|0` (case-insensitive).

## Run Modes

The script picks one of five run modes from the flags above:

| Mode | Trigger | Behavior |
| --- | --- | --- |
| `em_then_ctms` (default) | Neither `--skip_*` nor `--ctm_server` nor `--em_ctm` set | Runs the EM migration, then all CTM migrations from the TAR in parallel. |
| `em_only` | `--skip_ctm_migration=true` | Runs only the EM migration. |
| `ctms_only` | `--skip_em_migration=true` | Runs only the CTM migrations from the TAR in parallel. |
| `single_ctm` | `--ctm_server=<name>` | Runs only the CTM migration whose folder in the TAR matches `<name>` (uses `servers/<name>/*.tar.gz`). EM and other CTMs are skipped. |
| `single_ctm_em_only` | `--em_ctm=<name>` | Runs only the EM migration, scoped to one Control-M/Server. Uses only the inner EM TAR (no per-server tar required). The `ctmName` header on the EM `/import` call triggers single-CTM handlers on the SaaS backend. |

Conflicting combinations are rejected up front with a clear error:

* `--ctm_server=...` together with `--skip_ctm_migration=true`.
* `--skip_em_migration=true` together with `--skip_ctm_migration=true` and no `--ctm_server`.
* `--em_ctm=...` together with `--ctm_server=...`.
* `--em_ctm=...` together with `--skip_em_migration=true`.
* `--em_ctm=...` together with `--data=...`.

## Supported export TAR format

`--export_file_location` must be a **package TAR** that bundles an EM export and zero-or-more Control-M/Server exports. Only the folder layout is constant - the inner TAR file names may be anything as long as they end with `.tar.gz`:

```text
em/<any-name>.tar.gz
servers/<ctmName>/<any-name>.tar.gz
```

The legacy EM-only export TAR (top-level `EMExportForSaaS/` directory) is no longer accepted.

## What the script does

When the script runs it:

1. Validates the package TAR envelope and extracts the inner EM TAR + every per-server CTM TAR to a temp directory under the OS temp dir (cleaned up automatically when the script exits).
2. If interactive mode is on, prints the migration summary and waits for confirmation.
3. Runs the EM migration flow against the inner EM TAR (skipped in `ctms_only` and `single_ctm` modes):
   * Requests an S3 pre-signed URL from `GET /automation-api/migration/em/s3access`.
   * Uploads the EM TAR to the returned pre-signed URL.
   * Starts the migration via `GET /automation-api/migration/em/import` with `s3AccessRequestId`, `skipArchiveRules`, `skipPushDefinition`, and `unsupportedJobTypes` HTTP headers. In `single_ctm_em_only` mode an additional `ctmName` header is sent so the SaaS backend dispatches to the single-CTM handlers (`isSingleCtmMode=true`).
   * Polls status via `GET /automation-api/migration/em/import/<requestId>`.
4. For every `servers/<ctmName>/*.tar.gz` entry it then runs the CTM migration in parallel (capped by `--max_parallel_ctms`), with the same upload/start/poll shape:
   * Requests an S3 pre-signed URL from `GET /automation-api/migration/em/s3access` with the `ctmName` HTTP header set to the folder name in the package TAR.
   * Uploads the CTM TAR to the returned pre-signed URL.
   * Starts the migration via `POST /automation-api/migration/ctms/import?userRequestId=<S3_ACCESS_REQUEST_ID>`. The body is JSON with snake_case keys:

     ```json
     {
       "ctm_name": "<ctmName>",
       "ctm_export_file_suffix": "/migration/CTMS/<ctmName>/<file-name>.tar.gz"
     }
     ```

     Headers on this request are limited to `x-api-key` and `Content-Type: application/json`.
   * Polls status via `GET /automation-api/migration/ctms/import/<requestId>` with the `ctmName` HTTP header.

A failure in one CTM migration does **not** abort the others. The script collects per-CTM results and prints a final summary table; the script exits non-zero if any CTM failed.

## Authentication and HTTP details

* `x-api-key`: every backend call carries the AAPI token from `--aapi_token`.
* `Content-Type: application/json`: set on every backend call.
* `ctmName` HTTP header:
  * Sent on the CTM `s3access` request and the CTM `/import/<requestId>` poll.
  * Not sent on the CTM start `POST /import` (the start request carries `ctm_name` in the JSON body instead).
  * Also sent on the EM `GET /import` start call when `--em_ctm` is used, so the SaaS backend treats the run as single-CTM mode against the inner EM TAR.
* The CTM start request carries `userRequestId=<S3_ACCESS_REQUEST_ID>` as a URL query parameter.
* Concurrent CTM migrations serialize their pre-sign HTTP calls (the backend rejects overlapping pre-signs with HTTP 409), but uploads and start/poll calls run in parallel.

## Logs

* Log file path: `<--logs_dir>/control_m_self_hosted_data_import_into_control_m_saas_<YYYY_MM_DD_HH_MM_SS>.log`.
* Log file verbosity is controlled by `--log_level`; stdout always logs at `info`.
* Each log line is prefixed with the owning thread (`[EM]`, `[CTM[<ctmName>]]`, `[MainThread]`) so concurrent CTM migrations stay readable on a single stdout.
* The CTM start `POST /import` request payload is logged at `info` level (with `x-api-key` redacted) for troubleshooting.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | All requested migrations completed successfully. |
| `1` | The script raised an unhandled exception (validation error, EM migration failure, at least one CTM failure, etc.). The error message and a path to the log file are printed to stdout. |

## Examples

* Run a full package (EM + all CTMs in parallel, default run mode):

  ```bash
  run_saas_migration_import.sh \
      --tenant_aapi_url=mytenant-aapi.us1.ctmsaas.com \
      --aapi_token=*** \
      --export_file_location=/tmp/ctm-package-with-ctms.tar.gz
  ```

* Run only the EM migration:

  ```bash
  run_saas_migration_import.sh ... --skip_ctm_migration=true
  ```

* Run only the CTM migrations from the package TAR:

  ```bash
  run_saas_migration_import.sh ... --skip_em_migration=true --max_parallel_ctms=4
  ```

* Run a single named CTM migration from the package TAR (uses `servers/<name>/*.tar.gz`):

  ```bash
  run_saas_migration_import.sh ... --ctm_server=st-ntx-m0ugk
  ```

* Run an EM-tar-only single-CTM migration (no per-server tar required - uses only the inner EM TAR, scoped to one Control-M/Server):

  ```bash
  run_saas_migration_import.sh ... --em_ctm=vl-em11941
  ```
