Skip to content

Commit 1980175

Browse files
authored
[azure][fix] Reimplement resource type collection of compute, psql, mysql and ml services (#2234)
1 parent 8adf359 commit 1980175

File tree

6 files changed

+299
-133
lines changed

6 files changed

+299
-133
lines changed

plugins/azure/fix_plugin_azure/collector.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,11 @@ def remove_usage_zero_value() -> None:
269269
rm_leaf_nodes(AzureComputeVirtualMachineSize, AzureLocation)
270270
rm_leaf_nodes(AzureNetworkExpressRoutePortsLocation, AzureSubscription)
271271
rm_leaf_nodes(AzureNetworkVirtualApplianceSku, AzureSubscription)
272-
rm_leaf_nodes(AzureComputeDiskType, AzureSubscription)
272+
rm_leaf_nodes(AzureComputeDiskType, (AzureSubscription, AzureLocation)) # type: ignore
273273
rm_leaf_nodes(AzureMachineLearningVirtualMachineSize, AzureLocation)
274274
rm_leaf_nodes(AzureStorageSku, AzureLocation)
275-
rm_leaf_nodes(AzureMysqlServerType, AzureSubscription)
276-
rm_leaf_nodes(AzurePostgresqlServerType, AzureSubscription)
275+
rm_leaf_nodes(AzureMysqlServerType, AzureLocation)
276+
rm_leaf_nodes(AzurePostgresqlServerType, AzureLocation)
277277
rm_leaf_nodes(AzureCosmosDBLocation, AzureLocation, check_pred=False)
278278
rm_leaf_nodes(AzureLocation, check_pred=False)
279279
rm_leaf_nodes(AzureComputeDiskTypePricing, AzureSubscription)

plugins/azure/fix_plugin_azure/resource/compute.py

Lines changed: 91 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import defaultdict
12
import logging
23
from datetime import datetime
34
from typing import ClassVar, Dict, Optional, List, Any, Type
@@ -899,15 +900,16 @@ def build_custom_disk_size(
899900
return premium_ssd_v2_object
900901

901902
@staticmethod
902-
def create_unique_disk_sizes(collected_disks: List[MicrosoftResourceType], builder: GraphBuilder) -> None:
903+
def create_unique_disk_sizes(
904+
collected_disks: List[MicrosoftResourceType], builder: GraphBuilder, location: str
905+
) -> None:
903906
disk_sizes: List[Json] = []
904907
seen_hashes = set() # Set to keep track of unique hashes
905908
for disk in collected_disks:
906909
if not isinstance(disk, AzureComputeDisk):
907910
continue
908911
if (
909912
(volume_type := disk.volume_type)
910-
and (location := disk.location)
911913
and (size := disk.volume_size)
912914
and (iops := disk.volume_iops)
913915
and (throughput := disk.volume_throughput)
@@ -1046,15 +1048,22 @@ class AzureComputeDisk(MicrosoftResource, BaseVolume):
10461048
tier_name: Optional[str] = field(default=None, metadata={"description": "The sku tier."})
10471049

10481050
@classmethod
1049-
def collect_resources(
1050-
cls: Type[MicrosoftResourceType], builder: GraphBuilder, **kwargs: Any
1051-
) -> List[MicrosoftResourceType]:
1051+
def collect_resources(cls, builder: GraphBuilder, **kwargs: Any) -> List["AzureComputeDisk"]:
10521052
log.debug(f"[Azure:{builder.account.id}] Collecting {cls.__name__} with ({kwargs})")
1053+
if not issubclass(cls, MicrosoftResource):
1054+
return []
10531055
if spec := cls.api_spec:
10541056
items = builder.client.list(spec, **kwargs)
10551057
collected = cls.collect(items, builder)
1056-
# Create additional custom disk sizes for disks with Ultra SSD or Premium SSD v2 types
1057-
AzureComputeDiskType.create_unique_disk_sizes(collected, builder)
1058+
disks_by_location = defaultdict(list)
1059+
for disk in collected:
1060+
if disk_location := getattr(disk, "location", None):
1061+
disks_by_location[disk_location].append(disk)
1062+
for d_loc, disks in disks_by_location.items():
1063+
# Collect disk types for the disks in this location
1064+
AzureComputeDisk._collect_disk_types(builder, d_loc)
1065+
# Create additional custom disk sizes for disks with Ultra SSD or Premium SSD v2 types
1066+
AzureComputeDiskType.create_unique_disk_sizes(disks, builder, d_loc)
10581067
if builder.config.collect_usage_metrics:
10591068
try:
10601069
cls.collect_usage_metrics(builder, collected)
@@ -1063,33 +1072,32 @@ def collect_resources(
10631072
return collected
10641073
return []
10651074

1066-
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
1067-
if location := self.location:
1068-
1069-
def collect_disk_types() -> None:
1070-
log.debug(f"[Azure:{graph_builder.account.id}] Collecting AzureComputeDiskType")
1071-
product_names = {
1072-
"Standard SSD Managed Disks",
1073-
"Premium SSD Managed Disks",
1074-
"Standard HDD Managed Disks",
1075-
}
1076-
sku_items = []
1077-
for product_name in product_names:
1078-
api_spec = AzureResourceSpec(
1079-
service="compute",
1080-
version="2023-01-01-preview",
1081-
path=f"https://prices.azure.com/api/retail/prices?$filter=productName eq '{product_name}' and armRegionName eq '{location}' and unitOfMeasure eq '1/Month' and serviceFamily eq 'Storage' and type eq 'Consumption' and isPrimaryMeterRegion eq true",
1082-
path_parameters=[],
1083-
query_parameters=["api-version"],
1084-
access_path="Items",
1085-
expect_array=True,
1086-
)
1075+
@staticmethod
1076+
def _collect_disk_types(graph_builder: GraphBuilder, location: str) -> None:
1077+
def collect_disk_types() -> None:
1078+
log.debug(f"[Azure:{graph_builder.account.id}] Collecting AzureComputeDiskType")
1079+
product_names = {
1080+
"Standard SSD Managed Disks",
1081+
"Premium SSD Managed Disks",
1082+
"Standard HDD Managed Disks",
1083+
}
1084+
sku_items = []
1085+
for product_name in product_names:
1086+
api_spec = AzureResourceSpec(
1087+
service="compute",
1088+
version="2023-01-01-preview",
1089+
path=f"https://prices.azure.com/api/retail/prices?$filter=productName eq '{product_name}' and armRegionName eq '{location}' and unitOfMeasure eq '1/Month' and serviceFamily eq 'Storage' and type eq 'Consumption' and isPrimaryMeterRegion eq true",
1090+
path_parameters=[],
1091+
query_parameters=["api-version"],
1092+
access_path="Items",
1093+
expect_array=True,
1094+
)
10871095

1088-
items = graph_builder.client.list(api_spec)
1089-
sku_items.extend(items)
1090-
AzureComputeDiskType.collect(sku_items, graph_builder)
1096+
items = graph_builder.client.list(api_spec)
1097+
sku_items.extend(items)
1098+
AzureComputeDiskType.collect(sku_items, graph_builder)
10911099

1092-
graph_builder.submit_work(service_name, collect_disk_types)
1100+
graph_builder.submit_work(service_name, collect_disk_types)
10931101

10941102
@classmethod
10951103
def collect_usage_metrics(
@@ -2954,30 +2962,60 @@ def collect_instance_status() -> None:
29542962
if not instance_status_set:
29552963
self.instance_status = InstanceStatus.UNKNOWN
29562964

2957-
if location := self.location:
2965+
graph_builder.submit_work(service_name, collect_instance_status)
29582966

2959-
def collect_vm_sizes() -> None:
2960-
api_spec = AzureResourceSpec(
2961-
service="compute",
2962-
version="2023-03-01",
2963-
path="/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/"
2964-
+ f"{location}/vmSizes",
2965-
path_parameters=["subscriptionId"],
2966-
query_parameters=["api-version"],
2967-
access_path="value",
2968-
expect_array=True,
2969-
)
2970-
items = graph_builder.client.list(api_spec)
2971-
if not items:
2972-
return
2973-
# Set location for further connect_in_graph method
2974-
for item in items:
2975-
item["location"] = location
2976-
AzureComputeVirtualMachineSize.collect(items, graph_builder)
2967+
@classmethod
2968+
def collect_resources(cls, builder: GraphBuilder, **kwargs: Any) -> List["AzureComputeVirtualMachineBase"]:
2969+
log.debug(f"[Azure:{builder.account.id}] Collecting {cls.__name__} with ({kwargs})")
29772970

2978-
graph_builder.submit_work(service_name, collect_vm_sizes)
2971+
if not issubclass(cls, MicrosoftResource):
2972+
return []
29792973

2980-
graph_builder.submit_work(service_name, collect_instance_status)
2974+
if spec := cls.api_spec:
2975+
items = builder.client.list(spec, **kwargs)
2976+
collected = cls.collect(items, builder)
2977+
2978+
unique_locations = set(getattr(vm, "location") for vm in collected if getattr(vm, "location"))
2979+
2980+
for location in unique_locations:
2981+
log.debug(f"Processing virtual machines in location: {location}")
2982+
2983+
# Collect VM sizes for the VM in this location
2984+
AzureComputeVirtualMachineBase._collect_vm_sizes(builder, location)
2985+
2986+
if builder.config.collect_usage_metrics:
2987+
try:
2988+
cls.collect_usage_metrics(builder, collected)
2989+
except Exception as e:
2990+
log.warning(f"Failed to collect usage metrics for {cls.__name__}: {e}")
2991+
2992+
return collected
2993+
2994+
return []
2995+
2996+
@staticmethod
2997+
def _collect_vm_sizes(graph_builder: GraphBuilder, location: str) -> None:
2998+
def collect_vm_sizes() -> None:
2999+
api_spec = AzureResourceSpec(
3000+
service="compute",
3001+
version="2023-03-01",
3002+
path=f"/subscriptions/{{subscriptionId}}/providers/Microsoft.Compute/locations/{location}/vmSizes",
3003+
path_parameters=["subscriptionId"],
3004+
query_parameters=["api-version"],
3005+
access_path="value",
3006+
expect_array=True,
3007+
)
3008+
items = graph_builder.client.list(api_spec)
3009+
if not items:
3010+
return
3011+
3012+
# Set location for further connect_in_graph method
3013+
for item in items:
3014+
item["location"] = location
3015+
3016+
AzureComputeVirtualMachineSize.collect(items, graph_builder)
3017+
3018+
graph_builder.submit_work(service_name, collect_vm_sizes)
29813019

29823020
@classmethod
29833021
def collect_usage_metrics(

plugins/azure/fix_plugin_azure/resource/machinelearning.py

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from collections import defaultdict
34
import logging
45
from datetime import datetime
56
from typing import Any, ClassVar, Dict, Optional, List, Tuple, Type
@@ -553,32 +554,78 @@ class AzureMachineLearningCompute(MicrosoftResource):
553554
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
554555
location: Optional[str] = field(default=None, metadata={'description': 'The geo-location where the resource lives'}) # fmt: skip
555556

556-
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
557-
if location := self.location:
557+
@classmethod
558+
def collect_resources(cls, builder: GraphBuilder, **kwargs: Any) -> List["AzureMachineLearningCompute"]:
559+
log.debug(f"[Azure:{builder.account.id}] Collecting {cls.__name__} with ({kwargs})")
558560

559-
def collect_vm_sizes() -> None:
560-
api_spec = AzureResourceSpec(
561-
service="machinelearningservices",
562-
version="2024-04-01",
563-
path="/subscriptions/{subscriptionId}/providers/Microsoft.MachineLearningServices/locations/"
564-
+ f"{location}/vmSizes",
565-
path_parameters=["subscriptionId"],
566-
query_parameters=["api-version"],
567-
access_path="value",
568-
expect_array=True,
569-
)
570-
items = graph_builder.client.list(api_spec)
571-
if not items:
572-
return
573-
for item in items:
574-
item["location"] = location
575-
collected = AzureMachineLearningVirtualMachineSize.collect(items, graph_builder)
576-
for _ in collected:
577-
if (properties := self.properties) and (vm_size := properties.get("vmSize")):
578-
graph_builder.add_edge(self, clazz=AzureMachineLearningVirtualMachineSize, name=vm_size)
561+
if not issubclass(cls, MicrosoftResource):
562+
return []
563+
564+
if spec := cls.api_spec:
565+
items = builder.client.list(spec, **kwargs)
566+
collected = cls.collect(items, builder)
567+
568+
resources_by_location = defaultdict(list)
579569

580-
graph_builder.submit_work(service_name, collect_vm_sizes)
570+
for compute_resource in collected:
571+
location = getattr(compute_resource, "location", None)
572+
if location:
573+
resources_by_location[location].append(compute_resource)
581574

575+
# Process each unique location
576+
for location, compute_resources in resources_by_location.items():
577+
log.debug(f"Processing compute resources in location: {location}")
578+
579+
# Collect VM sizes for the compute resources in this location
580+
cls._collect_vm_sizes(builder, location, compute_resources)
581+
582+
if builder.config.collect_usage_metrics:
583+
try:
584+
cls.collect_usage_metrics(builder, collected)
585+
except Exception as e:
586+
log.warning(f"Failed to collect usage metrics for {cls.__name__}: {e}")
587+
588+
return collected
589+
590+
return []
591+
592+
@staticmethod
593+
def _collect_vm_sizes(
594+
graph_builder: GraphBuilder, location: str, compute_resources: List["AzureMachineLearningCompute"]
595+
) -> None:
596+
def collect_vm_sizes() -> None:
597+
api_spec = AzureResourceSpec(
598+
service="machinelearningservices",
599+
version="2024-04-01",
600+
path=f"/subscriptions/{{subscriptionId}}/providers/Microsoft.MachineLearningServices/locations/{location}/vmSizes",
601+
path_parameters=["subscriptionId"],
602+
query_parameters=["api-version"],
603+
access_path="value",
604+
expect_array=True,
605+
)
606+
items = graph_builder.client.list(api_spec)
607+
608+
if not items:
609+
return
610+
611+
# Set location for further connect_in_graph method
612+
for item in items:
613+
item["location"] = location
614+
615+
# Collect the virtual machine sizes
616+
collected_vm_sizes = AzureMachineLearningVirtualMachineSize.collect(items, graph_builder)
617+
618+
for compute_resource in compute_resources:
619+
vm_size = (compute_resource.properties or {}).get("vmSize")
620+
if vm_size:
621+
for size in collected_vm_sizes:
622+
if size.name == vm_size:
623+
graph_builder.add_edge(compute_resource, node=size)
624+
break
625+
626+
graph_builder.submit_work(service_name, collect_vm_sizes)
627+
628+
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
582629
if resource_id := self.id:
583630

584631
def collect_nodes() -> None:

0 commit comments

Comments
 (0)