Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[azure] [feat] Filter virtual machine sizes #1852

Merged
merged 7 commits into from Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugins/azure/resoto_plugin_azure/azure_client.py
Expand Up @@ -153,7 +153,7 @@ def _call(self, spec: AzureApiSpec, **kwargs: Any) -> List[Json]:
if lookup_map.get(param, None) is not None:
path_map[param] = lookup_map[param]
else:
raise ValueError(f"Param {param} in lookup_map does not found")
raise KeyError(f"Path parameter {param} was not provided as argument")

# Construct parameters
params = case_insensitive_dict()
Expand All @@ -163,7 +163,7 @@ def _call(self, spec: AzureApiSpec, **kwargs: Any) -> List[Json]:
if lookup_map.get(param, None) is not None:
params[param] = ser.query(param, lookup_map[param], "str") # type: ignore # noqa: E501
else:
raise ValueError(f"Param {param} in lookup_map does not found")
raise KeyError(f"Query parameter {param} was not provided as argument")

# Construct url
path = spec.path.format_map(path_map)
Expand Down
36 changes: 33 additions & 3 deletions plugins/azure/resoto_plugin_azure/collector.py
@@ -1,12 +1,15 @@
import logging
from concurrent.futures import ThreadPoolExecutor, Future
from typing import Type, Set, List

from typing import Any, Optional, Type, Set, List

from resoto_plugin_azure.config import AzureConfig, AzureCredentials
from resoto_plugin_azure.azure_client import AzureClient
from resoto_plugin_azure.resource.compute import resources as compute_resources
from resoto_plugin_azure.resource.compute import (
AzureVirtualMachineSize,
resources as compute_resources,
)
from resoto_plugin_azure.resource.base import (
AzureLocation,
AzureSubscription,
GraphBuilder,
AzureResource,
Expand Down Expand Up @@ -78,6 +81,8 @@ def collect(self) -> None:
raise Exception(f"Only Azure resources expected, but got {node}")
# wait for all work to finish
queue.wait_for_submitted_work()
# filter nodes
self.filter_nodes()
self.core_feedback.progress_done(self.subscription.subscription_id, 1, 1, context=[self.cloud.id])
log.info(f"[Azure:{self.subscription.safe_name}] Collecting resources done.")

Expand All @@ -99,3 +104,28 @@ def work_done(_: Future[None]) -> None:
all_done = GatherFutures.all(group_futures)
all_done.add_done_callback(work_done)
return all_done

def filter_nodes(self) -> None:
remove_nodes = []

def rm_nodes(cls, ignore_kinds: Optional[Type[Any]] = None) -> None: # type: ignore
for node in self.graph.nodes:
if not isinstance(node, cls):
continue
pred = list(self.graph.predecessors(node))
if ignore_kinds is not None:
pred = [p for p in pred if not isinstance(p, ignore_kinds)]

if not pred:
remove_nodes.extend(pred)
remove_nodes.append(node)
removed = set()
for node in remove_nodes:
if node in removed:
continue
removed.add(node)
self.graph.remove_node(node)
log.debug(f"Removing {len(remove_nodes)} unreferenced nodes of type {cls}")
remove_nodes.clear()

rm_nodes(AzureVirtualMachineSize, AzureLocation)
21 changes: 15 additions & 6 deletions plugins/azure/resoto_plugin_azure/resource/base.py
Expand Up @@ -142,14 +142,19 @@ def fetch_resources(
path: str,
path_parameters: List[str],
query_parameters: List[str],
compared_property: Callable[[Json], Union[List[str], str]],
binding_property: Callable[[Json], Union[List[str], str]],
first_property: Callable[[Json], Union[List[str], str]],
second_property: Callable[[Json], Union[List[str], str]],
) -> List[Tuple[Union[str, List[str]], Union[str, List[str]]]]:
"""
Fetch additional resources from the Azure API for further connection using the connect_in_graph method.
Fetch additional resources from the Azure API.
Can used for further connection using the connect_in_graph method.

Parameters:
- first_property: Compared property.
- second_property: Binding property | Compared property.

Returns:
List[Tuple[Union[str, List[str]], str]]: A list of tuples containing information to compare and connect the retrieved resources.
List[Tuple[Union[str, List[str]], Union[str, List[str]]]]: A list of tuples containing information to compare and connect the retrieved resources.
"""
resources_api_spec = AzureApiSpec(
service=service,
Expand All @@ -161,7 +166,7 @@ def fetch_resources(
expect_array=True,
)

return [(compared_property(r), binding_property(r)) for r in builder.client.list(resources_api_spec)]
return [(first_property(r), second_property(r)) for r in builder.client.list(resources_api_spec)]

@classmethod
def collect_resources(
Expand All @@ -176,7 +181,11 @@ def collect_resources(
return []

@classmethod
def collect(cls: Type[AzureResourceType], raw: List[Json], builder: GraphBuilder) -> List[AzureResourceType]:
def collect(
cls: Type[AzureResourceType],
raw: List[Json],
builder: GraphBuilder,
) -> List[AzureResourceType]:
# Default behavior: iterate over json snippets and for each:
# - bend the json
# - transform the result into a resource
Expand Down
18 changes: 12 additions & 6 deletions plugins/azure/resoto_plugin_azure/resource/compute.py
Expand Up @@ -2529,11 +2529,7 @@ class AzureVirtualMachine(AzureResource, BaseInstance):
]
},
"successors": {
"default": [
"azure_image",
"azure_disk",
"azure_network_interface",
]
"default": ["azure_image", "azure_disk", "azure_network_interface", "azure_virtual_machine_size"]
},
}
mapping: ClassVar[Dict[str, Bender]] = {
Expand Down Expand Up @@ -2578,6 +2574,7 @@ class AzureVirtualMachine(AzureResource, BaseInstance):
"user_data": S("properties", "userData"),
"virtual_machine_scale_set": S("properties", "virtualMachineScaleSet", "id"),
"vm_id": S("properties", "vmId"),
"location": S("location"),
"instance_type": S("properties", "hardwareProfile", "vmSize"),
"instance_status": S("properties", "provisioningState")
>> MapEnum(InstanceStatusMapping, default=InstanceStatus.UNKNOWN),
Expand Down Expand Up @@ -2612,6 +2609,7 @@ class AzureVirtualMachine(AzureResource, BaseInstance):
user_data: Optional[str] = field(default=None, metadata={'description': 'Userdata for the vm, which must be base-64 encoded. Customer should not pass any secrets in here. Minimum api-version: 2021-03-01.'}) # fmt: skip
virtual_machine_scale_set: Optional[str] = field(default=None, metadata={"description": ""})
vm_id: Optional[str] = field(default=None, metadata={'description': 'Specifies the vm unique id which is a 128-bits identifier that is encoded and stored in all azure iaas vms smbios and can be read using platform bios commands.'}) # fmt: skip
location: Optional[str] = field(default=None, metadata={"description": "Resource location."})

def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
if placement_group_id := self.proximity_placement_group:
Expand Down Expand Up @@ -2666,6 +2664,10 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
for network_interface in network_interfaces:
if ni_id := network_interface.id:
builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureNetworkInterface, id=ni_id)
if (vms_type := self.instance_type) and (vm_location := self.location):
builder.add_edge(
self, edge_type=EdgeType.default, clazz=AzureVirtualMachineSize, name=vms_type, location=vm_location
)


@define(eq=False, slots=False)
Expand Down Expand Up @@ -3205,7 +3207,7 @@ class AzureVirtualMachineSize(AzureResource, BaseInstanceType):
service="compute",
version="2023-03-01",
path="/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/{location}/vmSizes",
path_parameters=["location", "subscriptionId"],
path_parameters=["subscriptionId", "location"],
query_parameters=["api-version"],
access_path="value",
expect_array=True,
Expand All @@ -3231,6 +3233,10 @@ class AzureVirtualMachineSize(AzureResource, BaseInstanceType):
number_of_cores: Optional[int] = field(default=None, metadata={'description': 'The number of cores supported by the virtual machine size. For constrained vcpu capable vm sizes, this number represents the total vcpus of quota that the vm uses. For accurate vcpu count, please refer to https://docs. Microsoft. Com/azure/virtual-machines/constrained-vcpu or https://docs. Microsoft. Com/rest/api/compute/resourceskus/list.'}) # fmt: skip
os_disk_size_in_mb: Optional[int] = field(default=None, metadata={'description': 'The os disk size, in mb, allowed by the virtual machine size.'}) # fmt: skip
resource_disk_size_in_mb: Optional[int] = field(default=None, metadata={'description': 'The resource disk size, in mb, allowed by the virtual machine size.'}) # fmt: skip
location: Optional[str] = field(default=None, metadata={"description": "Resource location."})

def pre_process(self, graph_builder: GraphBuilder, source: Json) -> None:
self.location = graph_builder.location.name if graph_builder.location else ""
1101-1 marked this conversation as resolved.
Show resolved Hide resolved


resources: List[Type[AzureResource]] = [
Expand Down