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

1#!/usr/bin/env python3 

2""" 

3Web API for serving config.yaml dynamically. 

4Endpoint: /server-list/api/config 

5 

6For ESXi hosts, VM list is automatically populated from collected data. 

7""" 

8 

9import flask 

10 

11import server_list.spec.data_collector as data_collector 

12import server_list.spec.webapi as webapi 

13 

14config_api = flask.Blueprint("config_api", __name__) 

15 

16 

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 

21 

22 

23def enrich_config_with_vm_data(config: dict) -> dict: 

24 """Enrich config with VM data from ESXi hosts. 

25 

26 For machines running ESXi, automatically populate the VM list 

27 from collected ESXi data instead of using config.yaml. 

28 

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. 

31 

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 

37 

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() 

41 

42 enriched_machines = [] 

43 

44 for machine in config["machine"]: 

45 machine_copy = dict(machine) 

46 

47 if is_esxi_host(machine_copy): 

48 host_name = machine_copy.get("name", "") 

49 vm_list = all_vm_info.get(host_name, []) 

50 

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" 

55 

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 ] 

64 

65 enriched_machines.append(machine_copy) 

66 

67 return {**config, "machine": enriched_machines} 

68 

69 

70@config_api.route("/config", methods=["GET"]) 

71def get_config_api(): 

72 """ 

73 Get the server configuration. 

74 

75 For ESXi hosts, VM list is automatically populated from collected data. 

76 

77 Returns: 

78 JSON with machine configuration data 

79 """ 

80 from server_list.config import Config 

81 

82 config: Config | None = flask.current_app.config.get("CONFIG") 

83 

84 if config is None: 

85 return webapi.error_response("Config not available", 503) 

86 

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)