Coverage for src / server_list / spec / webapi / config.py: 100%
33 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-31 11:45 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-31 11:45 +0000
1#!/usr/bin/env python3
2"""
3Web API for serving config.yaml dynamically.
4Endpoint: /server-list/api/config
6For ESXi hosts, VM list is automatically populated from collected data.
7"""
9import flask
11import server_list.spec.data_collector as data_collector
12import server_list.spec.webapi as webapi
14config_api = flask.Blueprint("config_api", __name__)
17def is_esxi_host(machine: dict) -> bool:
18 """Check if machine is an ESXi host."""
19 os_name = machine.get("os", "").lower()
20 return "esxi" in os_name
23def enrich_config_with_vm_data(config: dict) -> dict:
24 """Enrich config with VM data from ESXi hosts.
26 For machines running ESXi, automatically populate the VM list
27 from collected ESXi data instead of using config.yaml.
29 When ESXi host is unreachable, cached data is used but power_state
30 is set to 'unknown' to indicate the current state cannot be determined.
32 Optimized: Uses batch queries to fetch all VM info and collection
33 statuses in 2 DB queries instead of 2N queries (N = number of hosts).
34 """
35 if "machine" not in config:
36 return config
38 # Batch fetch all data in 2 queries instead of 2N queries
39 all_vm_info = data_collector.get_all_vm_info()
40 all_status = data_collector.get_all_collection_status()
42 enriched_machines = []
44 for machine in config["machine"]:
45 machine_copy = dict(machine)
47 if is_esxi_host(machine_copy):
48 host_name = machine_copy.get("name", "")
49 vm_list = all_vm_info.get(host_name, [])
51 if vm_list:
52 # Check if host is reachable from pre-fetched status
53 status = all_status.get(host_name)
54 host_reachable = status is not None and status.status == "success"
56 # Convert to config format with additional info
57 machine_copy["vm"] = [
58 {
59 "name": vm.vm_name,
60 "power_state": vm.power_state if host_reachable else "unknown",
61 }
62 for vm in vm_list
63 ]
65 enriched_machines.append(machine_copy)
67 return {**config, "machine": enriched_machines}
70@config_api.route("/config", methods=["GET"])
71def get_config_api():
72 """
73 Get the server configuration.
75 For ESXi hosts, VM list is automatically populated from collected data.
77 Returns:
78 JSON with machine configuration data
79 """
80 from server_list.config import Config
82 config: Config | None = flask.current_app.config.get("CONFIG")
84 if config is None:
85 return webapi.error_response("Config not available", 503)
87 # Convert Config dataclass to dict and enrich with VM data
88 config_dict = config.to_dict()
89 enriched_config = enrich_config_with_vm_data(config_dict)
90 return webapi.success_response(enriched_config)