···
1
+
#!/usr/bin/env python3
3
+
Dynamic inventory script for Clouding.io servers.
4
+
Fetches server list from Clouding API and generates Ansible inventory.
7
+
- CLOUDING_TOKEN environment variable with API token
10
+
ansible-inventory -i inventory/prod/clouding.py --list
11
+
ansible-playbook -i inventory/prod/clouding.py playbook.yaml
14
+
from __future__ import annotations
19
+
from urllib.request import Request, urlopen
20
+
from urllib.error import URLError, HTTPError
21
+
from dataclasses import dataclass
25
+
class CloudingInventory:
26
+
"""Dynamic inventory for Clouding.io servers."""
30
+
endpoint: str = "https://api.clouding.io/v1/servers/"
33
+
def create(cls, endpoint: str | None = None) -> CloudingInventory:
34
+
api_token = os.environ.get("CLOUDING_TOKEN")
36
+
print("Error: CLOUDING_TOKEN environment variable not set", file=sys.stderr)
39
+
inventory = {"_meta": {"hostvars": {}}, "all": {"children": ["ungrouped"]}}
42
+
return cls(inventory, api_token, endpoint)
44
+
return cls(inventory, api_token)
46
+
def fetch_servers(self):
47
+
"""Fetch server list from Clouding API."""
48
+
headers = {"X-API-KEY": self.api_token, "Accept": "application/json"}
51
+
request = Request(self.endpoint, headers=headers)
52
+
with urlopen(request) as response:
53
+
data = json.loads(response.read().decode("utf-8"))
54
+
return data.get("servers", [])
55
+
except HTTPError as e:
56
+
print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
58
+
except URLError as e:
59
+
print(f"URL Error: {e.reason}", file=sys.stderr)
61
+
except Exception as e:
62
+
print(f"Error fetching servers: {e}", file=sys.stderr)
65
+
def add_server_to_inventory(self, server):
66
+
"""Add a server to the inventory."""
67
+
server_id = server.get("id")
68
+
server_name = server.get("name")
69
+
status = server.get("status")
70
+
power_state = server.get("powerState")
71
+
public_ip = server.get("publicIp")
73
+
if status != "Active" or power_state != "Running":
79
+
self.inventory["_meta"]["hostvars"][server_name] = {
80
+
"ansible_host": public_ip,
81
+
"ansible_user": "root", # Clouding uses root by default
82
+
"ansible_python_interpreter": "auto_silent",
83
+
"clouding_id": server_id,
84
+
"clouding_hostname": server.get("hostname"),
85
+
"clouding_status": status,
86
+
"clouding_power_state": power_state,
87
+
"clouding_vcores": server.get("vCores"),
88
+
"clouding_ram_gb": server.get("ramGb"),
89
+
"clouding_flavor": server.get("flavor"),
90
+
"clouding_volume_size_gb": server.get("volumeSizeGb"),
91
+
"clouding_dns_address": server.get("dnsAddress"),
94
+
image = server.get("image", {})
96
+
self.inventory["_meta"]["hostvars"][server_name].update(
98
+
"clouding_image_id": image.get("id"),
99
+
"clouding_image_name": image.get("name"),
103
+
# Add to 'clouding' group
104
+
if "clouding" not in self.inventory:
105
+
self.inventory["clouding"] = {"hosts": []}
106
+
self.inventory["all"]["children"].append("clouding")
108
+
if server_name not in self.inventory["clouding"]["hosts"]:
109
+
self.inventory["clouding"]["hosts"].append(server_name)
111
+
def generate_inventory(self):
112
+
"""Generate the complete inventory."""
113
+
servers = self.fetch_servers()
114
+
for server in servers:
115
+
self.add_server_to_inventory(server)
116
+
return self.inventory
120
+
"""Main entry point."""
121
+
inventory = CloudingInventory.create()
122
+
result = inventory.generate_inventory()
123
+
print(json.dumps(result, indent=2))
126
+
if __name__ == "__main__":