From 08f2254d010051eec0b115ba335c71885e9c8068 Mon Sep 17 00:00:00 2001 From: Alberto Florez Date: Tue, 26 May 2026 12:56:01 +0200 Subject: [PATCH 1/2] feat: add ESXi host support for dedicated migration networks --- inventory.yml | 55 +++++++++++-------- playbooks/mtv_provider_vmware.yml | 1 + roles/aap_seed/defaults/main.yml | 5 ++ .../templates/vmware/target_credential.yml.j2 | 5 ++ .../tasks/_mtv_provider_vmware.yml | 42 ++++++++++++++ .../templates/vmware_esxi_skeleton.yml.j2 | 14 +++++ 6 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 roles/mtv_management/templates/vmware_esxi_skeleton.yml.j2 diff --git a/inventory.yml b/inventory.yml index 59c01a4..fdd1504 100644 --- a/inventory.yml +++ b/inventory.yml @@ -154,28 +154,39 @@ migration_spoke: # storage and networking> ## Example Migration Target (uncomment to use as a baseline) - # - name: vmware-2w7dj - # type: vmware - # host: vcsrs00-vc.demo.example.com - # username: changeme - # password: changeme - # vddk: - # image: registry.example.com/openshift-virtualization-migration-vddk:latest - # username: "{{ container_username }}" - # password: "{{ container_password }}" - # sdkEndpoint: /sdk - # insecureSkipTlsVerify: true - # mapping: - # create: true + - name: vmware-2w7dj + type: vmware + host: vcsrs00-vc.demo.example.com + username: changeme + password: changeme + vddk: + image: registry.example.com/openshift-virtualization-migration-vddk:latest + username: "{{ container_username }}" + password: "{{ container_password }}" + sdkEndpoint: /sdk + insecureSkipTlsVerify: true + mapping: + create: true + esxi_hosts: + - id: host-1 + password: pass1 + ip: 10.10.10.123 + user: root + - id: host-2 + password: pass2 + ip: 10.10.10.124 + user: root ## Example Migration Target (uncomment to use as a baseline) - # - name: rhev-target-1 # Required - # type: ovirt - # host: rhev.demo.example.com - # username: changeme - # sdkEndpoint: /ovirt-engine/api #if not provided automatically will be added - # password: changeme - # insecureSkipTlsVerify: true - # mapping: - # create: true + - name: rhev-target-1 # Required + type: ovirt + host: rhev.demo.example.com + username: changeme + sdkEndpoint: /ovirt-engine/api #if not provided automatically will be added + password: changeme + insecureSkipTlsVerify: true + mapping: + create: true + + ... diff --git a/playbooks/mtv_provider_vmware.yml b/playbooks/mtv_provider_vmware.yml index dced461..edf407c 100644 --- a/playbooks/mtv_provider_vmware.yml +++ b/playbooks/mtv_provider_vmware.yml @@ -21,4 +21,5 @@ secretRef: "{{ vmware_credentials_secret_ref | default(omit, true) }}" vddk: image: "{{ vmware_vddk_init_image | default(omit, true) }}" + esxi_hosts: "{{ vmware_esxi_hosts | default('[]', true) | from_json }}" ... diff --git a/roles/aap_seed/defaults/main.yml b/roles/aap_seed/defaults/main.yml index 3c00b9e..b569ba6 100644 --- a/roles/aap_seed/defaults/main.yml +++ b/roles/aap_seed/defaults/main.yml @@ -430,6 +430,10 @@ aap_seed_controller_credential_types: - id: provider_name type: string label: Provider source + - id: esxi_host_list + type: string + label: ESXi configurations for migration network + multiline: true required: - name - host @@ -446,6 +450,7 @@ aap_seed_controller_credential_types: vmware_vddk_init_image_password: "{% raw %}{ { vddk_init_image_password }}{% endraw %}" vmware_vddk_init_image_credentials_secret: "{% raw %}{ { vddk_init_image_credentials_secret }}{% endraw %}" provider: "{% raw %}{ { provider_name }}{% endraw %}" + vmware_esxi_hosts: "{% raw %}{ { esxi_host_list }}{% endraw %}" env: VMWARE_HOST: "{% raw %}{ { host }}{% endraw %}" VMWARE_USER: "{% raw %}{ { username }}{% endraw %}" diff --git a/roles/aap_seed/templates/vmware/target_credential.yml.j2 b/roles/aap_seed/templates/vmware/target_credential.yml.j2 index 6b9a319..da12e61 100644 --- a/roles/aap_seed/templates/vmware/target_credential.yml.j2 +++ b/roles/aap_seed/templates/vmware/target_credential.yml.j2 @@ -24,3 +24,8 @@ inputs: vddk_init_image_credentials_secret: {{ migration_target['vddk']['credentialsSecret'] }} {% endif %} {% endif %} + +{% if 'esxi_hosts' in migration_target and migration_target['esxi_hosts'] is not none %} + esxi_host_list: | + {{ migration_target.esxi_hosts | to_json }} +{% endif %} diff --git a/roles/mtv_management/tasks/_mtv_provider_vmware.yml b/roles/mtv_management/tasks/_mtv_provider_vmware.yml index 8a779af..478f0ce 100644 --- a/roles/mtv_management/tasks/_mtv_provider_vmware.yml +++ b/roles/mtv_management/tasks/_mtv_provider_vmware.yml @@ -85,4 +85,46 @@ until: - mtv_management_r_vmware_provider.result is defined - mtv_management_r_vmware_provider.result | length > 0 + +- name: _mtv_provider_vmware | Create VMware Host credentials secret + redhat.openshift.k8s: + state: present + definition: + apiVersion: v1 + kind: Secret + type: Opaque + metadata: + name: "{{ mtv_management_populated_vmware_target['name'] }}-{{ vmware_esxi.id }}-secret" + namespace: "{{ mtv_management_provider_namespace }}" + data: + insecureSkipVerify: "{{ (vmware_esxi.insecureSkipTlsVerify | default(mtv_management_vmware_provider_insecure_skip_tls_verify) | bool) | b64encode if 'certificate' not in vmware_esxi or vmware_esxi['certificate'] | default('') | trim | length == 0 else false | b64encode }}" # noqa: yaml[line-length] + password: "{{ vmware_esxi.password | b64encode }}" + user: "{{ vmware_esxi.user | b64encode }}" + ip: "{{ vmware_esxi.ip | b64encode }}" + provider: "{{ mtv_management_populated_vmware_target['name'] | b64encode }}" + apply: true + loop: "{{ mtv_management_populated_vmware_target.esxi_hosts | default([]) }}" + loop_control: + loop_var: vmware_esxi + when: + - mtv_management_populated_vmware_target.esxi_hosts is defined + - mtv_management_populated_vmware_target.esxi_hosts | length > 0 + +- name: _mtv_provider_vmware | Create VMware Host resource + redhat.openshift.k8s: + state: present + definition: "{{ (lookup('ansible.builtin.template', 'vmware_esxi_skeleton.yml.j2') | from_yaml) }}" + apply: true + register: mtv_management_r_vmware_esxi + retries: 100 + delay: 10 + until: + - mtv_management_r_vmware_esxi.result is defined + - mtv_management_r_vmware_esxi.result | length > 0 + loop: "{{ mtv_management_populated_vmware_target.esxi_hosts | default([]) }}" + loop_control: + loop_var: vmware_esxi + when: + - mtv_management_populated_vmware_target.esxi_hosts is defined + - mtv_management_populated_vmware_target.esxi_hosts | length > 0 ... diff --git a/roles/mtv_management/templates/vmware_esxi_skeleton.yml.j2 b/roles/mtv_management/templates/vmware_esxi_skeleton.yml.j2 new file mode 100644 index 0000000..73f41c3 --- /dev/null +++ b/roles/mtv_management/templates/vmware_esxi_skeleton.yml.j2 @@ -0,0 +1,14 @@ +apiVersion: forklift.konveyor.io/v1beta1 +kind: Host +metadata: + name: "{{ mtv_management_populated_vmware_target['name'] }}-{{ vmware_esxi.id }}-config" + namespace: "{{ mtv_management_provider_namespace }}" +spec: + id: "{{ vmware_esxi.id }}" + ipAddress: "{{ vmware_esxi.ip }}" + provider: + name: "{{ mtv_management_populated_vmware_target['name'] }}" + namespace: "{{ mtv_management_provider_namespace }}" + secret: + name: "{{ mtv_management_populated_vmware_target['name'] }}-{{ vmware_esxi.id }}-secret" + namespace: "{{ mtv_management_provider_namespace }}" From 49f98c13e8905e9366f7a62b0f4d225b646b5bd8 Mon Sep 17 00:00:00 2001 From: Alberto Florez Date: Sat, 30 May 2026 18:07:40 +0200 Subject: [PATCH 2/2] fix: address PR feedback regarding validation, security, and parsing --- inventory.yml | 63 +++++++++---------- playbooks/mtv_provider_vmware.yml | 16 ++++- .../tasks/_mtv_provider_vmware.yml | 20 +++++- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/inventory.yml b/inventory.yml index fdd1504..1fd126a 100644 --- a/inventory.yml +++ b/inventory.yml @@ -154,39 +154,38 @@ migration_spoke: # storage and networking> ## Example Migration Target (uncomment to use as a baseline) - - name: vmware-2w7dj - type: vmware - host: vcsrs00-vc.demo.example.com - username: changeme - password: changeme - vddk: - image: registry.example.com/openshift-virtualization-migration-vddk:latest - username: "{{ container_username }}" - password: "{{ container_password }}" - sdkEndpoint: /sdk - insecureSkipTlsVerify: true - mapping: - create: true - esxi_hosts: - - id: host-1 - password: pass1 - ip: 10.10.10.123 - user: root - - id: host-2 - password: pass2 - ip: 10.10.10.124 - user: root + # - name: vmware-2w7dj + # type: vmware + # host: vcsrs00-vc.demo.example.com + # username: changeme + # password: changeme + # vddk: + # image: registry.example.com/openshift-virtualization-migration-vddk:latest + # username: "{{ container_username }}" + # password: "{{ container_password }}" + # sdkEndpoint: /sdk + # insecureSkipTlsVerify: true + # mapping: + # create: true + # esxi_hosts: + # - id: host-1 + # password: pass1 + # ip: 10.10.10.123 + # user: root + # - id: host-2 + # password: pass2 + # ip: 10.10.10.123 + # user: root ## Example Migration Target (uncomment to use as a baseline) - - name: rhev-target-1 # Required - type: ovirt - host: rhev.demo.example.com - username: changeme - sdkEndpoint: /ovirt-engine/api #if not provided automatically will be added - password: changeme - insecureSkipTlsVerify: true - mapping: - create: true - + # - name: rhev-target-1 # Required + # type: ovirt + # host: rhev.demo.example.com + # username: changeme + # sdkEndpoint: /ovirt-engine/api #if not provided automatically will be added + # password: changeme + # insecureSkipTlsVerify: true + # mapping: + # create: true ... diff --git a/playbooks/mtv_provider_vmware.yml b/playbooks/mtv_provider_vmware.yml index edf407c..af85f6f 100644 --- a/playbooks/mtv_provider_vmware.yml +++ b/playbooks/mtv_provider_vmware.yml @@ -4,6 +4,18 @@ connection: local gather_facts: false tasks: + - name: Validate ESXi hosts input format (Try Wrapper) + block: + - name: Attempt to parse ESXi hosts JSON + ansible.builtin.set_fact: + _parsed_esxi_hosts: "{{ vmware_esxi_hosts | default('[]', true) | from_json }}" + rescue: + - name: Fail gracefully on invalid JSON + ansible.builtin.fail: + msg: >- + Validation Error: The 'esxi_hosts' input provided is not valid JSON. + Please verify the format in your AAP credential or variable input. + - name: MTV VMware Provider ansible.builtin.import_role: name: infra.openshift_virtualization_migration.mtv_management @@ -21,5 +33,5 @@ secretRef: "{{ vmware_credentials_secret_ref | default(omit, true) }}" vddk: image: "{{ vmware_vddk_init_image | default(omit, true) }}" - esxi_hosts: "{{ vmware_esxi_hosts | default('[]', true) | from_json }}" -... + esxi_hosts: "{{ _parsed_esxi_hosts }}" +... \ No newline at end of file diff --git a/roles/mtv_management/tasks/_mtv_provider_vmware.yml b/roles/mtv_management/tasks/_mtv_provider_vmware.yml index 478f0ce..b34a729 100644 --- a/roles/mtv_management/tasks/_mtv_provider_vmware.yml +++ b/roles/mtv_management/tasks/_mtv_provider_vmware.yml @@ -86,6 +86,22 @@ - mtv_management_r_vmware_provider.result is defined - mtv_management_r_vmware_provider.result | length > 0 +- name: _mtv_provider_vmware | Validate ESXi host properties + ansible.builtin.assert: + that: + - "'id' in vmware_esxi" + - "'ip' in vmware_esxi" + - "'user' in vmware_esxi" + - "'password' in vmware_esxi" + quiet: true + fail_msg: "ESXi host entry '{{ vmware_esxi.id | default('UNKNOWN') }}' is missing required fields (id, ip, user, password)" + loop: "{{ mtv_management_populated_vmware_target.esxi_hosts | default([]) }}" + loop_control: + loop_var: vmware_esxi + when: + - mtv_management_populated_vmware_target.esxi_hosts is defined + - mtv_management_populated_vmware_target.esxi_hosts | length > 0 + - name: _mtv_provider_vmware | Create VMware Host credentials secret redhat.openshift.k8s: state: present @@ -93,7 +109,7 @@ apiVersion: v1 kind: Secret type: Opaque - metadata: + metadata: name: "{{ mtv_management_populated_vmware_target['name'] }}-{{ vmware_esxi.id }}-secret" namespace: "{{ mtv_management_provider_namespace }}" data: @@ -106,7 +122,7 @@ loop: "{{ mtv_management_populated_vmware_target.esxi_hosts | default([]) }}" loop_control: loop_var: vmware_esxi - when: + when: - mtv_management_populated_vmware_target.esxi_hosts is defined - mtv_management_populated_vmware_target.esxi_hosts | length > 0