From 0b0ae30967a168957364c836966d853cadf9f5fa Mon Sep 17 00:00:00 2001 From: Kevin Thompson Date: Thu, 7 Aug 2025 13:36:33 -0500 Subject: [PATCH] Several changes to include SSHing as the proper user with the right key --- inventory/hosts | 9 + .../linode/files/linode_inventory.py | 1 + roles/inventory/linode/tasks/main.yml | 289 ++++++++++-------- .../linode/templates/inventory.ini.j2 | 2 +- 4 files changed, 177 insertions(+), 124 deletions(-) create mode 100644 inventory/hosts diff --git a/inventory/hosts b/inventory/hosts new file mode 100644 index 0000000..58242c0 --- /dev/null +++ b/inventory/hosts @@ -0,0 +1,9 @@ +[local] +localhost ansible_connection=local + +[linode:children] +# Dynamic groups will be populated by the inventory script + +[all:vars] +ansible_user=phlux +ansible_ssh_common_args='-o StrictHostKeyChecking=no' diff --git a/roles/inventory/linode/files/linode_inventory.py b/roles/inventory/linode/files/linode_inventory.py index 961679a..a924cf5 100644 --- a/roles/inventory/linode/files/linode_inventory.py +++ b/roles/inventory/linode/files/linode_inventory.py @@ -97,6 +97,7 @@ class LinodeInventory: linode_tags = instance.get('tags', []) inventory['_meta']['hostvars'][hostname] = { 'ansible_host': primary_ip, + 'ansible_user': 'phlux', # Set default SSH user 'linode_id': instance['id'], 'linode_label': instance['label'], 'linode_region': instance['region'], diff --git a/roles/inventory/linode/tasks/main.yml b/roles/inventory/linode/tasks/main.yml index e1e84b3..f8c5898 100644 --- a/roles/inventory/linode/tasks/main.yml +++ b/roles/inventory/linode/tasks/main.yml @@ -1,139 +1,182 @@ --- -# Main tasks for linode_inventory role +- name: Update Linode Dynamic Inventory + hosts: localhost + gather_facts: true + connection: local + + vars: + # Override these variables as needed + linode_inventory_output_dir: "/tmp/linode_inventory" + inventory_format: "json" # or "ini" + awx_integration: true + cleanup_temp_files: false + + # Optional filters + include_only_running: false + specific_regions: [] # e.g., ['us-east', 'us-west'] + specific_tags: [] # e.g., ['production', 'web'] + + pre_tasks: + - name: Check for Linode API token (will be injected by AWX credential) + ansible.builtin.fail: + msg: "Linode API Token credential must be attached to this job template in AWX" + when: linode_api_token is undefined or linode_api_token == "" -- name: Set API token (AWX credential injection takes precedence) - ansible.builtin.set_fact: - linode_api_token: "{{ linode_api_token | default(lookup('env', 'LINODE_API_TOKEN')) | default('') }}" + - name: Display configuration + ansible.builtin.debug: + msg: | + Linode Inventory Configuration: + Output directory: {{ linode_inventory_output_dir }} + Output format: {{ inventory_format }} + AWX integration: {{ awx_integration }} + Include only running: {{ include_only_running }} -- name: Validate required variables - ansible.builtin.assert: - that: - - linode_api_token is defined - - linode_api_token | length > 0 - fail_msg: | - Linode API token not found. - For AWX: Attach a Linode API Token credential to your job template - For local: Set LINODE_API_TOKEN environment variable or pass linode_api_token variable - quiet: true + roles: + - role: linode_inventory + vars: + linode_api_token: "{{ linode_api_token }}" -- name: Ensure output directory exists - ansible.builtin.file: - path: "{{ linode_inventory_output_dir }}" - state: directory - mode: '0755' - delegate_to: localhost + post_tasks: + - name: Display next steps + ansible.builtin.debug: + msg: | + Inventory update complete! + + Next steps for AWX integration: + 1. Copy the inventory script to your SCM repository + 2. Create a custom inventory source in AWX + 3. Point it to the linode_inventory.py script + 4. Set up the Linode API credential + + Files created: + - JSON inventory: {{ linode_inventory_output_dir }}/{{ linode_inventory_output_file }} + {% if inventory_format == "ini" %} + - INI inventory: {{ linode_inventory_output_dir }}/linode_static_inventory.ini + {% endif %} -- name: Copy Linode inventory script - ansible.builtin.copy: - src: linode_inventory.py - dest: "{{ linode_inventory_output_dir }}/linode_inventory.py" - mode: '0755' - delegate_to: localhost +# Optional: Run against discovered Linode hosts +- name: Debug and use discovered Linode hosts + hosts: localhost + gather_facts: false + tasks: + - name: Check if inventory file exists + ansible.builtin.stat: + path: "{{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }}" + register: inventory_file_stat -- name: Execute Linode inventory script - ansible.builtin.command: - cmd: python3 {{ linode_inventory_output_dir }}/linode_inventory.py --list - environment: - LINODE_API_TOKEN: "{{ linode_api_token }}" - register: linode_inventory_result - delegate_to: localhost - changed_when: true + - name: Display inventory file status + ansible.builtin.debug: + msg: | + Inventory file path: {{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }} + File exists: {{ inventory_file_stat.stat.exists }} + File size: {{ inventory_file_stat.stat.size | default(0) }} bytes -- name: Show script execution details - ansible.builtin.debug: - msg: | - Script execution results: - Return code: {{ linode_inventory_result.rc }} - Stdout length: {{ linode_inventory_result.stdout | length }} - Stderr length: {{ linode_inventory_result.stderr | length }} - -- name: Show stderr if present - ansible.builtin.debug: - msg: "Script stderr: {{ linode_inventory_result.stderr }}" - when: linode_inventory_result.stderr | length > 0 + - name: Load and display inventory contents + ansible.builtin.slurp: + src: "{{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }}" + register: inventory_content + when: inventory_file_stat.stat.exists -- name: Show stdout if present - ansible.builtin.debug: - msg: "Script stdout: {{ linode_inventory_result.stdout }}" - when: linode_inventory_result.stdout | length > 0 + - name: Parse inventory JSON + ansible.builtin.set_fact: + dynamic_inventory: "{{ inventory_content.content | b64decode | from_json }}" + when: inventory_file_stat.stat.exists -- name: Test API token directly - ansible.builtin.uri: - url: "https://api.linode.com/v4/linode/instances" - method: GET - headers: - Authorization: "Bearer {{ linode_api_token }}" - Content-Type: "application/json" - return_content: yes - status_code: [200, 401, 403] - register: direct_api_test - delegate_to: localhost + - name: Display parsed inventory summary + ansible.builtin.debug: + msg: | + Inventory loaded successfully! + Total hostvars: {{ dynamic_inventory._meta.hostvars | length }} + Groups: {{ dynamic_inventory.keys() | reject('equalto', '_meta') | list }} + Hosts in hostvars: {{ dynamic_inventory._meta.hostvars.keys() | list }} + when: dynamic_inventory is defined -- name: Display direct API test results - ansible.builtin.debug: - msg: | - Direct API test results: - Status: {{ direct_api_test.status }} - Response: {{ direct_api_test.json | default('No JSON response') }} - -- name: Parse inventory JSON (only if stdout exists) - ansible.builtin.set_fact: - linode_inventory_data: "{{ linode_inventory_result.stdout | from_json }}" - when: - - linode_inventory_result.stdout | length > 0 - - linode_inventory_result.rc == 0 + - name: Add discovered hosts to in-memory inventory + ansible.builtin.add_host: + name: "{{ item.key }}" + groups: discovered_linodes + ansible_host: "{{ item.value.ansible_host }}" + linode_id: "{{ item.value.linode_id }}" + linode_region: "{{ item.value.linode_region }}" + linode_type: "{{ item.value.linode_type }}" + linode_status: "{{ item.value.linode_status }}" + linode_tags: "{{ item.value.linode_tags }}" + is_debian: "{{ item.value.is_debian }}" + is_ubuntu: "{{ item.value.is_ubuntu }}" + is_k3s: "{{ item.value.is_k3s }}" + is_control_plane: "{{ item.value.is_control_plane }}" + is_worker_node: "{{ item.value.is_worker_node }}" + tag_string: "{{ item.value.tag_string }}" + loop: "{{ dynamic_inventory._meta.hostvars | dict2items }}" + when: + - dynamic_inventory is defined + - item.value.linode_status == "running" -- name: Set empty inventory if script failed - ansible.builtin.set_fact: - linode_inventory_data: - _meta: - hostvars: {} - all: - children: ['ungrouped'] - ungrouped: - hosts: [] - when: linode_inventory_data is not defined + - name: Display added hosts with tag information + ansible.builtin.debug: + msg: | + Added {{ groups['discovered_linodes'] | default([]) | length }} running Linode hosts to inventory + + Host details: + {% for host in groups['discovered_linodes'] | default([]) %} + - {{ host }} ({{ hostvars[host]['ansible_host'] }}) + Tags: {{ hostvars[host]['linode_tags'] | join(', ') }} + K3s: {{ hostvars[host]['is_k3s'] }} + Control Plane: {{ hostvars[host]['is_control_plane'] }} + Worker: {{ hostvars[host]['is_worker_node'] }} + {% endfor %} -- name: Save inventory to file - ansible.builtin.copy: - content: "{{ linode_inventory_data | to_nice_json }}" - dest: "{{ temp_inventory_path }}" - mode: '0644' - delegate_to: localhost +- name: Test connection to discovered Linode hosts + hosts: discovered_linodes + gather_facts: false + vars: + ansible_user: phlux + ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o ConnectTimeout=10' + tasks: + - name: Test connectivity + ansible.builtin.ping: + register: ping_result + ignore_errors: true -- name: Display inventory summary - ansible.builtin.debug: - msg: | - Linode Dynamic Inventory Summary: - Total hosts discovered: {{ linode_inventory_data._meta.hostvars | length }} - Groups created: {{ linode_inventory_data.keys() | reject('equalto', '_meta') | list | length }} - Inventory saved to: {{ temp_inventory_path }} - API Token status: {{ 'Set (' + (linode_api_token[:8] + '...' if linode_api_token | length > 8 else linode_api_token) + ')' if linode_api_token is defined else 'NOT SET' }} + - name: Display connectivity status with tag info + ansible.builtin.debug: + msg: | + {{ inventory_hostname }} ({{ ansible_host }}): {{ 'REACHABLE' if ping_result is succeeded else 'UNREACHABLE' }} + Tags: {{ linode_tags | join(', ') }} + Role: {{ 'Control Plane' if is_control_plane else 'Worker Node' if is_worker_node else 'Other' }} -- name: Show raw script output for debugging - ansible.builtin.debug: - var: linode_inventory_result.stdout - when: linode_inventory_result.stdout | length > 0 +# Example: Run tasks only on k3s control plane nodes +- name: Example - Control Plane specific tasks + hosts: discovered_linodes + gather_facts: false + vars: + ansible_user: phlux + tasks: + - name: Control plane specific task + ansible.builtin.debug: + msg: "This would run control plane specific commands on {{ inventory_hostname }}" + when: is_control_plane | bool -- name: Show discovered hosts - ansible.builtin.debug: - msg: "Host: {{ item.key }} ({{ item.value.ansible_host }}) - Region: {{ item.value.linode_region }} - Status: {{ item.value.linode_status }}" - loop: "{{ linode_inventory_data._meta.hostvars | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: linode_inventory_data._meta.hostvars | length > 0 +# Example: Run tasks only on k3s worker nodes +- name: Example - Worker Node specific tasks + hosts: discovered_linodes + gather_facts: false + vars: + ansible_user: phlux + tasks: + - name: Worker node specific task + ansible.builtin.debug: + msg: "This would run worker node specific commands on {{ inventory_hostname }}" + when: is_worker_node | bool -- name: Create static inventory file (optional) - ansible.builtin.template: - src: inventory.ini.j2 - dest: "{{ linode_inventory_output_dir }}/linode_static_inventory.ini" - mode: '0644' - when: inventory_format == "ini" - delegate_to: localhost - -- name: Clean up temporary script - ansible.builtin.file: - path: "{{ linode_inventory_output_dir }}/linode_inventory.py" - state: absent - delegate_to: localhost - when: cleanup_temp_files | default(true) +# Example: Run tasks on all k3s nodes (control plane + workers) +- name: Example - All K3s nodes + hosts: discovered_linodes + gather_facts: false + vars: + ansible_user: phlux + tasks: + - name: K3s cluster task + ansible.builtin.debug: + msg: "This would run on all k3s nodes: {{ inventory_hostname }}" + when: is_k3s | bool diff --git a/roles/inventory/linode/templates/inventory.ini.j2 b/roles/inventory/linode/templates/inventory.ini.j2 index 6ea0695..824c057 100644 --- a/roles/inventory/linode/templates/inventory.ini.j2 +++ b/roles/inventory/linode/templates/inventory.ini.j2 @@ -18,5 +18,5 @@ {% endfor %} [all:vars] -ansible_user=root +ansible_user=phlux ansible_ssh_common_args='-o StrictHostKeyChecking=no'