2424import time
2525from collections import defaultdict
2626from collections .abc import AsyncIterator , Callable , Iterable , Mapping , Sequence
27+ from enum import Enum
2728from multiprocessing import Lock
2829from pathlib import Path
2930from typing import Any , Literal , NamedTuple , Required , TypedDict , TypeVar
227228}
228229
229230
231+ class FetchedResource (Enum ):
232+ """Available Azure resources, with section name, for API fetching"""
233+
234+ virtual_machines = ("Microsoft.Compute/virtualMachines" , "virtualmachines" )
235+
236+ def __init__ (self , resource_type , section_name ):
237+ self .resource_type = resource_type
238+ self .section_name = section_name
239+
240+ @property
241+ def section (self ):
242+ return self .section_name
243+
244+ @property
245+ def type (self ):
246+ return self .resource_type
247+
248+
249+ # list of metrics type handles within the new async functions
250+ # not to be gathered with the old methods
251+ METRICS_GATHERED_ASYNC = {
252+ FetchedResource .virtual_machines .type ,
253+ }
254+
255+
230256class TagsImportPatternOption (enum .Enum ):
231257 ignore_all = "IGNORE_ALL"
232258 import_all = "IMPORT_ALL"
@@ -902,10 +928,6 @@ async def resourcegroups(self):
902928 async def resources (self ):
903929 return await self .get_async ("resources" , key = "value" , params = {"api-version" : "2019-05-01" })
904930
905- async def vmview (self , group , name ):
906- temp = "resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s/instanceView"
907- return await self .get_async (temp % (group , name ), params = {"api-version" : "2018-06-01" })
908-
909931 async def app_gateway_view (self , group , name ):
910932 url = "resourceGroups/{}/providers/Microsoft.Network/applicationGateways/{}"
911933 return await self .get_async (url .format (group , name ), params = {"api-version" : "2022-01-01" })
@@ -1298,17 +1320,6 @@ def filter_keys(mapping: Mapping, keys: Iterable[str]) -> Mapping:
12981320 return {k : v for k , v in items if v is not None }
12991321
13001322
1301- async def process_vm (mgmt_client : MgmtApiClient , vmach : AzureResource , args : Args ) -> None :
1302- use_keys = ("statuses" ,)
1303-
1304- inst_view = await mgmt_client .vmview (vmach .info ["group" ], vmach .info ["name" ])
1305- vmach .info ["specific_info" ] = filter_keys (inst_view , use_keys )
1306-
1307- if args .piggyback_vms == "self" :
1308- vmach .piggytargets .remove (vmach .info ["group" ])
1309- vmach .piggytargets .append (vmach .info ["name" ])
1310-
1311-
13121323def get_params_from_azure_id (
13131324 resource_id : str , resource_types : Sequence [str ] | None = None
13141325) -> Sequence [str ]:
@@ -1751,7 +1762,30 @@ def write_section_app_registrations(graph_client: GraphApiClient, args: argparse
17511762 section .write ()
17521763
17531764
1754- async def gather_metrics (
1765+ async def process_old_metrics (
1766+ mgmt_client : MgmtApiClient , resources : Sequence [AzureResource ], args : Args
1767+ ) -> None :
1768+ resources = [
1769+ resource for resource in resources if resource .info ["type" ] not in METRICS_GATHERED_ASYNC
1770+ ]
1771+
1772+ await process_metrics (mgmt_client , resources , args )
1773+
1774+
1775+ async def process_metrics (
1776+ mgmt_client : MgmtApiClient , resources : Sequence [AzureResource ], args : Args
1777+ ) -> None :
1778+ errors = await _gather_metrics (mgmt_client , resources , args )
1779+
1780+ if not errors :
1781+ return
1782+
1783+ agent_info_section = AzureSection ("agent_info" )
1784+ agent_info_section .add (errors .dumpinfo ())
1785+ agent_info_section .write ()
1786+
1787+
1788+ async def _gather_metrics (
17551789 mgmt_client : MgmtApiClient , all_resources : Sequence [AzureResource ], args : Args
17561790) -> IssueCollector :
17571791 """
@@ -1763,12 +1797,6 @@ async def gather_metrics(
17631797
17641798 grouped_resource_ids = defaultdict (list )
17651799 for resource in all_resources :
1766- if (
1767- resource .info ["type" ] == "Microsoft.Compute/virtualMachines"
1768- and args .piggyback_vms == "grouphost"
1769- ):
1770- continue
1771-
17721800 grouped_resource_ids [(resource .info ["type" ], resource .info ["location" ])].append (
17731801 resource .info ["id" ]
17741802 )
@@ -1831,7 +1859,6 @@ def get_vm_labels_section(vm: AzureResource, group_labels: GroupLabels) -> Label
18311859async def process_resource (
18321860 mgmt_client : MgmtApiClient ,
18331861 resource : AzureResource ,
1834- group_labels : GroupLabels ,
18351862 args : Args ,
18361863) -> Sequence [Section ]:
18371864 sections : list [Section ] = []
@@ -1840,13 +1867,10 @@ async def process_resource(
18401867 if resource_type not in enabled_services :
18411868 return sections
18421869
1843- if resource_type == "Microsoft.Compute/virtualMachines" :
1844- await process_vm (mgmt_client , resource , args )
1845-
1846- if args .piggyback_vms == "self" :
1847- sections .append (get_vm_labels_section (resource , group_labels ))
1870+ if resource_type in METRICS_GATHERED_ASYNC :
1871+ return sections
18481872
1849- elif resource_type == "Microsoft.Network/applicationGateways" :
1873+ if resource_type == "Microsoft.Network/applicationGateways" :
18501874 await process_app_gateway (mgmt_client , resource )
18511875 elif resource_type == "Microsoft.RecoveryServices/vaults" :
18521876 await process_recovery_services_vaults (mgmt_client , resource )
@@ -1867,12 +1891,11 @@ async def process_resource(
18671891async def process_resources (
18681892 mgmt_client : MgmtApiClient ,
18691893 resources : Sequence [AzureResource ],
1870- group_labels : GroupLabels ,
18711894 args : Args ,
18721895) -> AsyncIterator [Sequence [Section ]]:
18731896 for resource in resources :
18741897 try :
1875- yield await process_resource (mgmt_client , resource , group_labels , args )
1898+ yield await process_resource (mgmt_client , resource , args )
18761899 except Exception as exc :
18771900 if args .debug :
18781901 raise
@@ -2074,6 +2097,54 @@ async def process_resource_health(
20742097 return _write_resource_health_section (resource_health_view , monitored_resources , args )
20752098
20762099
2100+ async def process_virtual_machines (
2101+ api_client : MgmtApiClient ,
2102+ args : Args ,
2103+ group_labels : GroupLabels ,
2104+ resources : Mapping [str , AzureResource ],
2105+ ) -> None :
2106+ response = await api_client .get_async (
2107+ "providers/Microsoft.Compute/virtualMachines" ,
2108+ params = {
2109+ "api-version" : "2024-11-01" ,
2110+ "statusOnly" : "true" , # fetching only run time status
2111+ },
2112+ key = "value" ,
2113+ )
2114+
2115+ virtual_machines : list [AzureResource ] = []
2116+ for vm in response :
2117+ try :
2118+ resource = resources [vm ["id" ].lower ()]
2119+ except KeyError :
2120+ raise ApiErrorMissingData (
2121+ f"Virtual machine not found in monitored resources: { vm ['id' ]} "
2122+ )
2123+
2124+ try :
2125+ statuses = vm .pop ("properties" )["instanceView" ]["statuses" ]
2126+ except KeyError :
2127+ raise ApiErrorMissingData ("Virtual machine instance's statuses must be present" )
2128+
2129+ resource .info ["specific_info" ] = {"statuses" : statuses }
2130+ virtual_machines .append (resource )
2131+
2132+ if args .piggyback_vms == "self" :
2133+ await process_metrics (api_client , virtual_machines , args )
2134+
2135+ for resource in virtual_machines :
2136+ if args .piggyback_vms == "self" :
2137+ labels_section = get_vm_labels_section (resource , group_labels )
2138+ labels_section .write ()
2139+
2140+ section = AzureSection (
2141+ FetchedResource .virtual_machines .section ,
2142+ [resource .info ["name" ] if args .piggyback_vms == "self" else resource .info ["group" ]],
2143+ )
2144+ section .add (resource .dumpinfo ())
2145+ section .write ()
2146+
2147+
20772148class ResourceHealth (TypedDict , total = False ):
20782149 id : Required [str ]
20792150 properties : Required [Mapping [str , str ]]
@@ -2140,6 +2211,33 @@ def _test_connection(args: Args, subscription: str) -> int | tuple[int, str]:
21402211 return 0
21412212
21422213
2214+ async def process_resources_async (
2215+ mgmt_client : MgmtApiClient ,
2216+ args : Args ,
2217+ group_labels : GroupLabels ,
2218+ monitored_resources : Sequence [AzureResource ],
2219+ ) -> None :
2220+ tasks = set ()
2221+
2222+ monitored_types = {
2223+ type_ for r in monitored_resources if (type_ := r .info ["type" ]) in args .services
2224+ }
2225+ resources_by_id = {
2226+ r .info ["id" ].lower (): r for r in monitored_resources if r .info ["type" ] in monitored_types
2227+ }
2228+
2229+ if FetchedResource .virtual_machines .type in monitored_types :
2230+ tasks .add (process_virtual_machines (mgmt_client , args , group_labels , resources_by_id ))
2231+
2232+ for coroutine in asyncio .as_completed (tasks ):
2233+ try :
2234+ await coroutine
2235+ except Exception as e :
2236+ if args .debug :
2237+ raise
2238+ write_exception_to_agent_info_section (e , "Management client (async)" )
2239+
2240+
21432241async def main_subscription (args : Args , selector : Selector , subscription : str ) -> None :
21442242 mgmt_client = MgmtApiClient (
21452243 _get_mgmt_authority_urls (args .authority , subscription ),
@@ -2167,12 +2265,11 @@ async def main_subscription(args: Args, selector: Selector, subscription: str) -
21672265
21682266 await usage_details (mgmt_client , monitored_groups , args )
21692267
2170- if err := await gather_metrics (mgmt_client , monitored_resources , args ):
2171- agent_info_section = AzureSection ("agent_info" )
2172- agent_info_section .add (err .dumpinfo ())
2173- agent_info_section .write ()
2268+ await process_resources_async (mgmt_client , args , group_labels , monitored_resources )
2269+
2270+ await process_old_metrics (mgmt_client , monitored_resources , args )
21742271
2175- all_sections = process_resources (mgmt_client , monitored_resources , group_labels , args )
2272+ all_sections = process_resources (mgmt_client , monitored_resources , args )
21762273 async for resource_sections in all_sections :
21772274 for section in resource_sections :
21782275 section .write ()
0 commit comments