From e2fbd98f22e287322eb24bccde92e50c1e980494 Mon Sep 17 00:00:00 2001 From: Ricardo Diaz Date: Thu, 29 Jan 2026 11:52:08 +0100 Subject: [PATCH] [edpm_ssh_info] Add role to retrieve EDPM SSH connectivity This role queries OpenShift for OpenStackDataPlaneNodeSet resources, retrieves SSH credentials, and returns node information for external data plane management (SSH connectivity). Assisted-By: Claude Sonnet 4.5 Signed-off-by: Ricardo Diaz --- docs/dictionary/en-custom.txt | 1 + roles/edpm_ssh_info/README.md | 150 ++++++++++++++++++++++++++ roles/edpm_ssh_info/defaults/main.yml | 22 ++++ roles/edpm_ssh_info/meta/main.yml | 4 + roles/edpm_ssh_info/tasks/main.yml | 84 +++++++++++++++ zuul.d/molecule.yaml | 9 ++ zuul.d/projects.yaml | 1 + 7 files changed, 271 insertions(+) create mode 100644 roles/edpm_ssh_info/README.md create mode 100644 roles/edpm_ssh_info/defaults/main.yml create mode 100644 roles/edpm_ssh_info/meta/main.yml create mode 100644 roles/edpm_ssh_info/tasks/main.yml diff --git a/docs/dictionary/en-custom.txt b/docs/dictionary/en-custom.txt index 3371228f0a..73ac68a53c 100644 --- a/docs/dictionary/en-custom.txt +++ b/docs/dictionary/en-custom.txt @@ -370,6 +370,7 @@ networkattachmentdefinition networkconfig networkmanager networktype +networker nfs nftables nhc diff --git a/roles/edpm_ssh_info/README.md b/roles/edpm_ssh_info/README.md new file mode 100644 index 0000000000..4b48b7ef18 --- /dev/null +++ b/roles/edpm_ssh_info/README.md @@ -0,0 +1,150 @@ +# edpm_ssh_info + +This Ansible role retrieves EDPM (External Data Plane Management) SSH connectivity information. + +## Description + +The role performs the following tasks: + +1. Queries OpenShift for OpenStackDataPlaneNodeSet resources +2. Retrieves the dataplane SSH private key from OpenShift secrets (if not already present locally) +3. Extracts compute node names and their control plane IP addresses +4. Returns all information in a structured format for use in subsequent plays + +## Requirements + +- Ansible collection `kubernetes.core` must be installed +- Ansible collection `community.okd` must be installed +- Valid kubeconfig file at `{{ ansible_user_dir }}/.kube/config` +- Access to the OpenShift/Kubernetes namespace containing EDPM resources +- Appropriate permissions to read OpenStackDataPlaneNodeSet resources and secrets + +## Variables + +### Role Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `cifmw_edpm_ssh_info_openstack_namespace` | `openstack` | OpenShift namespace where EDPM resources are deployed | +| `cifmw_edpm_ssh_info_kubeconfig_path` | `{{ ansible_user_dir }}/.kube/config` | Path to kubeconfig file | +| `cifmw_edpm_ssh_info_oc_auth_required` | `true` | Whether to authenticate to OpenShift cluster before querying resources | +| `cifmw_edpm_ssh_info_oc_username` | `kubeadmin` | OpenShift username for authentication | +| `cifmw_edpm_ssh_info_oc_password_file` | `{{ ansible_user_dir }}/.kube/kubeadmin-password` | Path to file containing OpenShift password | +| `cifmw_edpm_ssh_info_oc_api_url` | `https://api.ocp.openstack.lab:6443/` | OpenShift API URL | +| `cifmw_edpm_ssh_info_oc_validate_certs` | `false` | Whether to validate SSL certificates when connecting to OpenShift API | +| `cifmw_edpm_ssh_info_node_prefix` | `compute-` | Node name prefix to filter (e.g., 'compute-', 'networker-') | +| `cifmw_edpm_ssh_info_ssh_secret_name` | `dataplane-ansible-ssh-private-key-secret` | Name of the secret containing SSH private key | +| `cifmw_edpm_ssh_info_ssh_key_path` | `{{ ansible_user_dir }}/.ssh/compute_id` | Destination path for SSH private key | + +## Output + +### Facts Set + +The role sets a fact named `cifmw_edpm_ssh_info` with the following structure: + +```yaml +cifmw_edpm_ssh_info: + ssh_key_path: "/path/to/ssh/key" + nodes: + - name: compute-0 + host: 192.168.122.100 + - name: compute-1 + host: 192.168.122.101 +``` + +**Fields:** +- `ssh_key_path` (string): Path to the SSH private key file +- `nodes` (list): List of discovered dataplane nodes + - `name` (string): Node name (e.g., "compute-0") + - `host` (string): Control plane IP address + +## Usage + +### Basic Usage (with defaults) + +```yaml +- name: Get EDPM SSH information + hosts: localhost + gather_facts: false + tasks: + - name: Retrieve dataplane info + ansible.builtin.include_role: + name: edpm_ssh_info + + - name: Display discovered nodes + ansible.builtin.debug: + msg: "Found {{ cifmw_edpm_ssh_info.nodes | length }} nodes" +``` + +### Custom Configuration + +```yaml +- name: Get EDPM SSH information + hosts: localhost + gather_facts: false + tasks: + - name: Retrieve dataplane info + ansible.builtin.include_role: + name: edpm_ssh_info + vars: + cifmw_edpm_ssh_info_openstack_namespace: my-openstack + cifmw_edpm_ssh_info_node_prefix: networker- + cifmw_edpm_ssh_info_ssh_key_path: /custom/path/to/key +``` + +### Custom Password File + +```yaml +- name: Get EDPM SSH information with custom password file + hosts: localhost + gather_facts: false + tasks: + - name: Retrieve dataplane info + ansible.builtin.include_role: + name: edpm_ssh_info + vars: + cifmw_edpm_ssh_info_oc_password_file: /custom/path/to/password-file +``` + +### Using with Dynamic Inventory + +```yaml +- name: Get EDPM SSH information + hosts: localhost + gather_facts: false + tasks: + - name: Retrieve dataplane info + ansible.builtin.include_role: + name: edpm_ssh_info + +- name: Add nodes to inventory + hosts: localhost + gather_facts: false + tasks: + - name: Add compute nodes to dynamic inventory + ansible.builtin.add_host: + name: "{{ item.name }}" + ansible_host: "{{ item.host }}" + ansible_ssh_private_key_file: "{{ cifmw_edpm_ssh_info.ssh_key_path }}" + groups: + - compute_nodes + loop: "{{ cifmw_edpm_ssh_info.nodes }}" + +- name: Configure compute nodes + hosts: compute_nodes + tasks: + - name: Run configuration tasks + ansible.builtin.debug: + msg: "Configuring {{ inventory_hostname }}" +``` + +## Notes + +- If your kubeconfig is already authenticated, set `cifmw_edpm_ssh_info_oc_auth_required: false` to skip the `oc login` step +- The OpenShift password is read from the file specified in `cifmw_edpm_ssh_info_oc_password_file` (defaults to `~/.kube/kubeadmin-password`) +- You can set a custom password file path via `cifmw_edpm_ssh_info_oc_password_file` if your password is stored elsewhere +- The role will fail if no OpenStackDataPlaneNodeSet resources are found +- SSH key retrieval is skipped if the key file already exists at the destination path +- SSH key is saved with `0600` permissions for security +- The role is idempotent - it can be run multiple times safely +- Only nodes matching the specified prefix and having a control plane IP are included diff --git a/roles/edpm_ssh_info/defaults/main.yml b/roles/edpm_ssh_info/defaults/main.yml new file mode 100644 index 0000000000..560d66ea33 --- /dev/null +++ b/roles/edpm_ssh_info/defaults/main.yml @@ -0,0 +1,22 @@ +--- +# OpenShift/Kubernetes namespace for EDPM resources +cifmw_edpm_ssh_info_openstack_namespace: openstack + +# Kubeconfig file path +cifmw_edpm_ssh_info_kubeconfig_path: "{{ ansible_user_dir }}/.kube/config" + +# OpenShift authentication required +cifmw_edpm_ssh_info_oc_auth_required: true + +# OpenShift login credentials +cifmw_edpm_ssh_info_oc_username: kubeadmin +cifmw_edpm_ssh_info_oc_password_file: "{{ ansible_user_dir }}/.kube/kubeadmin-password" +cifmw_edpm_ssh_info_oc_api_url: https://api.ocp.openstack.lab:6443/ +cifmw_edpm_ssh_info_oc_validate_certs: false + +# Node name prefix to filter (e.g., 'compute-', 'networker-') +cifmw_edpm_ssh_info_node_prefix: compute- + +# SSH private key retrieval settings +cifmw_edpm_ssh_info_ssh_secret_name: dataplane-ansible-ssh-private-key-secret +cifmw_edpm_ssh_info_ssh_key_path: "{{ ansible_user_dir }}/.ssh/compute_id" diff --git a/roles/edpm_ssh_info/meta/main.yml b/roles/edpm_ssh_info/meta/main.yml new file mode 100644 index 0000000000..5e4e6fa567 --- /dev/null +++ b/roles/edpm_ssh_info/meta/main.yml @@ -0,0 +1,4 @@ +--- +collections: + - kubernetes.core + - community.okd diff --git a/roles/edpm_ssh_info/tasks/main.yml b/roles/edpm_ssh_info/tasks/main.yml new file mode 100644 index 0000000000..39ad9a2984 --- /dev/null +++ b/roles/edpm_ssh_info/tasks/main.yml @@ -0,0 +1,84 @@ +--- +- name: Read OpenShift password from file + ansible.builtin.slurp: + src: "{{ cifmw_edpm_ssh_info_oc_password_file }}" + register: _oc_password_content + no_log: "{{ cifmw_nolog | default(true) | bool }}" + when: cifmw_edpm_ssh_info_oc_auth_required + +- name: Authenticate to OpenShift cluster + community.okd.openshift_auth: + host: "{{ cifmw_edpm_ssh_info_oc_api_url }}" + username: "{{ cifmw_edpm_ssh_info_oc_username }}" + password: "{{ _oc_password_content.content | b64decode | trim }}" + validate_certs: "{{ cifmw_edpm_ssh_info_oc_validate_certs }}" + register: _openshift_auth_result + no_log: "{{ cifmw_nolog | default(true) | bool }}" + when: cifmw_edpm_ssh_info_oc_auth_required + +- name: Get OpenShift DataPlane NodeSets + kubernetes.core.k8s_info: + api_version: dataplane.openstack.org/v1beta1 + kind: OpenStackDataPlaneNodeSet + namespace: "{{ cifmw_edpm_ssh_info_openstack_namespace }}" + host: "{{ cifmw_edpm_ssh_info_oc_api_url if cifmw_edpm_ssh_info_oc_auth_required else omit }}" + api_key: "{{ _openshift_auth_result.openshift_auth.api_key if cifmw_edpm_ssh_info_oc_auth_required else omit }}" + kubeconfig: "{{ cifmw_edpm_ssh_info_kubeconfig_path if not cifmw_edpm_ssh_info_oc_auth_required else omit }}" + validate_certs: "{{ cifmw_edpm_ssh_info_oc_validate_certs }}" + register: osdpns_info + failed_when: osdpns_info.resources | length == 0 + +- name: Check if SSH private key already exists + ansible.builtin.stat: + path: "{{ cifmw_edpm_ssh_info_ssh_key_path }}" + register: ssh_key_stat + +- name: Retrieve SSH private key from OpenShift + when: not ssh_key_stat.stat.exists + block: + - name: Get dataplane SSH secret from OpenShift + kubernetes.core.k8s_info: + api_version: v1 + kind: Secret + name: "{{ cifmw_edpm_ssh_info_ssh_secret_name }}" + namespace: "{{ cifmw_edpm_ssh_info_openstack_namespace }}" + host: "{{ cifmw_edpm_ssh_info_oc_api_url if cifmw_edpm_ssh_info_oc_auth_required else omit }}" + api_key: "{{ _openshift_auth_result.openshift_auth.api_key if cifmw_edpm_ssh_info_oc_auth_required else omit }}" + kubeconfig: "{{ cifmw_edpm_ssh_info_kubeconfig_path if not cifmw_edpm_ssh_info_oc_auth_required else omit }}" + validate_certs: "{{ cifmw_edpm_ssh_info_oc_validate_certs }}" + register: secret_info + failed_when: secret_info.resources | length == 0 + + - name: Ensure SSH directory exists + ansible.builtin.file: + path: "{{ cifmw_edpm_ssh_info_ssh_key_path | dirname }}" + state: directory + mode: '0700' + + - name: Save SSH private key + ansible.builtin.copy: + content: "{{ secret_info.resources[0].data['ssh-privatekey'] | b64decode }}" + dest: "{{ cifmw_edpm_ssh_info_ssh_key_path }}" + mode: '0600' + no_log: "{{ cifmw_nolog | default(true) | bool }}" + +- name: Build dataplane information with nodes and SSH key + ansible.builtin.set_fact: + cifmw_edpm_ssh_info: + ssh_key_path: "{{ cifmw_edpm_ssh_info_ssh_key_path }}" + nodes: >- + {%- set nodes = [] -%} + {%- for nodeset in osdpns_info.resources -%} + {%- if nodeset.status.allHostnames is defined and nodeset.status.allIPs is defined -%} + {%- for fqdn in nodeset.status.allHostnames.keys() -%} + {%- set node_name = fqdn.split('.')[0] -%} + {%- if node_name.startswith(cifmw_edpm_ssh_info_node_prefix) and fqdn in nodeset.status.allIPs -%} + {%- set ctlplane_ip = nodeset.status.allIPs[fqdn].ctlplane | default(None) -%} + {%- if ctlplane_ip -%} + {%- set _ = nodes.append({'name': node_name, 'host': ctlplane_ip}) -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + {%- endfor -%} + {{ nodes }} diff --git a/zuul.d/molecule.yaml b/zuul.d/molecule.yaml index 31db44ea9f..c081175a25 100644 --- a/zuul.d/molecule.yaml +++ b/zuul.d/molecule.yaml @@ -927,6 +927,15 @@ - ^.config/molecule/.* name: cifmw-molecule-cleanup_openstack parent: cifmw-molecule-noop +- job: + files: + - ^common-requirements.txt + - ^test-requirements.txt + - ^roles/edpm_ssh_info/.* + - ^ci/playbooks/molecule.* + - ^.config/molecule/.* + name: cifmw-molecule-edpm_ssh_info + parent: cifmw-molecule-noop - job: files: - ^common-requirements.txt diff --git a/zuul.d/projects.yaml b/zuul.d/projects.yaml index b51fad17f0..40eb9ba96e 100644 --- a/zuul.d/projects.yaml +++ b/zuul.d/projects.yaml @@ -52,6 +52,7 @@ - cifmw-molecule-edpm_deploy_baremetal - cifmw-molecule-edpm_kustomize - cifmw-molecule-edpm_prepare + - cifmw-molecule-edpm_ssh_info - cifmw-molecule-env_op_images - cifmw-molecule-fdp_update_container_images - cifmw-molecule-fdp_update_edpm