diff --git a/GLOSSARY.md b/GLOSSARY.md new file mode 100644 index 0000000..9ff0007 --- /dev/null +++ b/GLOSSARY.md @@ -0,0 +1,27 @@ +# Glossary + +Domain vocabulary for the openshift-virtualization-migration collection. + +## Environment validation + +The pre-migration validation pass (role `validate_environment`, playbook `vmf_validate_environment.yml`). Validates the migration environment before seeding AAP and running a migration: OpenShift API authentication, OpenShift Virtualization and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration. Read-only probes; fails the play with an actionable message per failed check. + +## Migration validation + +The post-migration validation pass (role `validate_migration`, playbook `vmf_validate_migration.yml`). Runs as a standalone playbook after `vmf_migrate` completes — the operator or an AAP job template invokes it; it is not wired into the MTV pipeline (the in-pipeline hook path is `mtv_hook_invoke_aap`). Validates migration outcomes and performs the source power-off. + +## Source VM + +The original VMware virtual machine being migrated to OpenShift Virtualization. Queried read-only via the vCenter API. + +## Migrated VM + +The OpenShift Virtualization virtual machine produced by a migration; the counterpart of one source VM. Verified via the Kubernetes API (VirtualMachine / VirtualMachineInstance status). + +## Migration plan + +The MTV (Forklift) Plan custom resource that groups source VMs with network and storage maps and drives the migration. Referenced by name and namespace (`mtv_migrate_plan_name`, `mtv_migrate_plan_namespace`). + +## Source power-off + +The post-migration cutover step of powering off source VMs whose migrated counterparts passed validation — a hard stop via the vCenter API. Performed by validate_migration, controlled by a toggle (default: on), gated per-VM and fail-closed — see docs/adr/0001-validate-migration-fail-closed-power-off.md. diff --git a/README.md b/README.md index 0af0c62..c5601f8 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ This collection includes the following roles for managing OpenShift Virtualizati * [mtv_provider](roles/mtv_provider/README.md) - Ensure host OpenShift provider and source provider CRs exist on target clusters. Designed to run from AAP with credential injection. * [mtv_query_inventory](roles/mtv_query_inventory/README.md) - Queries MTV inventory. * [pre_migration_vm](roles/pre_migration_vm/README.md) - Perform VM related activities prior to a migration -* [preflight](roles/preflight/README.md) - Validate the migration environment before seeding AAP — OpenShift API authentication, OpenShift Virtualization and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration. * [utility_aap_deploy](roles/utility_aap_deploy/README.md) - Deploys an instance of Ansible Automation Platform. * [utility_openshift_target_credential](roles/utility_openshift_target_credential/README.md) - A utility role to manage OpenShift target cluster credentials for migration * [utility_registry_credentials](roles/utility_registry_credentials/README.md) - Manages registry credentials in an OpenShift namespace. +* [validate_environment](roles/validate_environment/README.md) - Validate the migration environment before seeding AAP — OpenShift API authentication, OpenShift Virtualization and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration. ## Requirements diff --git a/changelogs/fragments/107-preflight-role.yml b/changelogs/fragments/107-preflight-role.yml deleted file mode 100644 index 49130db..0000000 --- a/changelogs/fragments/107-preflight-role.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -minor_changes: - - Add preflight role to validate the migration environment before seeding AAP — OpenShift API authentication, OpenShift Virtualization and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration. - - Add vmf_preflight.yml playbook to run the preflight role from a controller or an AAP job template. -bugfixes: - - Preserve an explicit false for validate_certs in the preflight role's vCenter and AAP TLS-verification cascade — the previous fallback converted it to true. -... diff --git a/changelogs/fragments/110-validate-environment-role.yml b/changelogs/fragments/110-validate-environment-role.yml new file mode 100644 index 0000000..7bb93bb --- /dev/null +++ b/changelogs/fragments/110-validate-environment-role.yml @@ -0,0 +1,6 @@ +--- +minor_changes: + - Add validate_environment role to validate the migration environment before seeding AAP and running a migration — OpenShift API auth, OpenShift Virtualization and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration (playbook vmf_validate_environment.yml). +bugfixes: + - Preserve an explicit false for validate_certs in the validate_environment role's vCenter and AAP TLS-verification cascade — the previous fallback converted it to true. +... diff --git a/changelogs/fragments/121-pin-range-safety.yml b/changelogs/fragments/121-pin-range-safety.yml new file mode 100644 index 0000000..e22a1e3 --- /dev/null +++ b/changelogs/fragments/121-pin-range-safety.yml @@ -0,0 +1,2 @@ +trivial: + - Add pin-range safety notes to the community.vmware module justification in validate_environment. diff --git a/docs/architecture-v2.md b/docs/architecture-v2.md index bd2e1af..a37cd1d 100644 --- a/docs/architecture-v2.md +++ b/docs/architecture-v2.md @@ -22,9 +22,9 @@ | `aap_cleanup` | **REWRITTEN** | AAP 2.5+ only, simplified dispatch | | `aap_machine_credentials` | **DEFERRED** to Phase 3 | Day 2 scope | | `mtv_management` | **REWRITTEN** | Error handling, provider lifecycle, `provider` var fix | -| `mtv_migrate` | **REWRITTEN** | `failure_strategy`, pre-flight, scheduling, cold migration | +| `mtv_migrate` | **REWRITTEN** | `failure_strategy`, environment validation, scheduling, cold migration | | `network_mgmt` | **REWRITTEN** | Bug fixes (typo), error handling, OVN/UDN prep | -| `validate_migration` | **ENHANCED** | Structured output, pre-flight integration | +| `validate_migration` | **ENHANCED** | Structured output, environment validation integration | | `cluster_healthcheck` | **ENHANCED** | Additional checks for v2 readiness | | `operator_management` | **SIMPLIFIED** | Fewer defaults, cleaner config | | `ocp_cleanup` | **KEPT** | Enhanced scoping | @@ -61,7 +61,7 @@ flowchart TD end subgraph Migration["Phase C: Migration Execution"] - PreFlight["Pre-flight validation\nstorage, network, provider checks"] + EnvValidation["Environment validation\nstorage, network, provider checks"] Migrate["mtv_migrate role\nPlan creation + execution"] Monitor["Migration monitoring\nfailure_strategy handling"] PostVal["Post-migration validation"] @@ -77,8 +77,8 @@ flowchart TD EnvMgr --> Ops Ops --> Net Net --> MTV - MigReq --> PreFlight - PreFlight --> Migrate + MigReq --> EnvValidation + EnvValidation --> Migrate Migrate --> Monitor Monitor --> PostVal Seed -.-> EnvConfig @@ -183,7 +183,7 @@ One primary orchestration workflow: ``` MF - Full Migration Workflow ├── MF - Configure Environment (source + target) -├── MF - Validate (pre-flight) +├── MF - Validate (environment) ├── MF - Migrate └── MF - Validate (post-migration) ``` @@ -407,7 +407,7 @@ environment_manager_environments: {} # same structure as environments.yml **New capabilities:** - `failure_strategy` parameter (MIG-05) -- Pre-flight validation (MIG-08) +- Environment validation (MIG-08) - Cold migration with shutdown/timeout (MIG-07) - Maintenance window scheduling (MIG-09) - Structured migration report output @@ -417,7 +417,7 @@ environment_manager_environments: {} # same structure as environments.yml mtv_migrate_failure_strategy: continue_and_report # continue_and_report | continue_and_retry | halt_on_failure mtv_migrate_max_retries: 3 -mtv_migrate_pre_flight: true +mtv_migrate_environment_validation: true mtv_migrate_shutdown_timeout: 300 mtv_migrate_force_shutdown: false mtv_migrate_cleanup_source: false @@ -430,9 +430,9 @@ mtv_migrate_maintenance_window_end: "" # ISO 8601 ```yaml # tasks/main.yml (conceptual structure) - block: - - name: Pre-flight validation - include_tasks: _pre_flight.yml - when: mtv_migrate_pre_flight + - name: Environment validation + include_tasks: _environment_validation.yml + when: mtv_migrate_environment_validation - name: Wait for maintenance window include_tasks: _schedule.yml @@ -652,7 +652,7 @@ infra.openshift_virtualization_migration/ │ ├── mtv_migrate/ │ │ ├── tasks/ │ │ │ ├── main.yml -│ │ │ ├── _pre_flight.yml +│ │ │ ├── _environment_validation.yml │ │ │ ├── _schedule.yml │ │ │ ├── _plans.yml │ │ │ ├── _execute.yml diff --git a/playbooks/vmf_preflight.yml b/playbooks/vmf_validate_environment.yml similarity index 59% rename from playbooks/vmf_preflight.yml rename to playbooks/vmf_validate_environment.yml index 631baa8..2417b68 100644 --- a/playbooks/vmf_preflight.yml +++ b/playbooks/vmf_validate_environment.yml @@ -5,7 +5,7 @@ gather_facts: false tasks: - - name: Run preflight validation checks + - name: Run environment validation checks ansible.builtin.import_role: - name: infra.openshift_virtualization_migration.preflight + name: infra.openshift_virtualization_migration.validate_environment ... diff --git a/roles/preflight/tasks/main.yml b/roles/preflight/tasks/main.yml deleted file mode 100644 index 9454cf3..0000000 --- a/roles/preflight/tasks/main.yml +++ /dev/null @@ -1,97 +0,0 @@ ---- -# Preflight validates the migration environment before seeding AAP. -# -# All checks run on every invocation; the summary report (always-tagged) -# fails the play if any ran check failed, so one run reports everything -# that needs attention. The summary asserts only over the checks that -# ran, so an isolated --tags debug run ends green when the targeted -# check passed. Checks whose target is not configured fail that check -# with a message describing what to set — they do not crash the play. - -- name: Include pre-validation of required connection variables - ansible.builtin.include_tasks: - file: pre_validate.yml - apply: - tags: - - always - tags: - - always - -- name: Run preflight checks - block: - - name: Include OpenShift API authentication check - ansible.builtin.include_tasks: - file: validate_openshift.yml - apply: - tags: - - openshift - - validate - tags: - - openshift - - validate - - - name: Include OpenShift Virtualization operator check - ansible.builtin.include_tasks: - file: validate_cnv.yml - apply: - tags: - - cnv - - validate - tags: - - cnv - - validate - - - name: Include MTV operator check - ansible.builtin.include_tasks: - file: validate_mtv.yml - apply: - tags: - - mtv - - validate - tags: - - mtv - - validate - - - name: Include AAP access check - ansible.builtin.include_tasks: - file: validate_aap.yml - apply: - tags: - - aap - - validate - tags: - - aap - - validate - - - name: Include vCenter connectivity check - ansible.builtin.include_tasks: - file: validate_vcenter.yml - apply: - tags: - - vcenter - - validate - tags: - - vcenter - - validate - - - name: Include VDDK init image check - ansible.builtin.include_tasks: - file: validate_vddk.yml - apply: - tags: - - vddk - - validate - tags: - - vddk - - validate - - always: - - name: Include the preflight summary report - ansible.builtin.include_tasks: - file: report.yml - apply: - tags: - - always - tags: - - always -... diff --git a/roles/preflight/tasks/validate_aap.yml b/roles/preflight/tasks/validate_aap.yml deleted file mode 100644 index 042fb5c..0000000 --- a/roles/preflight/tasks/validate_aap.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- -# Probe the AAP API with the configured account. -# ansible.builtin.uri is used instead of ansible.controller modules because -# the preflight runs before seeding and needs only a lightweight auth probe -# (the me/ endpoint returns the authenticated account); controller modules -# pull in full controller configuration context that does not exist yet. - -- name: validate_aap | Probe AAP API with the configured account - when: preflight_aap_hostname | length > 0 - ansible.builtin.uri: - url: "{{ preflight_aap_hostname }}{{ preflight_aap_api_path }}me/" - method: GET - validate_certs: "{{ preflight_aap_validate_certs }}" - headers: - Authorization: >- - {{ - ('Bearer ' ~ (preflight_aap_token | trim)) - if preflight_aap_token | length > 0 - else ('Basic ' ~ ((preflight_aap_username ~ ':' ~ preflight_aap_password) | b64encode)) - }} - status_code: [200, 401, 403] - register: __preflight_aap - failed_when: false - no_log: "{{ preflight_secure_logging }}" - -- name: validate_aap | Record the AAP access check result - ansible.builtin.set_fact: - __preflight_result_aap: - ran: true - passed: "{{ (preflight_aap_hostname | length > 0) and ((__preflight_aap.status | default(0)) == 200) }}" - detail: >- - {% if preflight_aap_hostname | length == 0 %} - Not configured — set aap_hostname in the inventory (or AAP_HOSTNAME) - to enable the AAP access check - {% elif (__preflight_aap.status | default(0)) == 200 %} - HTTP 200 at {{ preflight_aap_hostname }} (authenticated as - {{ __preflight_aap.json.results[0].username | default('unknown', true) }}) - {% else %} - HTTP {{ __preflight_aap.status | default('connection error') }} at - {{ preflight_aap_hostname }} — 401/403 means the account or token is - wrong or expired; 404 means the API path is wrong — AAP 2.4 - and earlier deployments need preflight_aap_api_path=/api/v2/ - {% endif %} -... diff --git a/roles/preflight/tasks/validate_cnv.yml b/roles/preflight/tasks/validate_cnv.yml deleted file mode 100644 index 75b9fab..0000000 --- a/roles/preflight/tasks/validate_cnv.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -# Query the ClusterServiceVersion list in the CNV namespace. -# kubernetes.core.k8s_info is certified content and the collection's -# established pattern for cluster queries. - -- name: validate_cnv | List ClusterServiceVersions in the CNV namespace - kubernetes.core.k8s_info: - host: "{{ preflight_openshift_host }}" - api_key: "{{ preflight_openshift_api_key }}" - validate_certs: "{{ preflight_openshift_verify_ssl }}" - api_version: operators.coreos.com/v1alpha1 - kind: ClusterServiceVersion - namespace: "{{ preflight_cnv_namespace }}" - register: __preflight_cnv_csvs - failed_when: false - no_log: "{{ preflight_secure_logging }}" - -- name: validate_cnv | Find the OpenShift Virtualization CSV - ansible.builtin.set_fact: - __preflight_cnv_found: >- - {{ __preflight_cnv_csvs.resources | default([]) - | selectattr('metadata.name', 'search', 'kubevirt-hyperconverged') - | list | length > 0 }} - __preflight_cnv_csv: >- - {{ __preflight_cnv_csvs.resources | default([]) - | selectattr('metadata.name', 'search', 'kubevirt-hyperconverged') - | list | first | default({}) }} - -- name: validate_cnv | Record the OpenShift Virtualization operator check result - ansible.builtin.set_fact: - __preflight_result_cnv: - ran: true - passed: "{{ __preflight_cnv_found - and (__preflight_cnv_csv.status.phase | default('')) == 'Succeeded' }}" - detail: >- - {% if __preflight_cnv_csvs.resources is not defined %} - Cluster API query failed in {{ preflight_cnv_namespace }} — - check the OpenShift connection and credentials first; the - operator check is inconclusive when the API is unreachable - {% elif not __preflight_cnv_found %} - No kubevirt-hyperconverged CSV found in - {{ preflight_cnv_namespace }} — install the OpenShift - Virtualization operator before seeding - {% elif (__preflight_cnv_csv.status.phase | default('')) != 'Succeeded' %} - CSV {{ __preflight_cnv_csv.metadata.name }} in - {{ preflight_cnv_namespace }} is - {{ __preflight_cnv_csv.status.phase | default('unknown') }} — - expected Succeeded - {% else %} - CSV {{ __preflight_cnv_csv.metadata.name }} Succeeded in - {{ preflight_cnv_namespace }} - {% endif %} -... diff --git a/roles/preflight/tasks/validate_mtv.yml b/roles/preflight/tasks/validate_mtv.yml deleted file mode 100644 index b1e1cbd..0000000 --- a/roles/preflight/tasks/validate_mtv.yml +++ /dev/null @@ -1,53 +0,0 @@ ---- -# Query the ClusterServiceVersion list in the MTV namespace. -# kubernetes.core.k8s_info is certified content and the collection's -# established pattern for cluster queries. - -- name: validate_mtv | List ClusterServiceVersions in the MTV namespace - kubernetes.core.k8s_info: - host: "{{ preflight_openshift_host }}" - api_key: "{{ preflight_openshift_api_key }}" - validate_certs: "{{ preflight_openshift_verify_ssl }}" - api_version: operators.coreos.com/v1alpha1 - kind: ClusterServiceVersion - namespace: "{{ preflight_mtv_namespace }}" - register: __preflight_mtv_csvs - failed_when: false - no_log: "{{ preflight_secure_logging }}" - -- name: validate_mtv | Find the MTV CSV - ansible.builtin.set_fact: - __preflight_mtv_found: >- - {{ __preflight_mtv_csvs.resources | default([]) - | selectattr('metadata.name', 'search', 'mtv-operator') - | list | length > 0 }} - __preflight_mtv_csv: >- - {{ __preflight_mtv_csvs.resources | default([]) - | selectattr('metadata.name', 'search', 'mtv-operator') - | list | first | default({}) }} - -- name: validate_mtv | Record the MTV operator check result - ansible.builtin.set_fact: - __preflight_result_mtv: - ran: true - passed: "{{ __preflight_mtv_found - and (__preflight_mtv_csv.status.phase | default('')) == 'Succeeded' }}" - detail: >- - {% if __preflight_mtv_csvs.resources is not defined %} - Cluster API query failed in {{ preflight_mtv_namespace }} — - check the OpenShift connection and credentials first; the - operator check is inconclusive when the API is unreachable - {% elif not __preflight_mtv_found %} - No mtv-operator CSV found in {{ preflight_mtv_namespace }} — - install the Migration Toolkit for Virtualization operator before - seeding - {% elif (__preflight_mtv_csv.status.phase | default('')) != 'Succeeded' %} - CSV {{ __preflight_mtv_csv.metadata.name }} in - {{ preflight_mtv_namespace }} is - {{ __preflight_mtv_csv.status.phase | default('unknown') }} — - expected Succeeded - {% else %} - CSV {{ __preflight_mtv_csv.metadata.name }} Succeeded in - {{ preflight_mtv_namespace }} - {% endif %} -... diff --git a/roles/preflight/tasks/validate_openshift.yml b/roles/preflight/tasks/validate_openshift.yml deleted file mode 100644 index 8e8b803..0000000 --- a/roles/preflight/tasks/validate_openshift.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -# Probe the OpenShift API with the configured credential. -# ansible.builtin.uri is used instead of the k8s modules because the HTTP -# status code cleanly distinguishes 401/403 (credential rejected) from -# 200/201 (authenticated). - -- name: validate_openshift | Probe OpenShift API SelfSubjectReview with the configured credential - ansible.builtin.uri: - url: "{{ preflight_openshift_host }}/apis/authentication.k8s.io/v1/selfsubjectreviews" - method: POST - body: '{"apiVersion":"authentication.k8s.io/v1","kind":"SelfSubjectReview"}' - body_format: json - validate_certs: "{{ preflight_openshift_verify_ssl }}" - headers: - Authorization: "{{ 'Bearer ' ~ (preflight_openshift_api_key | trim) }}" - status_code: [200, 201, 401, 403] - register: __preflight_ocp - # No module state is created — the review object is informational only, - # so this probe must never report changed. - changed_when: false - failed_when: false - no_log: "{{ preflight_secure_logging }}" - -- name: validate_openshift | Record the OpenShift authentication check result - ansible.builtin.set_fact: - __preflight_result_openshift: - ran: true - passed: "{{ (__preflight_ocp.status | default(0)) in [200, 201] }}" - detail: >- - {% if (__preflight_ocp.status | default(0)) in [200, 201] %} - HTTP {{ __preflight_ocp.status }} at {{ preflight_openshift_host }} - (identity: {{ __preflight_ocp.json.status.userInfo.username | default('unknown', true) }}) - {% else %} - HTTP {{ __preflight_ocp.status | default('connection error') }} at - {{ preflight_openshift_host }} — 401/403 means the token or - credential is missing, expired, or rejected - {% endif %} -... diff --git a/roles/preflight/tasks/validate_vcenter.yml b/roles/preflight/tasks/validate_vcenter.yml deleted file mode 100644 index 927e71c..0000000 --- a/roles/preflight/tasks/validate_vcenter.yml +++ /dev/null @@ -1,97 +0,0 @@ ---- -# Probe vCenter connectivity and authentication. -# Two paths: the certified vmware.vmware_rest REST collection (vCenter 7.0+) -# first, then a community.vmware SOAP (pyVmomi) fallback that also covers 6.x -# — older vCenters do not serve the /api/ REST tree at all, and proxies in -# front of them commonly map only /sdk and /rest. Listing datacenters is the -# lightest operation that proves both connectivity and authentication against -# the configured source. The check only applies to vmware sources — other -# source types (ovirt) skip it. -# -# Module tier justification (community content): the certified -# vmware.vmware_rest collection requires vCenter 7.0+ and cannot reach 6.x at -# all (no /api/ tree), and the certified successor vmware.vmware collection -# does not yet ship a datacenter_info module. community.vmware -# .vmware_datacenter_info (pyVmomi, vCenter 6.5+) is the only content that -# covers the 6.x path, so the SOAP fallback runs only when the REST probe -# cannot return data. - -- name: validate_vcenter | List vCenter datacenters via the REST API (vCenter 7.0+) - when: - - preflight_vcenter_hostname | length > 0 - - __preflight_source_type | length == 0 or __preflight_source_type == 'vmware' - vmware.vmware_rest.vcenter_datacenter_info: - vcenter_hostname: "{{ preflight_vcenter_hostname }}" - vcenter_username: "{{ preflight_vcenter_username }}" - vcenter_password: "{{ preflight_vcenter_password }}" - vcenter_validate_certs: "{{ preflight_vcenter_validate_certs }}" - register: __preflight_vcenter_rest - failed_when: false - # A module crash (e.g. aiohttp ContentTypeError when the endpoint answers - # with a non-JSON body) bypasses failed_when and would abort the run; the - # fallback and record tasks below turn the fault into a graceful check - # result. - ignore_errors: true - no_log: "{{ preflight_secure_logging }}" - -- name: validate_vcenter | Fall back to the SOAP SDK for vCenter 6.x (no REST API) - when: - - preflight_vcenter_hostname | length > 0 - - __preflight_source_type | length == 0 or __preflight_source_type == 'vmware' - - __preflight_vcenter_rest.value is not defined or __preflight_vcenter_rest.value | length == 0 - community.vmware.vmware_datacenter_info: - hostname: "{{ preflight_vcenter_hostname }}" - username: "{{ preflight_vcenter_username }}" - password: "{{ preflight_vcenter_password }}" - validate_certs: "{{ preflight_vcenter_validate_certs }}" - register: __preflight_vcenter_soap - failed_when: false - ignore_errors: true - no_log: "{{ preflight_secure_logging }}" - -- name: validate_vcenter | Merge the REST and SOAP results into one datacenter list - # Prefers a result with datacenters (the SOAP fallback can find data the - # proxied REST path misses); falls back to an empty result from either - # path (connected but none visible), then the empty-string sentinel when - # neither path returned data — the record task distinguishes a real list - # from '' by type. - ansible.builtin.set_fact: - __preflight_vcenter_dcs: "{{ ([(__preflight_vcenter_rest.value | default(none)), - (__preflight_vcenter_soap.datacenter_info | default(none))] - | reject('none') | select('truthy') | first) - | default([(__preflight_vcenter_rest.value | default(none)), - (__preflight_vcenter_soap.datacenter_info | default(none))] - | reject('none') | first | default(none), true) }}" - -- name: validate_vcenter | Record the vCenter connectivity check result - ansible.builtin.set_fact: - __preflight_result_vcenter: - ran: true - passed: "{{ (__preflight_source_type | length > 0 and __preflight_source_type != 'vmware') - or ((preflight_vcenter_hostname | length > 0) - and (__preflight_vcenter_dcs is not string) - and (__preflight_vcenter_dcs | length) > 0) }}" - detail: >- - {% if __preflight_source_type | length > 0 and __preflight_source_type != 'vmware' %} - Skipped — source type is {{ __preflight_source_type }}, not vmware; - the vCenter check does not apply - {% elif preflight_vcenter_hostname | length == 0 %} - Not configured — set host/username/password on the source host - (or run from AAP where the source credential injects VMWARE_*) to - enable the vCenter check - {% elif __preflight_vcenter_dcs is not string and __preflight_vcenter_dcs | length > 0 %} - HTTP session established at {{ preflight_vcenter_hostname }} - ({{ __preflight_vcenter_dcs | length }} datacenters visible via - {{ 'REST' if __preflight_vcenter_rest.value is defined and __preflight_vcenter_rest.value | length > 0 - else 'the SOAP SDK' }}) - {% elif __preflight_vcenter_dcs is not string %} - Connected to {{ preflight_vcenter_hostname }} but no datacenters - are visible — a migration source needs at least one - {% else %} - vCenter authentication or connectivity failed at - {{ preflight_vcenter_hostname }} — re-run the vCenter check alone - (--tags vcenter) as a one-off CLI run with - preflight_secure_logging=false to see the fault, then rotate the - exposed credential - {% endif %} -... diff --git a/roles/preflight/README.md b/roles/validate_environment/README.md similarity index 59% rename from roles/preflight/README.md rename to roles/validate_environment/README.md index d7edc70..f4371a6 100644 --- a/roles/preflight/README.md +++ b/roles/validate_environment/README.md @@ -1,5 +1,5 @@ -# preflight +# validate_environment Validate the migration environment before seeding AAP. Runs every check and reports a summary, then fails the play if any check failed — one run reports everything that needs attention. @@ -19,25 +19,25 @@ If any check fails, fix it before you seed. Do not seed to paper over a missing ## Example ```bash -ansible-playbook playbooks/vmf_preflight.yml \ +ansible-playbook playbooks/vmf_validate_environment.yml \ --inventory inventory-sample.yml ``` From an AAP job template the target credential injects `K8S_AUTH_*`, the source credential injects `VMWARE_*`, and the controller injects `CONTROLLER_HOST`/`CONTROLLER_API_TOKEN` — the same role runs unchanged. -The AAP access check defaults to the AAP 2.5 platform controller API path (`/api/controller/v2/`). AAP 2.4 and earlier serve the API at `/api/v2/` — set `preflight_aap_api_path: /api/v2/` for those environments (a 404 from the AAP check means the path is wrong). +The AAP access check defaults to the AAP 2.5 platform controller API path (`/api/controller/v2/`). AAP 2.4 and earlier serve the API at `/api/v2/` — set `validate_environment_aap_api_path: /api/v2/` for those environments (a 404 from the AAP check means the path is wrong). -With the sample inventory the connection variables resolve from the target cluster host (`openshift_host`, `openshift_api_key`), the source host (`host`, `username`, `password`, `vddk.image`), and the AAP host (`aap_hostname`, `aap_token`) — selected by `preflight_target_name`, `preflight_source_name`, and `preflight_aap_name`, which default to the `target_name`/`source_name` extra vars (injected by the seeded credentials) or the first host of each group. +With the sample inventory the connection variables resolve from the target cluster host (`openshift_host`, `openshift_api_key`), the source host (`host`, `username`, `password`, `vddk.image`), and the AAP host (`aap_hostname`, `aap_token`) — selected by `validate_environment_target_name`, `validate_environment_source_name`, and `validate_environment_aap_name`, which default to the `target_name`/`source_name` extra vars (injected by the seeded credentials) or the first host of each group. -All checks run on every invocation, and the summary assert requires every check green — the per-check tags (`openshift`, `cnv`, `mtv`, `aap`, `vcenter`, `vddk`) exist for isolated debugging; the summary asserts only over the checks that ran, so a tagged debug run ends green when the targeted check passed. A source whose `type` is not `vmware` (for example `ovirt`) skips the vCenter and VDDK checks. +All checks run on every invocation, and the summary assert requires every check green. A source whose `type` is not `vmware` (for example `ovirt`) skips the vCenter and VDDK checks. ## Check Mode -Not supported. The connection probes use the `uri` module, which is skipped under `--check`, so a check-mode run reports false authentication failures for the OpenShift and AAP checks. Run the preflight without `--check` — every probe is read-only (SelfSubjectReview creates no state, `/api/v2/me/` is a GET, operator queries are list calls). +Not supported. The connection probes use the `uri` module, which is skipped under `--check`, so a check-mode run reports false authentication failures for the OpenShift and AAP checks. Run the validate_environment without `--check` — every probe is read-only (SelfSubjectReview creates no state, `/api/v2/me/` is a GET, operator queries are list calls). -## preflight +## validate_environment ``` Role belongs to infra/openshift_virtualization_migration @@ -56,104 +56,109 @@ Description: Validate the migration environment before seeding AAP — OpenShift #### Key: main -* **Description**: ['Runs every preflight check and reports a summary, then fails the play if any check failed. Checks: OpenShift API auth, CNV and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration.', 'Designed to run from an AAP job template (credentials inject K8S_AUTH_*, VMWARE_*, CONTROLLER_HOST, CONTROLLER_API_TOKEN) or a controller with the sample inventory. Connection variables cascade from environment variables to inventory host variables.', 'All checks run on every invocation. An unconfigured target fails that check with guidance rather than crashing the play, so one run reports everything that needs attention.'] +* **Description**: ['Runs the selected validate_environment checks and reports a summary, then fails the play if any ran check failed. Checks: OpenShift API auth, CNV and MTV operator health, AAP access, vCenter connectivity, and VDDK init image configuration.', 'Designed to run from an AAP job template (credentials inject K8S_AUTH_*, VMWARE_*, CONTROLLER_HOST, CONTROLLER_API_TOKEN) or a controller with the sample inventory. Connection variables cascade from environment variables to inventory host variables.', 'All checks run by default; validate_environment_checks limits the pass to the named checks. An unconfigured target fails that check with guidance rather than crashing the play, so one run reports everything that needs attention.'] * **Options**: - * **preflight_aap_api_path**: + * **validate_environment_aap_api_path**: * **Required**: False * **Type**: str - * **Default**: /api/v2/ - * **Description**: API base path for the access check. Defaults to /api/v2/ (AWX and AAP 2.4 and earlier); AAP 2.5 platform deployments serve the controller API at /api/controller/v2/. - * **preflight_aap_hostname**: + * **Default**: /api/controller/v2/ + * **Description**: API base path for the access check. Defaults to /api/controller/v2/ (AAP 2.5 platform deployments); AWX and AAP 2.4 and earlier serve the controller API at /api/v2/ — set that value for those environments. + * **validate_environment_aap_hostname**: * **Required**: False * **Type**: str * **Default**: - * **Description**: AAP controller URL for the access check. Resolves from AAP_HOSTNAME env var, the CONTROLLER_HOST env var injected by the controller for its own jobs, or the aap_hostname variable on the AAP host (preflight_aap_name). When unset, the AAP check fails with instructions. - * **preflight_aap_name**: + * **Description**: AAP controller URL for the access check. Resolves from AAP_HOSTNAME env var, the CONTROLLER_HOST env var injected by the controller for its own jobs, or the aap_hostname variable on the AAP host (validate_environment_aap_name). When unset, the AAP check fails with instructions. + * **validate_environment_aap_name**: * **Required**: False * **Type**: str * **Default**: none * **Description**: Inventory host whose aap_* variables feed the AAP check. Defaults to the first migration_aap host. - * **preflight_aap_password**: + * **validate_environment_aap_password**: * **Required**: False * **Type**: str * **Default**: * **Description**: AAP password for the access check when no token is set. - * **preflight_aap_token**: + * **validate_environment_aap_token**: * **Required**: False * **Type**: str * **Default**: * **Description**: AAP OAuth2 token. Resolves from AAP_TOKEN env var, the CONTROLLER_API_TOKEN env var injected by the controller for its own jobs, or the aap_token variable on the AAP host. Takes precedence over username/password. - * **preflight_aap_username**: + * **validate_environment_aap_username**: * **Required**: False * **Type**: str * **Default**: * **Description**: AAP username for the access check. - * **preflight_aap_validate_certs**: + * **validate_environment_aap_validate_certs**: * **Required**: False * **Type**: bool * **Default**: True * **Description**: Validate the AAP TLS certificate. - * **preflight_cnv_namespace**: + * **validate_environment_checks**: + * **Required**: False + * **Type**: list + * **Default**: ['openshift', 'cnv', 'mtv', 'aap', 'vcenter', 'vddk'] + * **Description**: Which environment validation checks to run, as check-name strings (openshift, cnv, mtv, aap, vcenter, vddk). Defaults to all six. Accepts a list, a bare check name, or a bracketed comma-separated string — the CLI -e form, e.g. -e validate_environment_checks=[vcenter] to debug a single check. An unknown name fails the include with a loud file-not-found error — the summary report still runs. + * **validate_environment_cnv_namespace**: * **Required**: False * **Type**: str * **Default**: openshift-cnv * **Description**: Namespace holding the OpenShift Virtualization operator CSV. - * **preflight_mtv_namespace**: + * **validate_environment_mtv_namespace**: * **Required**: False * **Type**: str * **Default**: openshift-mtv * **Description**: Namespace holding the MTV operator CSV. Resolves from the mtv_namespace inventory variable by default. - * **preflight_openshift_api_key**: + * **validate_environment_openshift_api_key**: * **Required**: False * **Type**: str * **Default**: - * **Description**: OpenShift API token. Resolves from K8S_AUTH_API_KEY env var or the openshift_api_key variable on the target cluster host (preflight_target_name). Required — the preflight aborts without it. - * **preflight_openshift_host**: + * **Description**: OpenShift API token. Resolves from K8S_AUTH_API_KEY env var or the openshift_api_key variable on the target cluster host (validate_environment_target_name). Required — the validate_environment aborts without it. + * **validate_environment_openshift_host**: * **Required**: False * **Type**: str * **Default**: - * **Description**: OpenShift API endpoint. Resolves from K8S_AUTH_HOST env var or the openshift_host variable on the target cluster host (preflight_target_name). Required — the preflight aborts without it. - * **preflight_openshift_verify_ssl**: + * **Description**: OpenShift API endpoint. Resolves from K8S_AUTH_HOST env var or the openshift_host variable on the target cluster host (validate_environment_target_name). Required — the validate_environment aborts without it. + * **validate_environment_openshift_verify_ssl**: * **Required**: False * **Type**: bool * **Default**: True * **Description**: Validate the OpenShift API TLS certificate. - * **preflight_secure_logging**: + * **validate_environment_secure_logging**: * **Required**: False * **Type**: bool * **Default**: True - * **Description**: Whether to enable secure logging for tasks that handle credentials. Set to false to debug failed probes. Prefer a one-off CLI run (--tags ) over flipping the toggle on a job template; if you must, rotate the exposed credential (token or password) afterwards. - * **preflight_source_name**: + * **Description**: Whether to enable secure logging for tasks that handle credentials. Set to false to debug failed probes. Prefer a one-off CLI run (-e validate_environment_secure_logging=false) over flipping the toggle on a job template; if you must, rotate the exposed credential (token or password) afterwards. + * **validate_environment_source_name**: * **Required**: False * **Type**: str * **Default**: none * **Description**: Inventory host whose variables feed the vCenter and VDDK checks. Defaults to the source_name extra var (injected by the seeded credentials) or the first vm_sources host. - * **preflight_target_name**: + * **validate_environment_target_name**: * **Required**: False * **Type**: str * **Default**: none * **Description**: Inventory host whose variables feed the OpenShift checks. Defaults to the target_name extra var (injected by the seeded credentials) or the first migration_clusters host. - * **preflight_vcenter_hostname**: + * **validate_environment_vcenter_hostname**: * **Required**: False * **Type**: str * **Default**: - * **Description**: vCenter endpoint for the connectivity check. Resolves from VMWARE_HOST env var or the host variable on the source host (preflight_source_name). The check applies to vmware sources; other source types skip it. When unset, the vCenter check fails with instructions. - * **preflight_vcenter_password**: + * **Description**: vCenter endpoint for the connectivity check. Resolves from VMWARE_HOST env var or the host variable on the source host (validate_environment_source_name). The check applies to vmware sources; other source types skip it. When unset, the vCenter check fails with instructions. + * **validate_environment_vcenter_password**: * **Required**: False * **Type**: str * **Default**: - * **Description**: vCenter password. Resolves from VMWARE_PASSWORD env var or the password variable on the source host (preflight_source_name). - * **preflight_vcenter_username**: + * **Description**: vCenter password. Resolves from VMWARE_PASSWORD env var or the password variable on the source host (validate_environment_source_name). + * **validate_environment_vcenter_username**: * **Required**: False * **Type**: str * **Default**: - * **Description**: vCenter username. Resolves from VMWARE_USER env var or the username variable on the source host (preflight_source_name). - * **preflight_vcenter_validate_certs**: + * **Description**: vCenter username. Resolves from VMWARE_USER env var or the username variable on the source host (validate_environment_source_name). + * **validate_environment_vcenter_validate_certs**: * **Required**: False * **Type**: bool * **Default**: True * **Description**: Validate the vCenter TLS certificate. - * **preflight_vddk_image**: + * **validate_environment_vddk_image**: * **Required**: False * **Type**: str * **Default**: @@ -169,68 +174,89 @@ Description: Validate the migration environment before seeding AAP — OpenShift | Var | Type | Value |Choices |Required | Title | |--------------|--------------|-------------|-------------|-------------|-------------| -| [`preflight_aap_api_path`](defaults/main.yml#L130) | str | `/api/controller/v2/` | None | False | AAP API Path | -| [`preflight_aap_hostname`](defaults/main.yml#L116) | str | `` | None | False | AAP Hostname | -| [`preflight_aap_name`](defaults/main.yml#L20) | str | `{{ groups['migration_aap'] ¦ default([], true) ¦ first ¦ default('', true) }}` | None | None | None | -| [`preflight_aap_password`](defaults/main.yml#L148) | str | `` | None | False | AAP Password | -| [`preflight_aap_token`](defaults/main.yml#L161) | str | `` | None | False | AAP Token | -| [`preflight_aap_username`](defaults/main.yml#L137) | str | `` | None | False | AAP Username | -| [`preflight_aap_validate_certs`](defaults/main.yml#L174) | str | `{{ (lookup('env', 'AAP_VALIDATE_CERTS') ¦ default(hostvars[preflight_aap_name]['aap_validate_certs'] ¦ default(true), true)) ¦ bool }}` | None | False | AAP Verify SSL | -| [`preflight_cnv_namespace`](defaults/main.yml#L183) | str | `` | None | False | CNV Namespace | -| [`preflight_mtv_namespace`](defaults/main.yml#L200) | str | `` | None | False | MTV Namespace | -| [`preflight_openshift_api_key`](defaults/main.yml#L46) | str | `` | None | True | OpenShift API Key | -| [`preflight_openshift_host`](defaults/main.yml#L33) | str | `` | None | True | OpenShift Host | -| [`preflight_openshift_verify_ssl`](defaults/main.yml#L63) | str | `{{ (lookup('env', 'K8S_AUTH_VERIFY_SSL') ¦ default(hostvars[preflight_target_name]['openshift_verify_ssl'] ¦ default(true), true)) ¦ bool }}` | None | False | OpenShift Verify SSL | -| [`preflight_secure_logging`](defaults/main.yml#L25) | str | `{{ secure_logging ¦ default(true) }}` | None | None | None | -| [`preflight_source_name`](defaults/main.yml#L17) | str | `{{ source_name ¦ default(groups['vm_sources'] ¦ default([], true) ¦ first ¦ default('', true), true) }}` | None | None | None | -| [`preflight_target_name`](defaults/main.yml#L14) | str | `{{ target_name ¦ default(groups['migration_clusters'] ¦ default([], true) ¦ first ¦ default('', true), true) }}` | None | None | None | -| [`preflight_vcenter_hostname`](defaults/main.yml#L72) | str | `` | None | False | vCenter Hostname | -| [`preflight_vcenter_password`](defaults/main.yml#L94) | str | `` | None | False | vCenter Password | -| [`preflight_vcenter_username`](defaults/main.yml#L83) | str | `` | None | False | vCenter Username | -| [`preflight_vcenter_validate_certs`](defaults/main.yml#L106) | str | `{{ (lookup('env', 'VMWARE_VALIDATE_CERTS') ¦ default(hostvars[preflight_source_name]['validate_certs'] ¦ default(true), true)) ¦ bool }}` | None | False | vCenter Verify SSL | -| [`preflight_vddk_image`](defaults/main.yml#L217) | str | `` | None | False | VDDK Init Image | +| [`validate_environment_aap_api_path`](defaults/main.yml#L145) | str | `/api/controller/v2/` | None | False | AAP API Path | +| [`validate_environment_aap_hostname`](defaults/main.yml#L131) | str | `` | None | False | AAP Hostname | +| [`validate_environment_aap_name`](defaults/main.yml#L20) | str | `{{ groups['migration_aap'] ¦ default([], true) ¦ first ¦ default('', true) }}` | None | None | None | +| [`validate_environment_aap_password`](defaults/main.yml#L163) | str | `` | None | False | AAP Password | +| [`validate_environment_aap_token`](defaults/main.yml#L176) | str | `` | None | False | AAP Token | +| [`validate_environment_aap_username`](defaults/main.yml#L152) | str | `` | None | False | AAP Username | +| [`validate_environment_aap_validate_certs`](defaults/main.yml#L189) | str | `{{ (lookup('env', 'AAP_VALIDATE_CERTS') ¦ default(hostvars[validate_environment_aap_name]['aap_validate_certs'] ¦ default(true), true)) ¦ bool }}` | None | False | AAP Verify SSL | +| [`validate_environment_checks`](defaults/main.yml#L34) | list | `[]` | None | False | Checks | +| [`validate_environment_checks.0`](defaults/main.yml#L35) | str | `openshift` | None | None | None | +| [`validate_environment_checks.1`](defaults/main.yml#L36) | str | `cnv` | None | None | None | +| [`validate_environment_checks.2`](defaults/main.yml#L37) | str | `mtv` | None | None | None | +| [`validate_environment_checks.3`](defaults/main.yml#L38) | str | `aap` | None | None | None | +| [`validate_environment_checks.4`](defaults/main.yml#L39) | str | `vcenter` | None | None | None | +| [`validate_environment_checks.5`](defaults/main.yml#L40) | str | `vddk` | None | None | None | +| [`validate_environment_cnv_namespace`](defaults/main.yml#L198) | str | `` | None | False | CNV Namespace | +| [`validate_environment_mtv_namespace`](defaults/main.yml#L215) | str | `` | None | False | MTV Namespace | +| [`validate_environment_openshift_api_key`](defaults/main.yml#L61) | str | `` | None | True | OpenShift API Key | +| [`validate_environment_openshift_host`](defaults/main.yml#L48) | str | `` | None | True | OpenShift Host | +| [`validate_environment_openshift_verify_ssl`](defaults/main.yml#L78) | str | `{{ (lookup('env', 'K8S_AUTH_VERIFY_SSL') ¦ default(hostvars[validate_environment_target_name]['openshift_verify_ssl'] ¦ default(true), true)) ¦ bool }}` | None | False | OpenShift Verify SSL | +| [`validate_environment_secure_logging`](defaults/main.yml#L25) | str | `{{ secure_logging ¦ default(true) }}` | None | None | None | +| [`validate_environment_source_name`](defaults/main.yml#L17) | str | `{{ source_name ¦ default(groups['vm_sources'] ¦ default([], true) ¦ first ¦ default('', true), true) }}` | None | None | None | +| [`validate_environment_target_name`](defaults/main.yml#L14) | str | `{{ target_name ¦ default(groups['migration_clusters'] ¦ default([], true) ¦ first ¦ default('', true), true) }}` | None | None | None | +| [`validate_environment_vcenter_hostname`](defaults/main.yml#L87) | str | `` | None | False | vCenter Hostname | +| [`validate_environment_vcenter_password`](defaults/main.yml#L109) | str | `` | None | False | vCenter Password | +| [`validate_environment_vcenter_username`](defaults/main.yml#L98) | str | `` | None | False | vCenter Username | +| [`validate_environment_vcenter_validate_certs`](defaults/main.yml#L121) | str | `{{ (lookup('env', 'VMWARE_VALIDATE_CERTS') ¦ default(hostvars[validate_environment_source_name]['validate_certs'] ¦ default(true), true)) ¦ bool }}` | None | False | vCenter Verify SSL | +| [`validate_environment_vddk_image`](defaults/main.yml#L232) | str | `` | None | False | VDDK Init Image | 🖇️ Full descriptions for vars in defaults/main.yml
-`preflight_aap_api_path`: >- +`validate_environment_aap_api_path`: >- +
+`validate_environment_aap_hostname`: >- +
+`validate_environment_aap_name`: None +
+`validate_environment_aap_password`: >- +
+`validate_environment_aap_token`: >- +
+`validate_environment_aap_username`: >- +
+`validate_environment_aap_validate_certs`: >- +
+`validate_environment_checks`: >-
-`preflight_aap_hostname`: >- +`validate_environment_checks.0`: None
-`preflight_aap_name`: None +`validate_environment_checks.1`: None
-`preflight_aap_password`: >- +`validate_environment_checks.2`: None
-`preflight_aap_token`: >- +`validate_environment_checks.3`: None
-`preflight_aap_username`: >- +`validate_environment_checks.4`: None
-`preflight_aap_validate_certs`: >- +`validate_environment_checks.5`: None
-`preflight_cnv_namespace`: >- +`validate_environment_cnv_namespace`: >-
-`preflight_mtv_namespace`: >- +`validate_environment_mtv_namespace`: >-
-`preflight_openshift_api_key`: >- +`validate_environment_openshift_api_key`: >-
-`preflight_openshift_host`: >- +`validate_environment_openshift_host`: >-
-`preflight_openshift_verify_ssl`: >- +`validate_environment_openshift_verify_ssl`: >-
-`preflight_secure_logging`: None +`validate_environment_secure_logging`: None
-`preflight_source_name`: None +`validate_environment_source_name`: None
-`preflight_target_name`: None +`validate_environment_target_name`: None
-`preflight_vcenter_hostname`: >- +`validate_environment_vcenter_hostname`: >-
-`preflight_vcenter_password`: >- +`validate_environment_vcenter_password`: >-
-`preflight_vcenter_username`: >- +`validate_environment_vcenter_username`: >-
-`preflight_vcenter_validate_certs`: >- +`validate_environment_vcenter_validate_certs`: >-
-`preflight_vddk_image`: >- +`validate_environment_vddk_image`: >-

@@ -242,8 +268,8 @@ Description: Validate the migration environment before seeding AAP — OpenShift | Var | Type | Value | |--------------|--------------|-------------| -| [__preflight_source_type](vars/main.yml#L4) | str | `{{ hostvars[preflight_source_name]['type'] ¦ default('', true) }}` | -| [__preflight_source_vddk](vars/main.yml#L8) | str | `{{ hostvars[preflight_source_name]['vddk'] ¦ default({}, true) }}` | +| [__validate_environment_source_type](vars/main.yml#L4) | str | `{{ hostvars[validate_environment_source_name]['type'] ¦ default('', true) }}` | +| [__validate_environment_source_vddk](vars/main.yml#L8) | str | `{{ hostvars[validate_environment_source_name]['vddk'] ¦ default({}, true) }}` | ### Tasks @@ -252,13 +278,8 @@ Description: Validate the migration environment before seeding AAP — OpenShift | Name | Module | Has Conditions | | ---- | ------ | --------- | | Include pre-validation of required connection variables | `ansible.builtin.include_tasks` | False | -| Run preflight checks | `block` | False | -| Include OpenShift API authentication check | `ansible.builtin.include_tasks` | False | -| Include OpenShift Virtualization operator check | `ansible.builtin.include_tasks` | False | -| Include MTV operator check | `ansible.builtin.include_tasks` | False | -| Include AAP access check | `ansible.builtin.include_tasks` | False | -| Include vCenter connectivity check | `ansible.builtin.include_tasks` | False | -| Include VDDK init image check | `ansible.builtin.include_tasks` | False | +| Run environment validation checks | `block` | False | +| Include each selected environment validation check | `ansible.builtin.include_tasks` | False | #### File: tasks/pre_validate.yml @@ -271,9 +292,9 @@ Description: Validate the migration environment before seeding AAP — OpenShift | Name | Module | Has Conditions | | ---- | ------ | --------- | -| report ¦ Build the preflight summary | `ansible.builtin.set_fact` | False | -| report ¦ Display the preflight summary | `ansible.builtin.debug` | False | -| report ¦ Assert every ran preflight check passed | `ansible.builtin.assert` | False | +| report ¦ Build the validate_environment summary | `ansible.builtin.set_fact` | False | +| report ¦ Display the validate_environment summary | `ansible.builtin.debug` | False | +| report ¦ Assert every ran validate_environment check passed | `ansible.builtin.assert` | False | | report ¦ Remind the operator to rotate credentials exposed in debug output | `ansible.builtin.debug` | True | #### File: tasks/validate_aap.yml @@ -338,15 +359,10 @@ classDef includeVars stroke:#8e44ad,stroke-width:2px; classDef rescue stroke:#665352,stroke-width:2px; Start-->|Include task| Include_pre_validation_of_required_connection_variables_pre_validate_yml_0[include pre validation of required connection
variables
include_task: pre validate yml]:::includeTasks - Include_pre_validation_of_required_connection_variables_pre_validate_yml_0-->|Block Start| Run_preflight_checks1_block_start_0[[run preflight checks]]:::block - Run_preflight_checks1_block_start_0-->|Include task| Include_OpenShift_API_authentication_check_validate_openshift_yml_0[include openshift api authentication check
include_task: validate openshift yml]:::includeTasks - Include_OpenShift_API_authentication_check_validate_openshift_yml_0-->|Include task| Include_OpenShift_Virtualization_operator_check_validate_cnv_yml_1[include openshift virtualization operator check
include_task: validate cnv yml]:::includeTasks - Include_OpenShift_Virtualization_operator_check_validate_cnv_yml_1-->|Include task| Include_MTV_operator_check_validate_mtv_yml_2[include mtv operator check
include_task: validate mtv yml]:::includeTasks - Include_MTV_operator_check_validate_mtv_yml_2-->|Include task| Include_AAP_access_check_validate_aap_yml_3[include aap access check
include_task: validate aap yml]:::includeTasks - Include_AAP_access_check_validate_aap_yml_3-->|Include task| Include_vCenter_connectivity_check_validate_vcenter_yml_4[include vcenter connectivity check
include_task: validate vcenter yml]:::includeTasks - Include_vCenter_connectivity_check_validate_vcenter_yml_4-->|Include task| Include_VDDK_init_image_check_validate_vddk_yml_5[include vddk init image check
include_task: validate vddk yml]:::includeTasks - Include_VDDK_init_image_check_validate_vddk_yml_5-.->|End of Block| Run_preflight_checks1_block_start_0 - Include_VDDK_init_image_check_validate_vddk_yml_5-->End + Include_pre_validation_of_required_connection_variables_pre_validate_yml_0-->|Block Start| Run_environment_validation_checks1_block_start_0[[run environment validation checks]]:::block + Run_environment_validation_checks1_block_start_0-->|Include task| Include_each_selected_environment_validation_check_validate______validate_environment_check____yml_0[include each selected environment validation check
include_task: validate validate environment check yml]:::includeTasks + Include_each_selected_environment_validation_check_validate______validate_environment_check____yml_0-.->|End of Block| Run_environment_validation_checks1_block_start_0 + Include_each_selected_environment_validation_check_validate______validate_environment_check____yml_0-->End ``` ### Graph for pre_validate.yml @@ -382,10 +398,10 @@ classDef importRole stroke:#699ba7,stroke-width:2px; classDef includeVars stroke:#8e44ad,stroke-width:2px; classDef rescue stroke:#665352,stroke-width:2px; - Start-->|Task| report___Build_the_preflight_summary0[report build the preflight summary]:::task - report___Build_the_preflight_summary0-->|Task| report___Display_the_preflight_summary1[report display the preflight summary]:::task - report___Display_the_preflight_summary1-->|Task| report___Assert_every_ran_preflight_check_passed2[report assert every ran preflight check passed]:::task - report___Assert_every_ran_preflight_check_passed2-->|Task| report___Remind_the_operator_to_rotate_credentials_exposed_in_debug_output3[report remind the operator to rotate credentials
exposed in debug output
When: **not preflight secure logging default true
bool**]:::task + Start-->|Task| report___Build_the_validate_environment_summary0[report build the validate environment summary]:::task + report___Build_the_validate_environment_summary0-->|Task| report___Display_the_validate_environment_summary1[report display the validate environment summary]:::task + report___Display_the_validate_environment_summary1-->|Task| report___Assert_every_ran_validate_environment_check_passed2[report assert every ran validate environment
check passed]:::task + report___Assert_every_ran_validate_environment_check_passed2-->|Task| report___Remind_the_operator_to_rotate_credentials_exposed_in_debug_output3[report remind the operator to rotate credentials
exposed in debug output
When: **not validate environment secure logging default
true bool**]:::task report___Remind_the_operator_to_rotate_credentials_exposed_in_debug_output3-->End ``` @@ -403,7 +419,7 @@ classDef importRole stroke:#699ba7,stroke-width:2px; classDef includeVars stroke:#8e44ad,stroke-width:2px; classDef rescue stroke:#665352,stroke-width:2px; - Start-->|Task| validate_aap___Probe_AAP_API_with_the_configured_account0[validate aap probe aap api with the configured
account
When: **preflight aap hostname length 0**]:::task + Start-->|Task| validate_aap___Probe_AAP_API_with_the_configured_account0[validate aap probe aap api with the configured
account
When: **validate environment aap hostname length 0**]:::task validate_aap___Probe_AAP_API_with_the_configured_account0-->|Task| validate_aap___Record_the_AAP_access_check_result1[validate aap record the aap access check result]:::task validate_aap___Record_the_AAP_access_check_result1-->End ``` @@ -481,8 +497,8 @@ classDef importRole stroke:#699ba7,stroke-width:2px; classDef includeVars stroke:#8e44ad,stroke-width:2px; classDef rescue stroke:#665352,stroke-width:2px; - Start-->|Task| validate_vcenter___List_vCenter_datacenters_via_the_REST_API__vCenter_7_0__0[validate vcenter list vcenter datacenters via
the rest api vcenter 7 0
When: **preflight vcenter hostname length 0 and
preflight source type length 0 or preflight
source type vmware**]:::task - validate_vcenter___List_vCenter_datacenters_via_the_REST_API__vCenter_7_0__0-->|Task| validate_vcenter___Fall_back_to_the_SOAP_SDK_for_vCenter_6_x__no_REST_API_1[validate vcenter fall back to the soap sdk for
vcenter 6 x no rest api
When: **preflight vcenter hostname length 0 and
preflight source type length 0 or preflight
source type vmware and preflight vcenter
rest value is not defined or preflight vcenter
rest value length 0**]:::task + Start-->|Task| validate_vcenter___List_vCenter_datacenters_via_the_REST_API__vCenter_7_0__0[validate vcenter list vcenter datacenters via
the rest api vcenter 7 0
When: **validate environment vcenter hostname length 0
and validate environment source type length
0 or validate environment source type vmware
**]:::task + validate_vcenter___List_vCenter_datacenters_via_the_REST_API__vCenter_7_0__0-->|Task| validate_vcenter___Fall_back_to_the_SOAP_SDK_for_vCenter_6_x__no_REST_API_1[validate vcenter fall back to the soap sdk for
vcenter 6 x no rest api
When: **validate environment vcenter hostname length 0
and validate environment source type length
0 or validate environment source type vmware
and validate environment vcenter rest value is
not defined or validate environment vcenter rest
value length 0**]:::task validate_vcenter___Fall_back_to_the_SOAP_SDK_for_vCenter_6_x__no_REST_API_1-->|Task| validate_vcenter___Merge_the_REST_and_SOAP_results_into_one_datacenter_list2[validate vcenter merge the rest and soap results
into one datacenter list]:::task validate_vcenter___Merge_the_REST_and_SOAP_results_into_one_datacenter_list2-->|Task| validate_vcenter___Record_the_vCenter_connectivity_check_result3[validate vcenter record the vcenter connectivity
check result]:::task validate_vcenter___Record_the_vCenter_connectivity_check_result3-->End diff --git a/roles/preflight/defaults/main.yml b/roles/validate_environment/defaults/main.yml similarity index 57% rename from roles/preflight/defaults/main.yml rename to roles/validate_environment/defaults/main.yml index 90ea9b3..b998cae 100644 --- a/roles/preflight/defaults/main.yml +++ b/roles/validate_environment/defaults/main.yml @@ -1,5 +1,5 @@ --- -# Preflight role defaults. +# Environment validation role defaults. # # Connection variables cascade from AAP-injected environment variables to # inventory host variables resolved through the target, source, and AAP @@ -11,29 +11,44 @@ # Resolver options — the inventory hosts whose variables feed the checks. # target_name and source_name match the names the seeded credentials # inject as extra vars; each falls back to the first host of its group. -preflight_target_name: "{{ target_name +validate_environment_target_name: "{{ target_name | default(groups['migration_clusters'] | default([], true) | first | default('', true), true) }}" -preflight_source_name: "{{ source_name +validate_environment_source_name: "{{ source_name | default(groups['vm_sources'] | default([], true) | first | default('', true), true) }}" -preflight_aap_name: "{{ groups['migration_aap'] | default([], true) | first | default('', true) }}" +validate_environment_aap_name: "{{ groups['migration_aap'] | default([], true) | first | default('', true) }}" # Secure logging toggle for tasks that handle credentials. # NEVER hardcode no_log: true — always reference this variable so failures -# can be debugged with preflight_secure_logging=false. -preflight_secure_logging: "{{ secure_logging | default(true) }}" +# can be debugged with validate_environment_secure_logging=false. +validate_environment_secure_logging: "{{ secure_logging | default(true) }}" + +# title: Checks +# required: False +# description: >- +# Which environment validation checks to run, as check-name strings. +# Defaults to all six. Accepts a list, a bare check name, or a +# bracketed comma-separated string — the CLI -e form, e.g. +# validate_environment_checks=[vcenter] to debug the vCenter check. +validate_environment_checks: + - openshift + - cnv + - mtv + - aap + - vcenter + - vddk # title: OpenShift Host # required: True # description: >- # OpenShift API endpoint. Cascades from K8S_AUTH_HOST env var (injected # by the AAP target credential) or the openshift_host variable on the -# target cluster host (preflight_target_name). -preflight_openshift_host: >- +# target cluster host (validate_environment_target_name). +validate_environment_openshift_host: >- {{ lookup('env', 'K8S_AUTH_HOST') - | default(hostvars[preflight_target_name]['openshift_host'] | default('', true), true) + | default(hostvars[validate_environment_target_name]['openshift_host'] | default('', true), true) }} # title: OpenShift API Key @@ -41,14 +56,14 @@ preflight_openshift_host: >- # description: >- # OpenShift API token. Cascades from K8S_AUTH_API_KEY env var (injected # by the AAP target credential) or the openshift_api_key variable on the -# target cluster host (preflight_target_name), falling back to +# target cluster host (validate_environment_target_name), falling back to # openshift_temporary_api_key. -preflight_openshift_api_key: >- +validate_environment_openshift_api_key: >- {{ lookup('env', 'K8S_AUTH_API_KEY') | default( - hostvars[preflight_target_name]['openshift_api_key'] - | default(hostvars[preflight_target_name]['openshift_temporary_api_key'] | default('', true), true), + hostvars[validate_environment_target_name]['openshift_api_key'] + | default(hostvars[validate_environment_target_name]['openshift_temporary_api_key'] | default('', true), true), true ) }} @@ -60,41 +75,41 @@ preflight_openshift_api_key: >- # K8S_AUTH_VERIFY_SSL env var or the openshift_verify_ssl variable on # the target cluster host. Defaults to true; the bool cast keeps the # argument-spec type honest when a raw env string feeds the value. -preflight_openshift_verify_ssl: "{{ (lookup('env', 'K8S_AUTH_VERIFY_SSL') - | default(hostvars[preflight_target_name]['openshift_verify_ssl'] | default(true), true)) | bool }}" +validate_environment_openshift_verify_ssl: "{{ (lookup('env', 'K8S_AUTH_VERIFY_SSL') + | default(hostvars[validate_environment_target_name]['openshift_verify_ssl'] | default(true), true)) | bool }}" # title: vCenter Hostname # required: False # description: >- # vCenter endpoint for the connectivity check. Cascades from # VMWARE_HOST env var (injected by the AAP source credential) or the -# host variable on the source host (preflight_source_name). -preflight_vcenter_hostname: >- +# host variable on the source host (validate_environment_source_name). +validate_environment_vcenter_hostname: >- {{ lookup('env', 'VMWARE_HOST') - | default(hostvars[preflight_source_name]['host'] | default('', true), true) + | default(hostvars[validate_environment_source_name]['host'] | default('', true), true) }} # title: vCenter Username # required: False # description: >- # vCenter username. Cascades from VMWARE_USER env var or the username -# variable on the source host (preflight_source_name). -preflight_vcenter_username: >- +# variable on the source host (validate_environment_source_name). +validate_environment_vcenter_username: >- {{ lookup('env', 'VMWARE_USER') - | default(hostvars[preflight_source_name]['username'] | default('', true), true) + | default(hostvars[validate_environment_source_name]['username'] | default('', true), true) }} # title: vCenter Password # required: False # description: >- # vCenter password. Cascades from VMWARE_PASSWORD env var or the -# password variable on the source host (preflight_source_name). -preflight_vcenter_password: >- +# password variable on the source host (validate_environment_source_name). +validate_environment_vcenter_password: >- {{ lookup('env', 'VMWARE_PASSWORD') - | default(hostvars[preflight_source_name]['password'] | default('', true), true) + | default(hostvars[validate_environment_source_name]['password'] | default('', true), true) }} # title: vCenter Verify SSL @@ -103,8 +118,8 @@ preflight_vcenter_password: >- # Validate the vCenter TLS certificate. Cascades from # VMWARE_VALIDATE_CERTS env var or the validate_certs variable on the # source host. Defaults to true. -preflight_vcenter_validate_certs: "{{ (lookup('env', 'VMWARE_VALIDATE_CERTS') - | default(hostvars[preflight_source_name]['validate_certs'] | default(true), true)) | bool }}" +validate_environment_vcenter_validate_certs: "{{ (lookup('env', 'VMWARE_VALIDATE_CERTS') + | default(hostvars[validate_environment_source_name]['validate_certs'] | default(true), true)) | bool }}" # title: AAP Hostname # required: False @@ -112,43 +127,43 @@ preflight_vcenter_validate_certs: "{{ (lookup('env', 'VMWARE_VALIDATE_CERTS') # Ansible Automation Platform controller URL for the access check. # Cascades from AAP_HOSTNAME env var, the CONTROLLER_HOST env var # injected by the controller for its own jobs, or the aap_hostname -# variable on the AAP host (preflight_aap_name). -preflight_aap_hostname: >- +# variable on the AAP host (validate_environment_aap_name). +validate_environment_aap_hostname: >- {{ lookup('env', 'AAP_HOSTNAME') | default(lookup('env', 'CONTROLLER_HOST') - | default(hostvars[preflight_aap_name]['aap_hostname'] | default('', true), true), true) + | default(hostvars[validate_environment_aap_name]['aap_hostname'] | default('', true), true), true) }} # title: AAP API Path # required: False # description: >- # API base path appended to the AAP hostname for the access check. -# Defaults to /api/controller/v2/ (AAP 2.5 platform and AWX). AAP 2.4 -# and earlier serve the API at /api/v2/ — set that value for those -# environments. -preflight_aap_api_path: /api/controller/v2/ +# Defaults to /api/controller/v2/ (AAP 2.5 platform deployments). AWX +# and AAP 2.4 and earlier serve the API at /api/v2/ — set that value +# for those environments. +validate_environment_aap_api_path: /api/controller/v2/ # title: AAP Username # required: False # description: >- # AAP username. Cascades from AAP_USERNAME env var or the aap_username -# variable on the AAP host (preflight_aap_name). -preflight_aap_username: >- +# variable on the AAP host (validate_environment_aap_name). +validate_environment_aap_username: >- {{ lookup('env', 'AAP_USERNAME') - | default(hostvars[preflight_aap_name]['aap_username'] | default('', true), true) + | default(hostvars[validate_environment_aap_name]['aap_username'] | default('', true), true) }} # title: AAP Password # required: False # description: >- # AAP password. Cascades from AAP_PASSWORD env var or the aap_password -# variable on the AAP host (preflight_aap_name). -preflight_aap_password: >- +# variable on the AAP host (validate_environment_aap_name). +validate_environment_aap_password: >- {{ lookup('env', 'AAP_PASSWORD') - | default(hostvars[preflight_aap_name]['aap_password'] | default('', true), true) + | default(hostvars[validate_environment_aap_name]['aap_password'] | default('', true), true) }} # title: AAP Token @@ -158,11 +173,11 @@ preflight_aap_password: >- # CONTROLLER_API_TOKEN env var injected by the controller for its own # jobs, or the aap_token variable on the AAP host. Takes precedence # over username/password. -preflight_aap_token: >- +validate_environment_aap_token: >- {{ lookup('env', 'AAP_TOKEN') | default(lookup('env', 'CONTROLLER_API_TOKEN') - | default(hostvars[preflight_aap_name]['aap_token'] | default('', true), true), true) + | default(hostvars[validate_environment_aap_name]['aap_token'] | default('', true), true), true) }} # title: AAP Verify SSL @@ -171,8 +186,8 @@ preflight_aap_token: >- # Validate the AAP TLS certificate. Cascades from AAP_VALIDATE_CERTS env # var or the aap_validate_certs variable on the AAP host. Defaults to # true. -preflight_aap_validate_certs: "{{ (lookup('env', 'AAP_VALIDATE_CERTS') - | default(hostvars[preflight_aap_name]['aap_validate_certs'] | default(true), true)) | bool }}" +validate_environment_aap_validate_certs: "{{ (lookup('env', 'AAP_VALIDATE_CERTS') + | default(hostvars[validate_environment_aap_name]['aap_validate_certs'] | default(true), true)) | bool }}" # title: CNV Namespace # required: False @@ -180,11 +195,11 @@ preflight_aap_validate_certs: "{{ (lookup('env', 'AAP_VALIDATE_CERTS') # Namespace holding the OpenShift Virtualization (CNV) operator CSV. # Resolves from the cnv_namespace variable or the target cluster # host's cnv_namespace, defaulting to openshift-cnv. -preflight_cnv_namespace: >- +validate_environment_cnv_namespace: >- {{ cnv_namespace | default( - hostvars[preflight_target_name]['cnv_namespace'] + hostvars[validate_environment_target_name]['cnv_namespace'] | default('openshift-cnv', true), true ) @@ -197,11 +212,11 @@ preflight_cnv_namespace: >- # CSV. Resolves from the mtv_namespace variable or the target cluster # host's mtv_namespace (inventory-sample sets it on the cluster host), # defaulting to openshift-mtv. -preflight_mtv_namespace: >- +validate_environment_mtv_namespace: >- {{ mtv_namespace | default( - hostvars[preflight_target_name]['mtv_namespace'] + hostvars[validate_environment_target_name]['mtv_namespace'] | default('openshift-mtv', true), true ) @@ -211,13 +226,13 @@ preflight_mtv_namespace: >- # required: False # description: >- # VDDK init image for direct-source migrations. Resolves from the -# source host's vddk.image dictionary entry (preflight_source_name) or +# source host's vddk.image dictionary entry (validate_environment_source_name) or # the vddk_image variable. The check enforces a scheme-less reference — # Forklift rejects scheme'd images with VDDKInitImageUnavailable. -preflight_vddk_image: >- +validate_environment_vddk_image: >- {{ - __preflight_source_vddk.get('image', vddk_image | default('', true)) - if __preflight_source_vddk is mapping + __validate_environment_source_vddk.get('image', vddk_image | default('', true)) + if __validate_environment_source_vddk is mapping else vddk_image | default('', true) }} ... diff --git a/roles/preflight/meta/argument_specs.yml b/roles/validate_environment/meta/argument_specs.yml similarity index 61% rename from roles/preflight/meta/argument_specs.yml rename to roles/validate_environment/meta/argument_specs.yml index f519815..fd35e61 100644 --- a/roles/preflight/meta/argument_specs.yml +++ b/roles/validate_environment/meta/argument_specs.yml @@ -5,32 +5,53 @@ argument_specs: Validate the migration environment before seeding AAP description: - >- - Runs every preflight check and reports a summary, then fails the - play if any check failed. Checks: OpenShift API auth, CNV and - MTV operator health, AAP access, vCenter connectivity, and VDDK - init image configuration. + Runs the selected validate_environment checks and reports a summary, + then fails the play if any ran check failed. Checks: OpenShift + API auth, CNV and MTV operator health, AAP access, vCenter + connectivity, and VDDK init image configuration. - >- Designed to run from an AAP job template (credentials inject K8S_AUTH_*, VMWARE_*, CONTROLLER_HOST, CONTROLLER_API_TOKEN) or a controller with the sample inventory. Connection variables cascade from environment variables to inventory host variables. - >- - All checks run on every invocation. An unconfigured target fails + All checks run by default; validate_environment_checks limits the + pass to the named checks. An unconfigured target fails that check with guidance rather than crashing the play, so one run reports everything that needs attention. options: - preflight_secure_logging: + validate_environment_checks: + type: list + elements: str + required: false + default: + - openshift + - cnv + - mtv + - aap + - vcenter + - vddk + description: >- + Which environment validation checks to run, as check-name + strings (openshift, cnv, mtv, aap, vcenter, vddk). Defaults to + all six. Accepts a list, a bare check name, or a bracketed + comma-separated string — the CLI -e form, e.g. + -e validate_environment_checks=[vcenter] to debug a single + check. An unknown name fails the include with a loud + file-not-found error — the summary report still runs. + + validate_environment_secure_logging: type: bool required: false default: true description: >- Whether to enable secure logging for tasks that handle credentials. Set to false to debug failed probes. Prefer a - one-off CLI run (--tags ) over flipping the toggle on a - job template; if you must, rotate the exposed credential - (token or password) afterwards. + one-off CLI run (-e validate_environment_secure_logging=false) + over flipping the toggle on a job template; if you must, + rotate the exposed credential (token or password) afterwards. - preflight_target_name: + validate_environment_target_name: type: str required: false description: >- @@ -38,7 +59,7 @@ argument_specs: Defaults to the target_name extra var (injected by the seeded credentials) or the first migration_clusters host. - preflight_source_name: + validate_environment_source_name: type: str required: false description: >- @@ -46,24 +67,24 @@ argument_specs: checks. Defaults to the source_name extra var (injected by the seeded credentials) or the first vm_sources host. - preflight_aap_name: + validate_environment_aap_name: type: str required: false description: >- Inventory host whose aap_* variables feed the AAP check. Defaults to the first migration_aap host. - preflight_openshift_host: + validate_environment_openshift_host: type: str required: false default: "" description: >- OpenShift API endpoint. Resolves from K8S_AUTH_HOST env var or the openshift_host variable on the target cluster host - (preflight_target_name). Required — the preflight aborts + (validate_environment_target_name). Required — the validate_environment aborts without it. - preflight_openshift_api_key: + validate_environment_openshift_api_key: type: str required: false default: "" @@ -71,52 +92,52 @@ argument_specs: description: >- OpenShift API token. Resolves from K8S_AUTH_API_KEY env var or the openshift_api_key variable on the target cluster host - (preflight_target_name). Required — the preflight aborts + (validate_environment_target_name). Required — the validate_environment aborts without it. - preflight_openshift_verify_ssl: + validate_environment_openshift_verify_ssl: type: bool required: false default: true description: >- Validate the OpenShift API TLS certificate. - preflight_vcenter_hostname: + validate_environment_vcenter_hostname: type: str required: false default: "" description: >- vCenter endpoint for the connectivity check. Resolves from VMWARE_HOST env var or the host variable on the source host - (preflight_source_name). The check applies to vmware sources; + (validate_environment_source_name). The check applies to vmware sources; other source types skip it. When unset, the vCenter check fails with instructions. - preflight_vcenter_username: + validate_environment_vcenter_username: type: str required: false default: "" description: >- vCenter username. Resolves from VMWARE_USER env var or the - username variable on the source host (preflight_source_name). + username variable on the source host (validate_environment_source_name). - preflight_vcenter_password: + validate_environment_vcenter_password: type: str required: false default: "" no_log: true description: >- vCenter password. Resolves from VMWARE_PASSWORD env var or the - password variable on the source host (preflight_source_name). + password variable on the source host (validate_environment_source_name). - preflight_vcenter_validate_certs: + validate_environment_vcenter_validate_certs: type: bool required: false default: true description: >- Validate the vCenter TLS certificate. - preflight_aap_hostname: + validate_environment_aap_hostname: type: str required: false default: "" @@ -124,26 +145,27 @@ argument_specs: AAP controller URL for the access check. Resolves from AAP_HOSTNAME env var, the CONTROLLER_HOST env var injected by the controller for its own jobs, or the aap_hostname variable - on the AAP host (preflight_aap_name). When unset, the AAP check + on the AAP host (validate_environment_aap_name). When unset, the AAP check fails with instructions. - preflight_aap_api_path: + validate_environment_aap_api_path: type: str required: false - default: /api/v2/ + default: /api/controller/v2/ description: >- - API base path for the access check. Defaults to /api/v2/ (AWX - and AAP 2.4 and earlier); AAP 2.5 platform deployments serve - the controller API at /api/controller/v2/. + API base path for the access check. Defaults to + /api/controller/v2/ (AAP 2.5 platform deployments); AWX and + AAP 2.4 and earlier serve the controller API at /api/v2/ — + set that value for those environments. - preflight_aap_username: + validate_environment_aap_username: type: str required: false default: "" description: >- AAP username for the access check. - preflight_aap_password: + validate_environment_aap_password: type: str required: false default: "" @@ -151,7 +173,7 @@ argument_specs: description: >- AAP password for the access check when no token is set. - preflight_aap_token: + validate_environment_aap_token: type: str required: false default: "" @@ -162,21 +184,21 @@ argument_specs: own jobs, or the aap_token variable on the AAP host. Takes precedence over username/password. - preflight_aap_validate_certs: + validate_environment_aap_validate_certs: type: bool required: false default: true description: >- Validate the AAP TLS certificate. - preflight_cnv_namespace: + validate_environment_cnv_namespace: type: str required: false default: openshift-cnv description: >- Namespace holding the OpenShift Virtualization operator CSV. - preflight_mtv_namespace: + validate_environment_mtv_namespace: type: str required: false default: openshift-mtv @@ -184,7 +206,7 @@ argument_specs: Namespace holding the MTV operator CSV. Resolves from the mtv_namespace inventory variable by default. - preflight_vddk_image: + validate_environment_vddk_image: type: str required: false default: "" diff --git a/roles/preflight/meta/main.yml b/roles/validate_environment/meta/main.yml similarity index 89% rename from roles/preflight/meta/main.yml rename to roles/validate_environment/meta/main.yml index 9b4cac3..735ec21 100644 --- a/roles/preflight/meta/main.yml +++ b/roles/validate_environment/meta/main.yml @@ -1,6 +1,6 @@ --- galaxy_info: - role_name: preflight + role_name: validate_environment author: Red Hat description: >- Validate the migration environment before seeding AAP — OpenShift API @@ -13,6 +13,7 @@ galaxy_info: - name: EL versions: - all + galaxy_tags: [] dependencies: [] ... diff --git a/roles/validate_environment/tasks/main.yml b/roles/validate_environment/tasks/main.yml new file mode 100644 index 0000000..15d03d9 --- /dev/null +++ b/roles/validate_environment/tasks/main.yml @@ -0,0 +1,32 @@ +--- +# The environment validation pass runs before seeding AAP. +# +# The checks default to all six; validate_environment_checks limits the +# pass to the named checks for a one-off run. The summary report fails +# the play if any ran check failed, so one run reports everything that +# needs attention. Checks whose target is not configured fail that check +# with a message describing what to set — they do not crash the play. + +- name: Include pre-validation of required connection variables + ansible.builtin.include_tasks: + file: pre_validate.yml + +- name: Run environment validation checks + block: + - name: Include each selected environment validation check + ansible.builtin.include_tasks: + file: "validate_{{ __validate_environment_check }}.yml" + # CLI -e passes a string (e.g. -e validate_environment_checks=[vddk]) + # where an inventory var is a real list — normalize both forms here. + loop: "{{ validate_environment_checks + if validate_environment_checks is not string + else (validate_environment_checks | replace('[', '') | replace(']', '') + | split(',') | map('trim') | reject('equalto', '') | list) }}" + loop_control: + loop_var: __validate_environment_check + + always: + - name: Include the validate_environment summary report + ansible.builtin.include_tasks: + file: report.yml +... diff --git a/roles/preflight/tasks/pre_validate.yml b/roles/validate_environment/tasks/pre_validate.yml similarity index 66% rename from roles/preflight/tasks/pre_validate.yml rename to roles/validate_environment/tasks/pre_validate.yml index 07be1be..36cd585 100644 --- a/roles/preflight/tasks/pre_validate.yml +++ b/roles/validate_environment/tasks/pre_validate.yml @@ -7,24 +7,24 @@ - name: pre_validate | Validate required OpenShift connection variables ansible.builtin.assert: that: - - preflight_openshift_host | length > 0 - - preflight_openshift_api_key | length > 0 + - validate_environment_openshift_host | length > 0 + - validate_environment_openshift_api_key | length > 0 fail_msg: >- OpenShift connection is required. Set openshift_host and openshift_api_key on the target cluster host in the inventory (or run from AAP where the target credential injects K8S_AUTH_HOST and K8S_AUTH_API_KEY). success_msg: >- - OpenShift connection configured: {{ preflight_openshift_host }} + OpenShift connection configured: {{ validate_environment_openshift_host }} quiet: true - name: pre_validate | Reject plaintext credential-bearing endpoints ansible.builtin.assert: that: - - (preflight_aap_hostname | trim | lower) is not match('^http://') - - (preflight_openshift_host | trim | lower) is not match('^http://') - - preflight_aap_hostname is not search('://[^/@]+:[^/@]+@') - - preflight_openshift_host is not search('://[^/@]+:[^/@]+@') + - (validate_environment_aap_hostname | trim | lower) is not match('^http://') + - (validate_environment_openshift_host | trim | lower) is not match('^http://') + - validate_environment_aap_hostname is not search('://[^/@]+:[^/@]+@') + - validate_environment_openshift_host is not search('://[^/@]+:[^/@]+@') fail_msg: >- Credential-bearing endpoints must use https and must not embed user:pass@ in the URL — bearer tokens sent to a plaintext endpoint diff --git a/roles/preflight/tasks/report.yml b/roles/validate_environment/tasks/report.yml similarity index 50% rename from roles/preflight/tasks/report.yml rename to roles/validate_environment/tasks/report.yml index f23388b..3284a51 100644 --- a/roles/preflight/tasks/report.yml +++ b/roles/validate_environment/tasks/report.yml @@ -1,8 +1,7 @@ --- # Collect every check result and fail the play if any ran check failed. -# Check files register __preflight_result_* facts with ran: true; the -# summary assert only covers checks that ran, so an isolated --tags debug -# run ends green when the targeted check passed. A check that never ran +# Check files register __validate_environment_result_* facts with ran: true; the +# summary assert only covers checks that ran. A check that never ran # (unexpected failure in a full run) still shows FAIL with "check did # not run" in the summary, so the report never lies. # @@ -15,53 +14,59 @@ # (selectattr(..., 'falsy', true)) when filtering summary values by # key, as the fail_msg does. -- name: report | Build the preflight summary +- name: report | Build the validate_environment summary ansible.builtin.set_fact: - __preflight_summary: + __validate_environment_summary: OpenShift API auth: >- - {{ __preflight_result_openshift | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_openshift + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} OpenShift Virtualization operator: >- - {{ __preflight_result_cnv | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_cnv + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} MTV operator: >- - {{ __preflight_result_mtv | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_mtv + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} AAP access: >- - {{ __preflight_result_aap | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_aap + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} vCenter connectivity: >- - {{ __preflight_result_vcenter | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_vcenter + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} VDDK init image: >- - {{ __preflight_result_vddk | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} + {{ __validate_environment_result_vddk + | default({'ran': false, 'passed': false, 'detail': 'check did not run'}) }} -- name: report | Display the preflight summary +- name: report | Display the validate_environment summary ansible.builtin.debug: msg: >- - {{ __preflight_check.value.passed | bool | ternary('PASS', 'FAIL') }} | - {{ __preflight_check.key }}: {{ __preflight_check.value.detail }} - loop: "{{ __preflight_summary | dict2items }}" + {{ __validate_environment_check.value.passed | bool | ternary('PASS', 'FAIL') }} | + {{ __validate_environment_check.key }}: {{ __validate_environment_check.value.detail }} + loop: "{{ __validate_environment_summary | dict2items }}" loop_control: - loop_var: __preflight_check - label: "{{ __preflight_check.key }}" + loop_var: __validate_environment_check + label: "{{ __validate_environment_check.key }}" -- name: report | Assert every ran preflight check passed +- name: report | Assert every ran validate_environment check passed ansible.builtin.assert: that: - >- - __preflight_summary | dict2items + __validate_environment_summary | dict2items | selectattr('value.ran', 'equalto', true) | map(attribute='value.passed') | map('bool') | select('equalto', false) | list | length == 0 fail_msg: >- - Preflight validation FAILED: - {{ __preflight_summary | dict2items + Environment validation FAILED: + {{ __validate_environment_summary | dict2items | selectattr('value.ran', 'equalto', true) | selectattr('value.passed', 'falsy', true) | map(attribute='key') | join(', ') }}. Fix each failed check before seeding — do not seed to paper over a missing operator. success_msg: >- - Preflight validation passed: all - {{ __preflight_summary | dict2items + Environment validation passed: all + {{ __validate_environment_summary | dict2items | selectattr('value.ran', 'equalto', true) | list | length }} checks green. The environment is ready to seed. quiet: true @@ -69,10 +74,10 @@ - name: report | Remind the operator to rotate credentials exposed in debug output ansible.builtin.debug: msg: >- - preflight_secure_logging is false — task parameters and API responses + validate_environment_secure_logging is false — task parameters and API responses were printed to job output. If this ran on a persistent AAP job template, rotate the exposed credentials now: the OpenShift API key (K8S_AUTH_API_KEY), the AAP token (AAP_TOKEN or CONTROLLER_API_TOKEN), and the vCenter password (VMWARE_PASSWORD). - when: not (preflight_secure_logging | default(true) | bool) + when: not (validate_environment_secure_logging | default(true) | bool) ... diff --git a/roles/validate_environment/tasks/validate_aap.yml b/roles/validate_environment/tasks/validate_aap.yml new file mode 100644 index 0000000..39c06b0 --- /dev/null +++ b/roles/validate_environment/tasks/validate_aap.yml @@ -0,0 +1,49 @@ +--- +# Probe the AAP API with the configured account. +# ansible.builtin.uri is used instead of ansible.controller modules because +# the validate_environment runs before seeding and needs only a lightweight auth probe +# (the me/ endpoint returns the authenticated account); controller modules +# pull in full controller configuration context that does not exist yet. + +- name: validate_aap | Probe AAP API with the configured account + when: validate_environment_aap_hostname | length > 0 + ansible.builtin.uri: + url: "{{ validate_environment_aap_hostname }}{{ validate_environment_aap_api_path }}me/" + method: GET + validate_certs: "{{ validate_environment_aap_validate_certs }}" + headers: + Authorization: >- + {{ + ('Bearer ' ~ (validate_environment_aap_token | trim)) + if validate_environment_aap_token | length > 0 + else ('Basic ' ~ ((validate_environment_aap_username ~ ':' ~ validate_environment_aap_password) | b64encode)) + }} + status_code: [200, 401, 403] + register: __validate_environment_aap + failed_when: false + # A module crash (e.g. aiohttp ContentTypeError when the endpoint answers + # with a non-JSON body) bypasses failed_when and would abort the run; the + # record task below turns the fault into a graceful check result. + ignore_errors: true + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_aap | Record the AAP access check result + ansible.builtin.set_fact: + __validate_environment_result_aap: + ran: true + passed: "{{ (validate_environment_aap_hostname | length > 0) + and ((__validate_environment_aap.status | default(0)) == 200) }}" + detail: >- + {% if validate_environment_aap_hostname | length == 0 %} + Not configured — set aap_hostname in the inventory (or AAP_HOSTNAME) + to enable the AAP access check + {% elif (__validate_environment_aap.status | default(0)) == 200 %} + HTTP 200 at {{ validate_environment_aap_hostname }} (authenticated as + {{ __validate_environment_aap.json.results[0].username | default('unknown', true) }}) + {% else %} + HTTP {{ __validate_environment_aap.status | default('connection error') }} at + {{ validate_environment_aap_hostname }} — 401/403 means the account or token is + wrong or expired; 404 means the API path is wrong — AAP 2.4 + and earlier deployments need validate_environment_aap_api_path=/api/v2/ + {% endif %} +... diff --git a/roles/validate_environment/tasks/validate_cnv.yml b/roles/validate_environment/tasks/validate_cnv.yml new file mode 100644 index 0000000..a2e09e5 --- /dev/null +++ b/roles/validate_environment/tasks/validate_cnv.yml @@ -0,0 +1,57 @@ +--- +# Query the ClusterServiceVersion list in the CNV namespace. +# kubernetes.core.k8s_info is certified content and the collection's +# established pattern for cluster queries. + +- name: validate_cnv | List ClusterServiceVersions in the CNV namespace + kubernetes.core.k8s_info: + host: "{{ validate_environment_openshift_host }}" + api_key: "{{ validate_environment_openshift_api_key }}" + validate_certs: "{{ validate_environment_openshift_verify_ssl }}" + api_version: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + namespace: "{{ validate_environment_cnv_namespace }}" + register: __validate_environment_cnv_csvs + failed_when: false + # A module crash (e.g. an unresolvable host or a malformed kubeconfig) + # bypasses failed_when and would abort the run; the record task below + # turns the fault into a graceful check result. + ignore_errors: true + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_cnv | Find the OpenShift Virtualization CSV + ansible.builtin.set_fact: + __validate_environment_cnv_found: >- + {{ __validate_environment_cnv_csvs.resources | default([]) + | selectattr('metadata.name', 'search', 'kubevirt-hyperconverged') + | list | length > 0 }} + __validate_environment_cnv_csv: >- + {{ __validate_environment_cnv_csvs.resources | default([]) + | selectattr('metadata.name', 'search', 'kubevirt-hyperconverged') + | list | first | default({}) }} + +- name: validate_cnv | Record the OpenShift Virtualization operator check result + ansible.builtin.set_fact: + __validate_environment_result_cnv: + ran: true + passed: "{{ __validate_environment_cnv_found + and (__validate_environment_cnv_csv.status.phase | default('')) == 'Succeeded' }}" + detail: >- + {% if __validate_environment_cnv_csvs.resources is not defined %} + Cluster API query failed in {{ validate_environment_cnv_namespace }} — + check the OpenShift connection and credentials first; the + operator check is inconclusive when the API is unreachable + {% elif not __validate_environment_cnv_found %} + No kubevirt-hyperconverged CSV found in + {{ validate_environment_cnv_namespace }} — install the OpenShift + Virtualization operator before seeding + {% elif (__validate_environment_cnv_csv.status.phase | default('')) != 'Succeeded' %} + CSV {{ __validate_environment_cnv_csv.metadata.name }} in + {{ validate_environment_cnv_namespace }} is + {{ __validate_environment_cnv_csv.status.phase | default('unknown') }} — + expected Succeeded + {% else %} + CSV {{ __validate_environment_cnv_csv.metadata.name }} Succeeded in + {{ validate_environment_cnv_namespace }} + {% endif %} +... diff --git a/roles/validate_environment/tasks/validate_mtv.yml b/roles/validate_environment/tasks/validate_mtv.yml new file mode 100644 index 0000000..9b7700d --- /dev/null +++ b/roles/validate_environment/tasks/validate_mtv.yml @@ -0,0 +1,53 @@ +--- +# Query the ClusterServiceVersion list in the MTV namespace. +# kubernetes.core.k8s_info is certified content and the collection's +# established pattern for cluster queries. + +- name: validate_mtv | List ClusterServiceVersions in the MTV namespace + kubernetes.core.k8s_info: + host: "{{ validate_environment_openshift_host }}" + api_key: "{{ validate_environment_openshift_api_key }}" + validate_certs: "{{ validate_environment_openshift_verify_ssl }}" + api_version: operators.coreos.com/v1alpha1 + kind: ClusterServiceVersion + namespace: "{{ validate_environment_mtv_namespace }}" + register: __validate_environment_mtv_csvs + failed_when: false + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_mtv | Find the MTV CSV + ansible.builtin.set_fact: + __validate_environment_mtv_found: >- + {{ __validate_environment_mtv_csvs.resources | default([]) + | selectattr('metadata.name', 'search', 'mtv-operator') + | list | length > 0 }} + __validate_environment_mtv_csv: >- + {{ __validate_environment_mtv_csvs.resources | default([]) + | selectattr('metadata.name', 'search', 'mtv-operator') + | list | first | default({}) }} + +- name: validate_mtv | Record the MTV operator check result + ansible.builtin.set_fact: + __validate_environment_result_mtv: + ran: true + passed: "{{ __validate_environment_mtv_found + and (__validate_environment_mtv_csv.status.phase | default('')) == 'Succeeded' }}" + detail: >- + {% if __validate_environment_mtv_csvs.resources is not defined %} + Cluster API query failed in {{ validate_environment_mtv_namespace }} — + check the OpenShift connection and credentials first; the + operator check is inconclusive when the API is unreachable + {% elif not __validate_environment_mtv_found %} + No mtv-operator CSV found in {{ validate_environment_mtv_namespace }} — + install the Migration Toolkit for Virtualization operator before + seeding + {% elif (__validate_environment_mtv_csv.status.phase | default('')) != 'Succeeded' %} + CSV {{ __validate_environment_mtv_csv.metadata.name }} in + {{ validate_environment_mtv_namespace }} is + {{ __validate_environment_mtv_csv.status.phase | default('unknown') }} — + expected Succeeded + {% else %} + CSV {{ __validate_environment_mtv_csv.metadata.name }} Succeeded in + {{ validate_environment_mtv_namespace }} + {% endif %} +... diff --git a/roles/validate_environment/tasks/validate_openshift.yml b/roles/validate_environment/tasks/validate_openshift.yml new file mode 100644 index 0000000..d61501c --- /dev/null +++ b/roles/validate_environment/tasks/validate_openshift.yml @@ -0,0 +1,38 @@ +--- +# Probe the OpenShift API with the configured credential. +# ansible.builtin.uri is used instead of the k8s modules because the HTTP +# status code cleanly distinguishes 401/403 (credential rejected) from +# 200/201 (authenticated). + +- name: validate_openshift | Probe OpenShift API SelfSubjectReview with the configured credential + ansible.builtin.uri: + url: "{{ validate_environment_openshift_host }}/apis/authentication.k8s.io/v1/selfsubjectreviews" + method: POST + body: '{"apiVersion":"authentication.k8s.io/v1","kind":"SelfSubjectReview"}' + body_format: json + validate_certs: "{{ validate_environment_openshift_verify_ssl }}" + headers: + Authorization: "{{ 'Bearer ' ~ (validate_environment_openshift_api_key | trim) }}" + status_code: [200, 201, 401, 403] + register: __validate_environment_ocp + # No module state is created — the review object is informational only, + # so this probe must never report changed. + changed_when: false + failed_when: false + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_openshift | Record the OpenShift authentication check result + ansible.builtin.set_fact: + __validate_environment_result_openshift: + ran: true + passed: "{{ (__validate_environment_ocp.status | default(0)) in [200, 201] }}" + detail: >- + {% if (__validate_environment_ocp.status | default(0)) in [200, 201] %} + HTTP {{ __validate_environment_ocp.status }} at {{ validate_environment_openshift_host }} + (identity: {{ __validate_environment_ocp.json.status.userInfo.username | default('unknown', true) }}) + {% else %} + HTTP {{ __validate_environment_ocp.status | default('connection error') }} at + {{ validate_environment_openshift_host }} — 401/403 means the token or + credential is missing, expired, or rejected + {% endif %} +... diff --git a/roles/validate_environment/tasks/validate_vcenter.yml b/roles/validate_environment/tasks/validate_vcenter.yml new file mode 100644 index 0000000..492df3a --- /dev/null +++ b/roles/validate_environment/tasks/validate_vcenter.yml @@ -0,0 +1,104 @@ +--- +# Probe vCenter connectivity and authentication. +# Two paths: the certified vmware.vmware_rest REST collection (vCenter 7.0+) +# first, then a community.vmware SOAP (pyVmomi) fallback that also covers 6.x +# — older vCenters do not serve the /api/ REST tree at all, and proxies in +# front of them commonly map only /sdk and /rest. Listing datacenters is the +# lightest operation that proves both connectivity and authentication against +# the configured source. The check only applies to vmware sources — other +# source types (ovirt) skip it. +# +# Module tier justification (community content): the certified +# vmware.vmware_rest collection requires vCenter 7.0+ and cannot reach 6.x at +# all (no /api/ tree), and the certified successor vmware.vmware collection +# does not yet ship a datacenter_info module. community.vmware +# .vmware_datacenter_info (pyVmomi, vCenter 6.5+) is the only content that +# covers the 6.x path, so the SOAP fallback runs only when the REST probe +# cannot return data. +# +# Pin-range safety: community.vmware is pinned >=5.5.0 (galaxy.yml); the +# task uses only the core connection options (hostname, username, password, +# validate_certs), unchanged across 5.x. Requires pyVmomi, which the +# execution environments ship. + +- name: validate_vcenter | List vCenter datacenters via the REST API (vCenter 7.0+) + when: + - validate_environment_vcenter_hostname | length > 0 + - __validate_environment_source_type | length == 0 or __validate_environment_source_type == 'vmware' + vmware.vmware_rest.vcenter_datacenter_info: + vcenter_hostname: "{{ validate_environment_vcenter_hostname }}" + vcenter_username: "{{ validate_environment_vcenter_username }}" + vcenter_password: "{{ validate_environment_vcenter_password }}" + vcenter_validate_certs: "{{ validate_environment_vcenter_validate_certs }}" + register: __validate_environment_vcenter_rest + failed_when: false + # A module crash (e.g. aiohttp ContentTypeError when the endpoint answers + # with a non-JSON body) bypasses failed_when and would abort the run; the + # fallback and record tasks below turn the fault into a graceful check + # result. + ignore_errors: true + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_vcenter | Fall back to the SOAP SDK for vCenter 6.x (no REST API) + when: + - validate_environment_vcenter_hostname | length > 0 + - __validate_environment_source_type | length == 0 or __validate_environment_source_type == 'vmware' + - __validate_environment_vcenter_rest.value is not defined + or __validate_environment_vcenter_rest.value | length == 0 + community.vmware.vmware_datacenter_info: + hostname: "{{ validate_environment_vcenter_hostname }}" + username: "{{ validate_environment_vcenter_username }}" + password: "{{ validate_environment_vcenter_password }}" + validate_certs: "{{ validate_environment_vcenter_validate_certs }}" + register: __validate_environment_vcenter_soap + failed_when: false + ignore_errors: true + no_log: "{{ validate_environment_secure_logging }}" + +- name: validate_vcenter | Merge the REST and SOAP results into one datacenter list + # Prefers a result with datacenters (the SOAP fallback can find data the + # proxied REST path misses); falls back to an empty result from either + # path (connected but none visible), then the empty-string sentinel when + # neither path returned data — the record task distinguishes a real list + # from '' by type. + ansible.builtin.set_fact: + __validate_environment_vcenter_dcs: "{{ ([(__validate_environment_vcenter_rest.value | default(none)), + (__validate_environment_vcenter_soap.datacenter_info | default(none))] + | reject('none') | select('truthy') | first) + | default([(__validate_environment_vcenter_rest.value | default(none)), + (__validate_environment_vcenter_soap.datacenter_info | default(none))] + | reject('none') | first | default(none), true) }}" + +- name: validate_vcenter | Record the vCenter connectivity check result + ansible.builtin.set_fact: + __validate_environment_result_vcenter: + ran: true + passed: "{{ (__validate_environment_source_type | length > 0 and __validate_environment_source_type != 'vmware') + or ((validate_environment_vcenter_hostname | length > 0) + and (__validate_environment_vcenter_dcs is not string) + and (__validate_environment_vcenter_dcs | length) > 0) }}" + detail: >- + {% if __validate_environment_source_type | length > 0 and __validate_environment_source_type != 'vmware' %} + Skipped — source type is {{ __validate_environment_source_type }}, not vmware; + the vCenter check does not apply + {% elif validate_environment_vcenter_hostname | length == 0 %} + Not configured — set host/username/password on the source host + (or run from AAP where the source credential injects VMWARE_*) to + enable the vCenter check + {% elif __validate_environment_vcenter_dcs is not string and __validate_environment_vcenter_dcs | length > 0 %} + HTTP session established at {{ validate_environment_vcenter_hostname }} + ({{ __validate_environment_vcenter_dcs | length }} datacenters visible via + {{ 'REST' if __validate_environment_vcenter_rest.value is defined + and __validate_environment_vcenter_rest.value | length > 0 + else 'the SOAP SDK' }}) + {% elif __validate_environment_vcenter_dcs is not string %} + Connected to {{ validate_environment_vcenter_hostname }} but no datacenters + are visible — a migration source needs at least one + {% else %} + vCenter authentication or connectivity failed at + {{ validate_environment_vcenter_hostname }} — re-run the vCenter + check as a one-off CLI run with validate_environment_checks=[vcenter] + and validate_environment_secure_logging=false to see the fault, + then rotate the exposed credential + {% endif %} +... diff --git a/roles/preflight/tasks/validate_vddk.yml b/roles/validate_environment/tasks/validate_vddk.yml similarity index 51% rename from roles/preflight/tasks/validate_vddk.yml rename to roles/validate_environment/tasks/validate_vddk.yml index b02cadf..a9ca352 100644 --- a/roles/preflight/tasks/validate_vddk.yml +++ b/roles/validate_environment/tasks/validate_vddk.yml @@ -9,23 +9,23 @@ - name: validate_vddk | Record the VDDK init image check result ansible.builtin.set_fact: - __preflight_result_vddk: + __validate_environment_result_vddk: ran: true - passed: "{{ (__preflight_source_type | length > 0 and __preflight_source_type != 'vmware') - or (preflight_vddk_image | length > 0 - and not (preflight_vddk_image is match('^[A-Za-z][A-Za-z0-9+.-]*://'))) }}" + passed: "{{ (__validate_environment_source_type | length > 0 and __validate_environment_source_type != 'vmware') + or (validate_environment_vddk_image | length > 0 + and not (validate_environment_vddk_image is match('^[A-Za-z][A-Za-z0-9+.-]*://'))) }}" detail: >- - {% if __preflight_source_type | length > 0 and __preflight_source_type != 'vmware' %} - Skipped — source type is {{ __preflight_source_type }}, not vmware; + {% if __validate_environment_source_type | length > 0 and __validate_environment_source_type != 'vmware' %} + Skipped — source type is {{ __validate_environment_source_type }}, not vmware; the VDDK check does not apply - {% elif preflight_vddk_image | length == 0 %} + {% elif validate_environment_vddk_image | length == 0 %} No VDDK init image configured — set vddk.image on the source host (required for direct-source migrations with VDDK) - {% elif preflight_vddk_image is match('^[A-Za-z][A-Za-z0-9+.-]*://') %} - VDDK init image {{ preflight_vddk_image }} includes a scheme — + {% elif validate_environment_vddk_image is match('^[A-Za-z][A-Za-z0-9+.-]*://') %} + VDDK init image {{ validate_environment_vddk_image }} includes a scheme — Forklift rejects scheme'd references (VDDKInitImageUnavailable); remove the scheme prefix {% else %} - VDDK init image configured: {{ preflight_vddk_image }} + VDDK init image configured: {{ validate_environment_vddk_image }} {% endif %} ... diff --git a/roles/preflight/vars/main.yml b/roles/validate_environment/vars/main.yml similarity index 52% rename from roles/preflight/vars/main.yml rename to roles/validate_environment/vars/main.yml index e8a58ff..8d50508 100644 --- a/roles/preflight/vars/main.yml +++ b/roles/validate_environment/vars/main.yml @@ -1,9 +1,9 @@ --- # Internal derived values — not user-facing. Users set the source host's # type in the inventory; this resolves it for the vCenter and VDDK gates. -__preflight_source_type: "{{ hostvars[preflight_source_name]['type'] | default('', true) }}" +__validate_environment_source_type: "{{ hostvars[validate_environment_source_name]['type'] | default('', true) }}" # Internal: the source host's vddk dictionary (empty when the host or the # vddk entry is absent). -__preflight_source_vddk: "{{ hostvars[preflight_source_name]['vddk'] | default({}, true) }}" +__validate_environment_source_vddk: "{{ hostvars[validate_environment_source_name]['vddk'] | default({}, true) }}" ... diff --git a/tests/integration/targets/preflight_report/tasks/main.yml b/tests/integration/targets/preflight_report/tasks/main.yml deleted file mode 100644 index 9d5275d..0000000 --- a/tests/integration/targets/preflight_report/tasks/main.yml +++ /dev/null @@ -1,103 +0,0 @@ ---- -# Regression test for the preflight report aggregation. -# Runs the report tasks against pre-populated check facts — no real -# endpoints — covering the three summary paths: an isolated tagged run, -# an all-passed full run with mixed boolean forms, and a failed full run -# whose assert names the failed checks. - -- name: Main | Isolated run — two checks ran, the assert covers those only - vars: - __preflight_result_openshift: {ran: true, passed: true, detail: 'isolated pass'} - __preflight_result_aap: {ran: true, passed: 'True', detail: 'isolated pass (string form)'} - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: report.yml - -- name: Main | Full run — all checks ran and passed in mixed boolean forms - vars: - __preflight_result_openshift: {ran: true, passed: true, detail: 'bool true'} - __preflight_result_cnv: {ran: true, passed: 'True', detail: 'string true'} - __preflight_result_mtv: {ran: true, passed: true, detail: 'bool true'} - __preflight_result_aap: {ran: true, passed: 'True', detail: 'string true'} - __preflight_result_vcenter: {ran: true, passed: true, detail: 'bool true'} - __preflight_result_vddk: {ran: true, passed: 'True', detail: 'string true'} - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: report.yml - -- name: Main | Full run — ran checks failed, the assert names them - block: - - name: Main | Run the report with a failed check - vars: - __preflight_result_openshift: {ran: true, passed: true, detail: 'pass'} - __preflight_result_cnv: {ran: true, passed: false, detail: 'failed'} - __preflight_result_mtv: {ran: true, passed: 'False', detail: 'string false form'} - __preflight_result_aap: {ran: true, passed: 'True', detail: 'pass'} - __preflight_result_vcenter: {ran: true, passed: true, detail: 'pass'} - __preflight_result_vddk: {ran: true, passed: 'True', detail: 'pass'} - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: report.yml - rescue: - - name: Main | Mark the expected failure observed - ansible.builtin.set_fact: - __preflight_report_failure_observed: true - - - name: Main | Assert the summary marks both failed checks - ansible.builtin.assert: - that: - - not (__preflight_summary['OpenShift Virtualization operator'].passed | bool) - - not (__preflight_summary['MTV operator'].passed | bool) - quiet: true - -- name: Main | Assert the expected failure was observed - ansible.builtin.assert: - that: __preflight_report_failure_observed | default(false) | bool - quiet: true - -- name: Main | pre_validate accepts a clean https endpoint - vars: - preflight_openshift_host: https://api.cluster.example.com - preflight_openshift_api_key: test-token - preflight_aap_hostname: https://aap.example.com - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: pre_validate.yml - -- name: Main | pre_validate rejects a user:pass@ endpoint - block: - - name: Main | Run pre_validate with embedded credentials - vars: - preflight_openshift_host: https://svc-user:svc-pass@cluster.example.com - preflight_openshift_api_key: test-token - preflight_aap_hostname: https://aap.example.com - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: pre_validate.yml - rescue: - - name: Main | Mark the expected userinfo rejection observed - ansible.builtin.set_fact: - __preflight_userinfo_rejection_observed: true - -- name: Main | pre_validate rejects a case-variant plaintext endpoint - block: - - name: Main | Run pre_validate with an uppercase scheme - vars: - preflight_openshift_host: HTTP://api.cluster.example.com - preflight_openshift_api_key: test-token - preflight_aap_hostname: https://aap.example.com - ansible.builtin.include_role: - name: infra.openshift_virtualization_migration.preflight - tasks_from: pre_validate.yml - rescue: - - name: Main | Mark the expected plaintext rejection observed - ansible.builtin.set_fact: - __preflight_plaintext_rejection_observed: true - -- name: Main | Assert both endpoint rejections fired - ansible.builtin.assert: - that: - - __preflight_userinfo_rejection_observed | default(false) | bool - - __preflight_plaintext_rejection_observed | default(false) | bool - quiet: true -... diff --git a/tests/integration/targets/validate_environment_report/tasks/main.yml b/tests/integration/targets/validate_environment_report/tasks/main.yml new file mode 100644 index 0000000..04b0fc0 --- /dev/null +++ b/tests/integration/targets/validate_environment_report/tasks/main.yml @@ -0,0 +1,103 @@ +--- +# Regression test for the validate_environment report aggregation. +# Runs the report tasks against pre-populated check facts — no real +# endpoints — covering the three summary paths: an isolated partial run, +# an all-passed full run with mixed boolean forms, and a failed full run +# whose assert names the failed checks. + +- name: Main | Isolated run — two checks ran, the assert covers those only + vars: + __validate_environment_result_openshift: {ran: true, passed: true, detail: 'isolated pass'} + __validate_environment_result_aap: {ran: true, passed: 'True', detail: 'isolated pass (string form)'} + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: report.yml + +- name: Main | Full run — all checks ran and passed in mixed boolean forms + vars: + __validate_environment_result_openshift: {ran: true, passed: true, detail: 'bool true'} + __validate_environment_result_cnv: {ran: true, passed: 'True', detail: 'string true'} + __validate_environment_result_mtv: {ran: true, passed: true, detail: 'bool true'} + __validate_environment_result_aap: {ran: true, passed: 'True', detail: 'string true'} + __validate_environment_result_vcenter: {ran: true, passed: true, detail: 'bool true'} + __validate_environment_result_vddk: {ran: true, passed: 'True', detail: 'string true'} + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: report.yml + +- name: Main | Full run — ran checks failed, the assert names them + block: + - name: Main | Run the report with a failed check + vars: + __validate_environment_result_openshift: {ran: true, passed: true, detail: 'pass'} + __validate_environment_result_cnv: {ran: true, passed: false, detail: 'failed'} + __validate_environment_result_mtv: {ran: true, passed: 'False', detail: 'string false form'} + __validate_environment_result_aap: {ran: true, passed: 'True', detail: 'pass'} + __validate_environment_result_vcenter: {ran: true, passed: true, detail: 'pass'} + __validate_environment_result_vddk: {ran: true, passed: 'True', detail: 'pass'} + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: report.yml + rescue: + - name: Main | Mark the expected failure observed + ansible.builtin.set_fact: + __validate_environment_report_failure_observed: true + + - name: Main | Assert the summary marks both failed checks + ansible.builtin.assert: + that: + - not (__validate_environment_summary['OpenShift Virtualization operator'].passed | bool) + - not (__validate_environment_summary['MTV operator'].passed | bool) + quiet: true + +- name: Main | Assert the expected failure was observed + ansible.builtin.assert: + that: __validate_environment_report_failure_observed | default(false) | bool + quiet: true + +- name: Main | pre_validate accepts a clean https endpoint + vars: + validate_environment_openshift_host: https://api.cluster.example.com + validate_environment_openshift_api_key: test-token + validate_environment_aap_hostname: https://aap.example.com + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: pre_validate.yml + +- name: Main | pre_validate rejects a user:pass@ endpoint + block: + - name: Main | Run pre_validate with embedded credentials + vars: + validate_environment_openshift_host: https://svc-user:svc-pass@cluster.example.com + validate_environment_openshift_api_key: test-token + validate_environment_aap_hostname: https://aap.example.com + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: pre_validate.yml + rescue: + - name: Main | Mark the expected userinfo rejection observed + ansible.builtin.set_fact: + __validate_environment_userinfo_rejection_observed: true + +- name: Main | pre_validate rejects a case-variant plaintext endpoint + block: + - name: Main | Run pre_validate with an uppercase scheme + vars: + validate_environment_openshift_host: HTTP://api.cluster.example.com + validate_environment_openshift_api_key: test-token + validate_environment_aap_hostname: https://aap.example.com + ansible.builtin.include_role: + name: infra.openshift_virtualization_migration.validate_environment + tasks_from: pre_validate.yml + rescue: + - name: Main | Mark the expected plaintext rejection observed + ansible.builtin.set_fact: + __validate_environment_plaintext_rejection_observed: true + +- name: Main | Assert both endpoint rejections fired + ansible.builtin.assert: + that: + - __validate_environment_userinfo_rejection_observed | default(false) | bool + - __validate_environment_plaintext_rejection_observed | default(false) | bool + quiet: true +...