Several changes to include SSHing as the proper user with the right key

This commit is contained in:
2025-08-07 13:36:33 -05:00
parent e07d000435
commit 0b0ae30967
4 changed files with 177 additions and 124 deletions

9
inventory/hosts Normal file
View File

@@ -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'

View File

@@ -97,6 +97,7 @@ class LinodeInventory:
linode_tags = instance.get('tags', []) linode_tags = instance.get('tags', [])
inventory['_meta']['hostvars'][hostname] = { inventory['_meta']['hostvars'][hostname] = {
'ansible_host': primary_ip, 'ansible_host': primary_ip,
'ansible_user': 'phlux', # Set default SSH user
'linode_id': instance['id'], 'linode_id': instance['id'],
'linode_label': instance['label'], 'linode_label': instance['label'],
'linode_region': instance['region'], 'linode_region': instance['region'],

View File

@@ -1,139 +1,182 @@
--- ---
# Main tasks for linode_inventory role - name: Update Linode Dynamic Inventory
hosts: localhost
gather_facts: true
connection: local
- name: Set API token (AWX credential injection takes precedence) vars:
ansible.builtin.set_fact: # Override these variables as needed
linode_api_token: "{{ linode_api_token | default(lookup('env', 'LINODE_API_TOKEN')) | default('') }}" linode_inventory_output_dir: "/tmp/linode_inventory"
inventory_format: "json" # or "ini"
awx_integration: true
cleanup_temp_files: false
- name: Validate required variables # Optional filters
ansible.builtin.assert: include_only_running: false
that: specific_regions: [] # e.g., ['us-east', 'us-west']
- linode_api_token is defined specific_tags: [] # e.g., ['production', 'web']
- 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
- name: Ensure output directory exists pre_tasks:
ansible.builtin.file: - name: Check for Linode API token (will be injected by AWX credential)
path: "{{ linode_inventory_output_dir }}" ansible.builtin.fail:
state: directory msg: "Linode API Token credential must be attached to this job template in AWX"
mode: '0755' when: linode_api_token is undefined or linode_api_token == ""
delegate_to: localhost
- name: Copy Linode inventory script - name: Display configuration
ansible.builtin.copy: ansible.builtin.debug:
src: linode_inventory.py msg: |
dest: "{{ linode_inventory_output_dir }}/linode_inventory.py" Linode Inventory Configuration:
mode: '0755' Output directory: {{ linode_inventory_output_dir }}
delegate_to: localhost Output format: {{ inventory_format }}
AWX integration: {{ awx_integration }}
Include only running: {{ include_only_running }}
- name: Execute Linode inventory script roles:
ansible.builtin.command: - role: linode_inventory
cmd: python3 {{ linode_inventory_output_dir }}/linode_inventory.py --list vars:
environment: linode_api_token: "{{ linode_api_token }}"
LINODE_API_TOKEN: "{{ linode_api_token }}"
register: linode_inventory_result
delegate_to: localhost
changed_when: true
- name: Show script execution details post_tasks:
ansible.builtin.debug: - name: Display next steps
msg: | ansible.builtin.debug:
Script execution results: msg: |
Return code: {{ linode_inventory_result.rc }} Inventory update complete!
Stdout length: {{ linode_inventory_result.stdout | length }}
Stderr length: {{ linode_inventory_result.stderr | length }}
- name: Show stderr if present Next steps for AWX integration:
ansible.builtin.debug: 1. Copy the inventory script to your SCM repository
msg: "Script stderr: {{ linode_inventory_result.stderr }}" 2. Create a custom inventory source in AWX
when: linode_inventory_result.stderr | length > 0 3. Point it to the linode_inventory.py script
4. Set up the Linode API credential
- name: Show stdout if present Files created:
ansible.builtin.debug: - JSON inventory: {{ linode_inventory_output_dir }}/{{ linode_inventory_output_file }}
msg: "Script stdout: {{ linode_inventory_result.stdout }}" {% if inventory_format == "ini" %}
when: linode_inventory_result.stdout | length > 0 - INI inventory: {{ linode_inventory_output_dir }}/linode_static_inventory.ini
{% endif %}
- name: Test API token directly # Optional: Run against discovered Linode hosts
ansible.builtin.uri: - name: Debug and use discovered Linode hosts
url: "https://api.linode.com/v4/linode/instances" hosts: localhost
method: GET gather_facts: false
headers: tasks:
Authorization: "Bearer {{ linode_api_token }}" - name: Check if inventory file exists
Content-Type: "application/json" ansible.builtin.stat:
return_content: yes path: "{{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }}"
status_code: [200, 401, 403] register: inventory_file_stat
register: direct_api_test
delegate_to: localhost
- name: Display direct API test results - name: Display inventory file status
ansible.builtin.debug: ansible.builtin.debug:
msg: | msg: |
Direct API test results: Inventory file path: {{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }}
Status: {{ direct_api_test.status }} File exists: {{ inventory_file_stat.stat.exists }}
Response: {{ direct_api_test.json | default('No JSON response') }} File size: {{ inventory_file_stat.stat.size | default(0) }} bytes
- name: Parse inventory JSON (only if stdout exists) - name: Load and display inventory contents
ansible.builtin.set_fact: ansible.builtin.slurp:
linode_inventory_data: "{{ linode_inventory_result.stdout | from_json }}" src: "{{ linode_inventory_output_dir | default('/tmp/linode_inventory') }}/{{ linode_inventory_output_file | default('linode_inventory.json') }}"
when: register: inventory_content
- linode_inventory_result.stdout | length > 0 when: inventory_file_stat.stat.exists
- linode_inventory_result.rc == 0
- name: Set empty inventory if script failed - name: Parse inventory JSON
ansible.builtin.set_fact: ansible.builtin.set_fact:
linode_inventory_data: dynamic_inventory: "{{ inventory_content.content | b64decode | from_json }}"
_meta: when: inventory_file_stat.stat.exists
hostvars: {}
all:
children: ['ungrouped']
ungrouped:
hosts: []
when: linode_inventory_data is not defined
- name: Save inventory to file - name: Display parsed inventory summary
ansible.builtin.copy: ansible.builtin.debug:
content: "{{ linode_inventory_data | to_nice_json }}" msg: |
dest: "{{ temp_inventory_path }}" Inventory loaded successfully!
mode: '0644' Total hostvars: {{ dynamic_inventory._meta.hostvars | length }}
delegate_to: localhost Groups: {{ dynamic_inventory.keys() | reject('equalto', '_meta') | list }}
Hosts in hostvars: {{ dynamic_inventory._meta.hostvars.keys() | list }}
when: dynamic_inventory is defined
- name: Display inventory summary - name: Add discovered hosts to in-memory inventory
ansible.builtin.debug: ansible.builtin.add_host:
msg: | name: "{{ item.key }}"
Linode Dynamic Inventory Summary: groups: discovered_linodes
Total hosts discovered: {{ linode_inventory_data._meta.hostvars | length }} ansible_host: "{{ item.value.ansible_host }}"
Groups created: {{ linode_inventory_data.keys() | reject('equalto', '_meta') | list | length }} linode_id: "{{ item.value.linode_id }}"
Inventory saved to: {{ temp_inventory_path }} linode_region: "{{ item.value.linode_region }}"
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' }} 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: Show raw script output for debugging - name: Display added hosts with tag information
ansible.builtin.debug: ansible.builtin.debug:
var: linode_inventory_result.stdout msg: |
when: linode_inventory_result.stdout | length > 0 Added {{ groups['discovered_linodes'] | default([]) | length }} running Linode hosts to inventory
- name: Show discovered hosts Host details:
ansible.builtin.debug: {% for host in groups['discovered_linodes'] | default([]) %}
msg: "Host: {{ item.key }} ({{ item.value.ansible_host }}) - Region: {{ item.value.linode_region }} - Status: {{ item.value.linode_status }}" - {{ host }} ({{ hostvars[host]['ansible_host'] }})
loop: "{{ linode_inventory_data._meta.hostvars | dict2items }}" Tags: {{ hostvars[host]['linode_tags'] | join(', ') }}
loop_control: K3s: {{ hostvars[host]['is_k3s'] }}
label: "{{ item.key }}" Control Plane: {{ hostvars[host]['is_control_plane'] }}
when: linode_inventory_data._meta.hostvars | length > 0 Worker: {{ hostvars[host]['is_worker_node'] }}
{% endfor %}
- name: Create static inventory file (optional) - name: Test connection to discovered Linode hosts
ansible.builtin.template: hosts: discovered_linodes
src: inventory.ini.j2 gather_facts: false
dest: "{{ linode_inventory_output_dir }}/linode_static_inventory.ini" vars:
mode: '0644' ansible_user: phlux
when: inventory_format == "ini" ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o ConnectTimeout=10'
delegate_to: localhost tasks:
- name: Test connectivity
ansible.builtin.ping:
register: ping_result
ignore_errors: true
- name: Clean up temporary script - name: Display connectivity status with tag info
ansible.builtin.file: ansible.builtin.debug:
path: "{{ linode_inventory_output_dir }}/linode_inventory.py" msg: |
state: absent {{ inventory_hostname }} ({{ ansible_host }}): {{ 'REACHABLE' if ping_result is succeeded else 'UNREACHABLE' }}
delegate_to: localhost Tags: {{ linode_tags | join(', ') }}
when: cleanup_temp_files | default(true) Role: {{ 'Control Plane' if is_control_plane else 'Worker Node' if is_worker_node else 'Other' }}
# 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
# 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
# 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

View File

@@ -18,5 +18,5 @@
{% endfor %} {% endfor %}
[all:vars] [all:vars]
ansible_user=root ansible_user=phlux
ansible_ssh_common_args='-o StrictHostKeyChecking=no' ansible_ssh_common_args='-o StrictHostKeyChecking=no'