Rewrite the linode inventory stuff...this is a pain in the ass.
This commit is contained in:
@@ -1,26 +1,17 @@
|
||||
---
|
||||
# Default variables for linode_inventory role
|
||||
|
||||
# Linode API settings
|
||||
linode_api_url: "https://api.linode.com/v4"
|
||||
linode_inventory_output_dir: "/tmp"
|
||||
linode_inventory_output_file: "linode_inventory.json"
|
||||
|
||||
# Inventory grouping options
|
||||
create_region_groups: true
|
||||
create_type_groups: true
|
||||
create_status_groups: true
|
||||
create_tag_groups: true
|
||||
|
||||
# Default groups to create
|
||||
default_groups:
|
||||
- all
|
||||
- ungrouped
|
||||
|
||||
# Filter options
|
||||
include_only_running: false
|
||||
specific_regions: []
|
||||
specific_tags: []
|
||||
include_only_running: true
|
||||
specific_regions: [] # e.g., ['us-east', 'us-west']
|
||||
specific_tags: [] # e.g., ['production', 'web']
|
||||
|
||||
# Output format
|
||||
inventory_format: "json" # json or ini
|
||||
# Default SSH settings for discovered hosts
|
||||
default_ansible_user: "phlux"
|
||||
default_ssh_args: "-o StrictHostKeyChecking=no"
|
||||
|
||||
# Group naming options
|
||||
group_prefix_region: "region_"
|
||||
group_prefix_type: "type_"
|
||||
group_prefix_tag: "tag_"
|
||||
group_prefix_status: "status_"
|
||||
|
@@ -1,208 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from argparse import ArgumentParser
|
||||
|
||||
class LinodeInventory:
|
||||
def __init__(self):
|
||||
self.api_token = os.environ.get('LINODE_API_TOKEN')
|
||||
if not self.api_token:
|
||||
raise ValueError("LINODE_API_TOKEN environment variable is required")
|
||||
|
||||
self.base_url = "https://api.linode.com/v4"
|
||||
|
||||
def make_request(self, endpoint):
|
||||
"""Make HTTP request to Linode API using urllib"""
|
||||
url = f"{self.base_url}{endpoint}"
|
||||
req = urllib.request.Request(url)
|
||||
req.add_header('Authorization', f'Bearer {self.api_token}')
|
||||
req.add_header('Content-Type', 'application/json')
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as response:
|
||||
data = response.read().decode('utf-8')
|
||||
return json.loads(data)
|
||||
except urllib.error.HTTPError as e:
|
||||
error_msg = e.read().decode('utf-8') if e.fp else str(e)
|
||||
raise Exception(f"HTTP Error {e.code}: {error_msg}")
|
||||
except urllib.error.URLError as e:
|
||||
raise Exception(f"URL Error: {e}")
|
||||
except Exception as e:
|
||||
raise Exception(f"Request failed: {e}")
|
||||
|
||||
def get_instances(self):
|
||||
"""Fetch all Linode instances"""
|
||||
try:
|
||||
# Get first page to check pagination
|
||||
response = self.make_request("/linode/instances?page=1")
|
||||
instances = response['data']
|
||||
|
||||
# Handle pagination if there are more pages
|
||||
if response.get('pages', 1) > 1:
|
||||
for page in range(2, response['pages'] + 1):
|
||||
page_response = self.make_request(f"/linode/instances?page={page}")
|
||||
instances.extend(page_response['data'])
|
||||
|
||||
return instances
|
||||
except Exception as e:
|
||||
print(f"Error fetching instances: {e}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
def generate_inventory(self):
|
||||
"""Generate Ansible inventory from Linode instances"""
|
||||
inventory = {
|
||||
'_meta': {
|
||||
'hostvars': {}
|
||||
},
|
||||
'all': {
|
||||
'children': ['ungrouped']
|
||||
},
|
||||
'ungrouped': {
|
||||
'hosts': []
|
||||
}
|
||||
}
|
||||
|
||||
# Group definitions
|
||||
regions = {}
|
||||
types = {}
|
||||
statuses = {}
|
||||
|
||||
try:
|
||||
instances = self.get_instances()
|
||||
|
||||
if not instances:
|
||||
print("No instances found or API request failed", file=sys.stderr)
|
||||
return inventory
|
||||
|
||||
for instance in instances:
|
||||
# Use Linode label as hostname
|
||||
hostname = instance['label']
|
||||
|
||||
# Get primary IPv4 address
|
||||
ipv4_addresses = instance.get('ipv4', [])
|
||||
primary_ip = ipv4_addresses[0] if ipv4_addresses else None
|
||||
|
||||
if not primary_ip:
|
||||
print(f"Warning: No IPv4 address found for {hostname}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
# Add to ungrouped hosts
|
||||
inventory['ungrouped']['hosts'].append(hostname)
|
||||
|
||||
# Host variables
|
||||
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'],
|
||||
'linode_type': instance['type'],
|
||||
'linode_status': instance['status'],
|
||||
'linode_ipv4': instance.get('ipv4', []),
|
||||
'linode_ipv6': instance.get('ipv6'),
|
||||
'linode_tags': linode_tags,
|
||||
'linode_specs': instance.get('specs', {}),
|
||||
'linode_hypervisor': instance.get('hypervisor'),
|
||||
'linode_created': instance.get('created'),
|
||||
'linode_updated': instance.get('updated'),
|
||||
'linode_group': instance.get('group', ''),
|
||||
'linode_image': instance.get('image', {}).get('id', '') if isinstance(instance.get('image'), dict) else '',
|
||||
'linode_backups': instance.get('backups', {}).get('enabled', False) if isinstance(instance.get('backups'), dict) else False,
|
||||
# Add individual tag variables for easy access
|
||||
'is_debian': 'Debian' in linode_tags,
|
||||
'is_ubuntu': 'Ubuntu' in linode_tags,
|
||||
'is_k3s': 'k3s' in linode_tags,
|
||||
'is_control_plane': 'control-plane' in linode_tags,
|
||||
'is_worker_node': 'worker-node' in linode_tags,
|
||||
# Tag string for easy filtering
|
||||
'tag_string': ','.join(linode_tags).lower(),
|
||||
}
|
||||
|
||||
# Group by region
|
||||
region_group = f"region_{instance['region'].replace('-', '_')}"
|
||||
if region_group not in regions:
|
||||
regions[region_group] = {'hosts': []}
|
||||
regions[region_group]['hosts'].append(hostname)
|
||||
|
||||
# Group by instance type
|
||||
type_group = f"type_{instance['type'].replace('-', '_').replace('.', '_')}"
|
||||
if type_group not in types:
|
||||
types[type_group] = {'hosts': []}
|
||||
types[type_group]['hosts'].append(hostname)
|
||||
|
||||
# Group by status
|
||||
status_group = f"status_{instance['status']}"
|
||||
if status_group not in statuses:
|
||||
statuses[status_group] = {'hosts': []}
|
||||
statuses[status_group]['hosts'].append(hostname)
|
||||
|
||||
# Group by tags
|
||||
for tag in instance.get('tags', []):
|
||||
tag_group = f"tag_{tag.replace('-', '_').replace(' ', '_').replace('.', '_')}"
|
||||
if tag_group not in inventory:
|
||||
inventory[tag_group] = {'hosts': []}
|
||||
inventory[tag_group]['hosts'].append(hostname)
|
||||
|
||||
# Group by Linode group (if set)
|
||||
if instance.get('group'):
|
||||
group_name = f"group_{instance['group'].replace('-', '_').replace(' ', '_')}"
|
||||
if group_name not in inventory:
|
||||
inventory[group_name] = {'hosts': []}
|
||||
inventory[group_name]['hosts'].append(hostname)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error generating inventory: {e}", file=sys.stderr)
|
||||
return inventory
|
||||
|
||||
# Add all groups to inventory
|
||||
inventory.update(regions)
|
||||
inventory.update(types)
|
||||
inventory.update(statuses)
|
||||
|
||||
# Add group children to 'all'
|
||||
all_groups = list(regions.keys()) + list(types.keys()) + list(statuses.keys())
|
||||
tag_groups = [k for k in inventory.keys() if k.startswith('tag_')]
|
||||
group_groups = [k for k in inventory.keys() if k.startswith('group_')]
|
||||
all_groups.extend(tag_groups)
|
||||
all_groups.extend(group_groups)
|
||||
|
||||
if all_groups:
|
||||
inventory['all']['children'].extend(all_groups)
|
||||
|
||||
return inventory
|
||||
|
||||
def get_host_vars(self, hostname):
|
||||
"""Get variables for a specific host"""
|
||||
inventory = self.generate_inventory()
|
||||
return inventory['_meta']['hostvars'].get(hostname, {})
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser(description='Linode Dynamic Inventory')
|
||||
parser.add_argument('--list', action='store_true', help='List all hosts')
|
||||
parser.add_argument('--host', help='Get variables for specific host')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
inventory = LinodeInventory()
|
||||
|
||||
if args.list:
|
||||
result = inventory.generate_inventory()
|
||||
print(json.dumps(result, indent=2))
|
||||
elif args.host:
|
||||
result = inventory.get_host_vars(args.host)
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@@ -1,30 +0,0 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: Kevin M Thompson
|
||||
description: Ansible role for Linode dynamic inventory management
|
||||
company: Ewnix
|
||||
license: MIT
|
||||
min_ansible_version: 2.9
|
||||
platforms:
|
||||
- name: EL
|
||||
versions:
|
||||
- 7
|
||||
- 8
|
||||
- 9
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- 18.04
|
||||
- 20.04
|
||||
- 22.04
|
||||
- name: Debian
|
||||
versions:
|
||||
- 10
|
||||
- 11
|
||||
galaxy_tags:
|
||||
- linode
|
||||
- inventory
|
||||
- dynamic
|
||||
- cloud
|
||||
- automation
|
||||
|
||||
dependencies: []
|
@@ -1,214 +1,83 @@
|
||||
---
|
||||
# Main tasks for linode_inventory role
|
||||
|
||||
- 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: Validate Linode API token
|
||||
ansible.builtin.fail:
|
||||
msg: "LINODE_API_TOKEN environment variable must be set"
|
||||
when: lookup('env', 'LINODE_API_TOKEN') == ""
|
||||
|
||||
- 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
|
||||
- name: Fetch Linode instances
|
||||
ansible.builtin.uri:
|
||||
url: "https://api.linode.com/v4/linode/instances"
|
||||
method: GET
|
||||
headers:
|
||||
Authorization: "Bearer {{ lookup('env', 'LINODE_API_TOKEN') }}"
|
||||
Content-Type: "application/json"
|
||||
return_content: yes
|
||||
status_code: 200
|
||||
register: linode_response
|
||||
|
||||
- name: Ensure output directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ linode_inventory_output_dir }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
delegate_to: localhost
|
||||
|
||||
- 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
|
||||
|
||||
- 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: 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: 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 inventory summary
|
||||
- name: Display API response 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 }}
|
||||
Found {{ linode_response.json.data | length }} Linode instances
|
||||
Running instances: {{ linode_response.json.data | selectattr('status', 'equalto', 'running') | list | length }}
|
||||
|
||||
- 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 }}"
|
||||
- name: Add Linode hosts to in-memory inventory
|
||||
ansible.builtin.add_host:
|
||||
name: "{{ item.label }}"
|
||||
groups:
|
||||
- linode_all
|
||||
- "region_{{ item.region | replace('-', '_') }}"
|
||||
- "type_{{ item.type | replace('-', '_') | replace('.', '_') }}"
|
||||
- "status_{{ item.status }}"
|
||||
- "{% for tag in item.tags %}tag_{{ tag | replace('-', '_') | replace(' ', '_') | replace('.', '_') }}{% if not loop.last %},{% endif %}{% endfor %}"
|
||||
ansible_host: "{{ item.ipv4[0] }}"
|
||||
ansible_user: "phlux"
|
||||
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
|
||||
linode_id: "{{ item.id }}"
|
||||
linode_label: "{{ item.label }}"
|
||||
linode_region: "{{ item.region }}"
|
||||
linode_type: "{{ item.type }}"
|
||||
linode_status: "{{ item.status }}"
|
||||
linode_tags: "{{ item.tags }}"
|
||||
linode_ipv4: "{{ item.ipv4 }}"
|
||||
linode_ipv6: "{{ item.ipv6 | default('') }}"
|
||||
# Convenience boolean flags
|
||||
is_k3s: "{{ 'k3s' in item.tags }}"
|
||||
is_control_plane: "{{ 'control-plane' in item.tags }}"
|
||||
is_worker_node: "{{ 'worker-node' in item.tags }}"
|
||||
is_debian: "{{ 'Debian' in item.tags }}"
|
||||
is_ubuntu: "{{ 'Ubuntu' in item.tags }}"
|
||||
loop: "{{ linode_response.json.data }}"
|
||||
when:
|
||||
- item.status == "running"
|
||||
- item.ipv4 | length > 0
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
when: linode_inventory_data._meta.hostvars | length > 0
|
||||
label: "{{ item.label }}"
|
||||
|
||||
- name: Debug current directory and Git status
|
||||
ansible.builtin.shell: |
|
||||
echo "Current directory: $(pwd)" &&
|
||||
echo "Directory contents:" &&
|
||||
ls -la &&
|
||||
echo "Git status:" &&
|
||||
git status 2>&1 || echo "Not a git repository" &&
|
||||
echo "Git remote:" &&
|
||||
git remote -v 2>&1 || echo "No git remotes"
|
||||
register: debug_git_info
|
||||
delegate_to: localhost
|
||||
ignore_errors: true
|
||||
|
||||
- name: Initialize Git if needed and configure
|
||||
ansible.builtin.shell: |
|
||||
if [ ! -d ".git" ]; then
|
||||
echo "Not in a git repository, checking for git in parent directories"
|
||||
git_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
||||
if [ -n "$git_root" ]; then
|
||||
cd "$git_root"
|
||||
echo "Found git repository at: $git_root"
|
||||
else
|
||||
echo "No git repository found"
|
||||
exit 0
|
||||
fi
|
||||
fi &&
|
||||
git config user.email "awx@ewnix.net" &&
|
||||
git config user.name "AWX Automation" &&
|
||||
git remote set-url origin git@git.ewnix.net:phlux/ewnix-automation.git &&
|
||||
echo "Git configured successfully"
|
||||
register: git_config_result
|
||||
delegate_to: localhost
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check SSH configuration and keys
|
||||
ansible.builtin.shell: |
|
||||
echo "=== SSH configuration ==="
|
||||
echo "SSH_AUTH_SOCK: ${SSH_AUTH_SOCK:-not set}"
|
||||
echo "=== SSH keys available ==="
|
||||
ssh-add -l 2>/dev/null || echo "No SSH agent or no keys loaded"
|
||||
echo "=== SSH config ==="
|
||||
ls -la ~/.ssh/ 2>/dev/null || echo "No .ssh directory"
|
||||
echo "=== Test SSH to git.ewnix.net ==="
|
||||
timeout 10 ssh -T git@git.ewnix.net -o ConnectTimeout=5 -o StrictHostKeyChecking=no 2>&1 || echo "SSH test completed"
|
||||
register: ssh_debug
|
||||
delegate_to: localhost
|
||||
ignore_errors: true
|
||||
|
||||
- name: Create inventory directory
|
||||
ansible.builtin.file:
|
||||
path: "inventory"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Create JSON inventory file
|
||||
ansible.builtin.copy:
|
||||
content: "{{ linode_inventory_data | to_nice_json }}"
|
||||
dest: "inventory/linode_hosts.json"
|
||||
mode: '0644'
|
||||
delegate_to: localhost
|
||||
when: linode_inventory_data is defined
|
||||
|
||||
- name: Check if inventory file was created
|
||||
ansible.builtin.stat:
|
||||
path: "inventory/linode_hosts.json"
|
||||
register: inventory_file_check
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Attempt to commit and push inventory
|
||||
ansible.builtin.shell: |
|
||||
git_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
||||
if [ -n "$git_root" ]; then
|
||||
cd "$git_root" &&
|
||||
echo "Working in git repository: $git_root" &&
|
||||
git add inventory/linode_hosts.json &&
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git commit -m "Update Linode inventory - $(date '+%Y-%m-%d %H:%M:%S') [AWX]" &&
|
||||
echo "Committed successfully, attempting push..." &&
|
||||
timeout 30 git push origin HEAD &&
|
||||
echo "Push successful!"
|
||||
fi
|
||||
else
|
||||
echo "No git repository found - inventory file created but not committed"
|
||||
fi
|
||||
register: git_commit_result
|
||||
delegate_to: localhost
|
||||
ignore_errors: true
|
||||
when:
|
||||
- linode_inventory_data is defined
|
||||
- push_to_git | default(true)
|
||||
- inventory_file_check.stat.exists
|
||||
|
||||
- name: Display comprehensive debug information
|
||||
- name: Display discovered hosts summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
=== COMPREHENSIVE DEBUG RESULTS ===
|
||||
🎉 Successfully added {{ groups['linode_all'] | default([]) | length }} Linode hosts to inventory!
|
||||
|
||||
Current Directory Info:
|
||||
{% if debug_git_info is defined and debug_git_info.stdout is defined %}
|
||||
{{ debug_git_info.stdout }}
|
||||
{% else %}
|
||||
Debug info not available
|
||||
{% endif %}
|
||||
📋 Hosts discovered:
|
||||
{% for host in groups['linode_all'] | default([]) %}
|
||||
- {{ host }} ({{ hostvars[host]['ansible_host'] }})
|
||||
Region: {{ hostvars[host]['linode_region'] }}
|
||||
Type: {{ hostvars[host]['linode_type'] }}
|
||||
Tags: {{ hostvars[host]['linode_tags'] | join(', ') }}
|
||||
{% endfor %}
|
||||
|
||||
SSH Configuration:
|
||||
{% if ssh_debug is defined and ssh_debug.stdout is defined %}
|
||||
{{ ssh_debug.stdout }}
|
||||
{% else %}
|
||||
SSH debug not available
|
||||
{% endif %}
|
||||
📂 Groups available for targeting:
|
||||
{% for group in groups.keys() | sort if group.startswith(('tag_', 'region_', 'type_', 'status_')) %}
|
||||
- {{ group }}: {{ groups[group] | length }} hosts
|
||||
{% endfor %}
|
||||
|
||||
Git Configuration:
|
||||
{% if git_config_result is defined and git_config_result.stdout is defined %}
|
||||
{{ git_config_result.stdout }}
|
||||
{% else %}
|
||||
Git config not available
|
||||
{% endif %}
|
||||
💡 Use these groups in your job templates:
|
||||
- tag_k3s: All k3s cluster nodes
|
||||
- tag_control_plane: Control plane nodes only
|
||||
- tag_worker_node: Worker nodes only
|
||||
- region_us_southeast: All hosts in us-southeast
|
||||
|
||||
Git Commit/Push Results:
|
||||
{% if git_commit_result is defined and git_commit_result.stdout is defined %}
|
||||
{{ git_commit_result.stdout }}
|
||||
{% else %}
|
||||
Git commit result not available
|
||||
{% endif %}
|
||||
|
||||
Inventory File Status:
|
||||
- Exists: {{ inventory_file_check.stat.exists if inventory_file_check is defined else 'Unknown' }}
|
||||
{% if inventory_file_check is defined and inventory_file_check.stat.exists %}
|
||||
- Size: {{ inventory_file_check.stat.size }} bytes
|
||||
{% endif %}
|
||||
|
||||
- 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)
|
||||
✅ Hosts are now available for subsequent job templates in this workflow!
|
||||
|
@@ -1,107 +0,0 @@
|
||||
#!/bin/bash
|
||||
# AWX Inventory Update Script
|
||||
# Generated on: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
LINODE_INVENTORY_SCRIPT="{{ linode_inventory_output_dir }}/linode_inventory.py"
|
||||
INVENTORY_OUTPUT="{{ linode_inventory_output_dir }}/{{ linode_inventory_output_file }}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
log "Checking prerequisites..."
|
||||
|
||||
if [[ -z "${LINODE_API_TOKEN:-}" ]]; then
|
||||
error "LINODE_API_TOKEN environment variable is not set"
|
||||
fi
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
error "python3 is not installed"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$LINODE_INVENTORY_SCRIPT" ]]; then
|
||||
error "Inventory script not found: $LINODE_INVENTORY_SCRIPT"
|
||||
fi
|
||||
|
||||
log "Prerequisites check passed"
|
||||
}
|
||||
|
||||
# Update inventory
|
||||
update_inventory() {
|
||||
log "Updating Linode inventory..."
|
||||
|
||||
if ! python3 "$LINODE_INVENTORY_SCRIPT" --list > "$INVENTORY_OUTPUT.tmp"; then
|
||||
error "Failed to generate inventory"
|
||||
fi
|
||||
|
||||
# Validate JSON
|
||||
if ! python3 -m json.tool "$INVENTORY_OUTPUT.tmp" >/dev/null 2>&1; then
|
||||
error "Generated inventory is not valid JSON"
|
||||
fi
|
||||
|
||||
# Move to final location
|
||||
mv "$INVENTORY_OUTPUT.tmp" "$INVENTORY_OUTPUT"
|
||||
|
||||
log "Inventory updated successfully: $INVENTORY_OUTPUT"
|
||||
}
|
||||
|
||||
# Display summary
|
||||
display_summary() {
|
||||
if [[ -f "$INVENTORY_OUTPUT" ]]; then
|
||||
local host_count
|
||||
host_count=$(python3 -c "import json; data=json.load(open('$INVENTORY_OUTPUT')); print(len(data.get('_meta', {}).get('hostvars', {})))")
|
||||
|
||||
local group_count
|
||||
group_count=$(python3 -c "import json; data=json.load(open('$INVENTORY_OUTPUT')); print(len([k for k in data.keys() if k != '_meta']))")
|
||||
|
||||
log "Inventory Summary:"
|
||||
log " Hosts discovered: $host_count"
|
||||
log " Groups created: $group_count"
|
||||
log " Output file: $INVENTORY_OUTPUT"
|
||||
|
||||
# List hosts
|
||||
log "Discovered hosts:"
|
||||
python3 -c "
|
||||
import json
|
||||
data = json.load(open('$INVENTORY_OUTPUT'))
|
||||
for hostname, vars in data.get('_meta', {}).get('hostvars', {}).items():
|
||||
print(f' - {hostname} ({vars.get(\"ansible_host\", \"no-ip\")}) - {vars.get(\"linode_region\", \"unknown\")} - {vars.get(\"linode_status\", \"unknown\")}')
|
||||
"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log "Starting AWX Linode inventory update"
|
||||
|
||||
check_prerequisites
|
||||
update_inventory
|
||||
display_summary
|
||||
|
||||
log "Inventory update completed successfully"
|
||||
}
|
||||
|
||||
# Run if executed directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
@@ -1,22 +0,0 @@
|
||||
# Generated Linode Inventory
|
||||
# Generated on: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
{% for host_name, host_vars in linode_inventory_data._meta.hostvars.items() %}
|
||||
{{ host_name }} ansible_host={{ host_vars.ansible_host }} linode_id={{ host_vars.linode_id }} linode_region={{ host_vars.linode_region }} linode_type={{ host_vars.linode_type }} linode_status={{ host_vars.linode_status }}
|
||||
{% endfor %}
|
||||
|
||||
{% for group_name, group_data in linode_inventory_data.items() %}
|
||||
{% if group_name != '_meta' and group_name != 'all' %}
|
||||
[{{ group_name }}]
|
||||
{% if group_data.hosts is defined %}
|
||||
{% for host in group_data.hosts %}
|
||||
{{ host }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
[all:vars]
|
||||
ansible_user=phlux
|
||||
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
|
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hostvars": {
|
||||
{% for host, vars in linode_inventory_data._meta.hostvars.items() %}
|
||||
"{{ host }}": {
|
||||
"ansible_host": "{{ vars.ansible_host }}",
|
||||
"ansible_user": "phlux",
|
||||
"linode_id": {{ vars.linode_id }},
|
||||
"linode_label": "{{ vars.linode_label }}",
|
||||
"linode_region": "{{ vars.linode_region }}",
|
||||
"linode_type": "{{ vars.linode_type }}",
|
||||
"linode_status": "{{ vars.linode_status }}",
|
||||
"linode_ipv4": {{ vars.linode_ipv4 | to_json }},
|
||||
"linode_ipv6": "{{ vars.linode_ipv6 | default('') }}",
|
||||
"linode_tags": {{ vars.linode_tags | to_json }},
|
||||
"is_debian": {{ vars.is_debian | lower }},
|
||||
"is_ubuntu": {{ vars.is_ubuntu | lower }},
|
||||
"is_k3s": {{ vars.is_k3s | lower }},
|
||||
"is_control_plane": {{ vars.is_control_plane | lower }},
|
||||
"is_worker_node": {{ vars.is_worker_node | lower }},
|
||||
"tag_string": "{{ vars.tag_string }}"
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
}
|
||||
},
|
||||
"all": {
|
||||
"children": {{ linode_inventory_data.all.children | to_json }}
|
||||
},
|
||||
"ungrouped": {
|
||||
"hosts": {{ linode_inventory_data.ungrouped.hosts | to_json }}
|
||||
}{% for group_name, group_data in linode_inventory_data.items() %}{% if group_name not in ['_meta', 'all', 'ungrouped'] %},
|
||||
"{{ group_name }}": {
|
||||
"hosts": {{ group_data.hosts | to_json }}
|
||||
}{% endif %}{% endfor %}
|
||||
}
|
@@ -1,14 +1,13 @@
|
||||
---
|
||||
# Internal variables for linode_inventory role
|
||||
|
||||
linode_inventory_script_path: "{{ role_path }}/files/linode_inventory.py"
|
||||
temp_inventory_path: "{{ linode_inventory_output_dir }}/{{ linode_inventory_output_file }}"
|
||||
# Linode API endpoint
|
||||
linode_api_url: "https://api.linode.com/v4/linode/instances"
|
||||
|
||||
# Required environment variables
|
||||
# Required environment variables
|
||||
required_env_vars:
|
||||
- LINODE_API_TOKEN
|
||||
|
||||
# Python packages required
|
||||
python_requirements:
|
||||
- requests
|
||||
- json
|
||||
# Default groups that will be created
|
||||
default_groups:
|
||||
- linode_all
|
||||
|
Reference in New Issue
Block a user