diff --git a/roles/inventory/linode/files/linode_inventory.py b/roles/inventory/linode/files/linode_inventory.py index aff1330..6a7036e 100644 --- a/roles/inventory/linode/files/linode_inventory.py +++ b/roles/inventory/linode/files/linode_inventory.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 import json -import requests import sys import os +import urllib.request +import urllib.error from argparse import ArgumentParser class LinodeInventory: @@ -13,17 +14,43 @@ class LinodeInventory: raise ValueError("LINODE_API_TOKEN environment variable is required") self.base_url = "https://api.linode.com/v4" - self.headers = { - "Authorization": f"Bearer {self.api_token}", - "Content-Type": "application/json" - } + 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""" - url = f"{self.base_url}/linode/instances" - response = requests.get(url, headers=self.headers) - response.raise_for_status() - return response.json()['data'] + 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""" @@ -47,14 +74,22 @@ class LinodeInventory: 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 (this is what you wanted!) + # 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) @@ -72,7 +107,10 @@ class LinodeInventory: 'linode_specs': instance.get('specs', {}), 'linode_hypervisor': instance.get('hypervisor'), 'linode_created': instance.get('created'), - 'linode_updated': instance.get('updated') + 'linode_updated': instance.get('updated'), + 'linode_group': instance.get('group', ''), + 'linode_image': instance.get('image', {}).get('id', '') if instance.get('image') else '', + 'linode_backups': instance.get('backups', {}).get('enabled', False) } # Group by region @@ -95,14 +133,21 @@ class LinodeInventory: # Group by tags for tag in instance.get('tags', []): - tag_group = f"tag_{tag.replace('-', '_').replace(' ', '_')}" + 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 requests.exceptions.RequestException as e: - print(f"Error fetching Linode instances: {e}", file=sys.stderr) - return {} + except Exception as e: + print(f"Error generating inventory: {e}", file=sys.stderr) + return inventory # Add all groups to inventory inventory.update(regions) @@ -112,7 +157,9 @@ class LinodeInventory: # 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) @@ -135,9 +182,11 @@ def main(): inventory = LinodeInventory() if args.list: - print(json.dumps(inventory.generate_inventory(), indent=2)) + result = inventory.generate_inventory() + print(json.dumps(result, indent=2)) elif args.host: - print(json.dumps(inventory.get_host_vars(args.host), indent=2)) + result = inventory.get_host_vars(args.host) + print(json.dumps(result, indent=2)) else: parser.print_help()