Skip to content

Commit 3410e81

Browse files
1101-1aquamatthias
andauthored
[azure][feat] Add postgres service collection (#2155)
Co-authored-by: Matthias Veit <matthias_veit@yahoo.de>
1 parent 2278030 commit 3410e81

19 files changed

+1653
-350
lines changed

plugins/azure/fix_plugin_azure/collector.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@
3434
AzureNetworkUsage,
3535
resources as network_resources,
3636
)
37-
from fix_plugin_azure.resource.mysql import AzureMysqlCapability, AzureMysqlServerType, resources as mysql_resources
37+
from fix_plugin_azure.resource.mysql import AzureMysqlServerType, resources as mysql_resources
3838
from fix_plugin_azure.resource.sql_server import resources as sql_resources
39+
from fix_plugin_azure.resource.postgresql import (
40+
AzurePostgresqlServerType,
41+
resources as postgresql_resources,
42+
)
3943
from fix_plugin_azure.resource.storage import AzureStorageAccountUsage, AzureStorageSku, resources as storage_resources
4044
from fixlib.baseresources import Cloud, GraphRoot, BaseAccount, BaseRegion
4145
from fixlib.core.actions import CoreFeedback, ErrorAccumulator
@@ -63,6 +67,7 @@ def resource_with_params(clazz: Type[MicrosoftResource], param: str) -> bool:
6367
+ storage_resources
6468
+ sql_resources
6569
+ mysql_resources
70+
+ postgresql_resources
6671
)
6772
all_resources = subscription_resources + graph_resources # defines all resource kinds. used in model check
6873

@@ -233,18 +238,16 @@ def remove_usage_zero_value() -> None:
233238
rm_nodes(AzureVirtualMachineSize, AzureLocation)
234239
rm_nodes(AzureExpressRoutePortsLocation, AzureSubscription)
235240
rm_nodes(AzureNetworkVirtualApplianceSku, AzureSubscription)
236-
rm_nodes(AzureDiskType, AzureLocation)
241+
rm_nodes(AzureDiskType, AzureSubscription)
237242
rm_nodes(AzureStorageSku, AzureLocation)
238-
rm_nodes(AzureMysqlServerType, AzureLocation)
243+
rm_nodes(AzureMysqlServerType, AzureSubscription)
244+
rm_nodes(AzurePostgresqlServerType, AzureSubscription)
239245
remove_usage_zero_value()
240246

241247
def after_collect(self) -> None:
242248
# Filter unnecessary nodes such as AzureDiskTypePricing
243249
nodes_to_remove = []
244-
node_types = (
245-
AzureDiskTypePricing,
246-
AzureMysqlCapability,
247-
)
250+
node_types = (AzureDiskTypePricing,)
248251

249252
for node in self.graph.nodes:
250253
if not isinstance(node, node_types):

plugins/azure/fix_plugin_azure/resource/base.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,29 @@ class AzureUserAssignedIdentity:
473473
resource_id: Optional[str] = field(default=None, metadata={'description': 'The resource ID of the user assigned identity.'}) # fmt: skip
474474

475475

476+
@define(eq=False, slots=False)
477+
class AzureUserIdentity:
478+
kind: ClassVar[str] = "azure_user_identity"
479+
mapping: ClassVar[Dict[str, Bender]] = {"client_id": S("clientId"), "principal_id": S("principalId")}
480+
client_id: Optional[str] = field(default=None, metadata={'description': 'the client identifier of the Service Principal which this identity represents.'}) # fmt: skip
481+
principal_id: Optional[str] = field(default=None, metadata={'description': 'the object identifier of the Service Principal which this identity represents.'}) # fmt: skip
482+
483+
484+
@define(eq=False, slots=False)
485+
class AzureResourceIdentity:
486+
kind: ClassVar[str] = "azure_resource_identity"
487+
mapping: ClassVar[Dict[str, Bender]] = {
488+
"principal_id": S("principalId"),
489+
"tenant_id": S("tenantId"),
490+
"type": S("type"),
491+
"user_assigned_identities": S("userAssignedIdentities"),
492+
}
493+
principal_id: Optional[str] = field(default=None, metadata={'description': 'The Azure Active Directory principal id.'}) # fmt: skip
494+
tenant_id: Optional[str] = field(default=None, metadata={"description": "The Azure Active Directory tenant id."})
495+
type: Optional[str] = field(default=None, metadata={'description': 'The identity type. Set this to SystemAssigned in order to automatically create and assign an Azure Active Directory principal for the resource.'}) # fmt: skip
496+
user_assigned_identities: Optional[Dict[str, AzureUserIdentity]] = field(default=None, metadata={'description': 'The resource ids of the user assigned identities to use'}) # fmt: skip
497+
498+
476499
@define(eq=False, slots=False)
477500
class AzurePrincipalClient:
478501
kind: ClassVar[str] = "azure_principal_client"
@@ -589,6 +612,30 @@ class AzureSystemData:
589612
last_modified_by_type: Optional[str] = field(default=None, metadata={'description': 'The type of identity that last modified the resource.'}) # fmt: skip
590613

591614

615+
@define(eq=False, slots=False)
616+
class AzureTrackedResource:
617+
kind: ClassVar[str] = "azure_tracked_resource"
618+
mapping: ClassVar[Dict[str, Bender]] = {
619+
"location": S("location"),
620+
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
621+
"type": S("type"),
622+
}
623+
location: Optional[str] = field(default=None, metadata={'description': 'The geo-location where the resource lives'}) # fmt: skip
624+
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
625+
type: Optional[str] = field(default=None, metadata={'description': 'The type of the resource. E.g. Microsoft.Compute/virtualMachines or Microsoft.Storage/storageAccounts '}) # fmt: skip
626+
627+
628+
@define(eq=False, slots=False)
629+
class AzureProxyResource:
630+
kind: ClassVar[str] = "azure_proxy_resource"
631+
mapping: ClassVar[Dict[str, Bender]] = {
632+
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
633+
"type": S("type"),
634+
}
635+
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
636+
type: Optional[str] = field(default=None, metadata={'description': 'The type of the resource. E.g. Microsoft.Compute/virtualMachines or Microsoft.Storage/storageAccounts '}) # fmt: skip
637+
638+
592639
@define(eq=False, slots=False)
593640
class AzureSku:
594641
kind: ClassVar[str] = "azure_sku"

plugins/azure/fix_plugin_azure/resource/compute.py

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from fix_plugin_azure.azure_client import AzureResourceSpec
88
from fix_plugin_azure.resource.base import (
9+
AzureProxyResource,
910
MicrosoftResource,
1011
MicrosoftResourceType,
1112
GraphBuilder,
@@ -779,16 +780,7 @@ class AzureDiskTypePricing(MicrosoftResource):
779780
@define(eq=False, slots=False)
780781
class AzureDiskType(MicrosoftResource, BaseVolumeType):
781782
kind: ClassVar[str] = "azure_disk_type"
782-
# Define api spec to collect as regional resources
783-
api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec(
784-
service="compute",
785-
version="2023-01-01-preview",
786-
path="",
787-
path_parameters=["subscriptionId", "location"],
788-
query_parameters=[],
789-
access_path="Items",
790-
expect_array=True,
791-
)
783+
# Collect via AzureDisk()
792784
mapping: ClassVar[Dict[str, Bender]] = {
793785
"id": S("skuName"),
794786
"name": S("skuName"),
@@ -951,29 +943,6 @@ def create_unique_disk_sizes(collected_disks: List[MicrosoftResourceType], build
951943
seen_hashes.add(hash_value)
952944
AzureDiskType.collect(disk_sizes, builder)
953945

954-
@classmethod
955-
def collect_resources(
956-
cls: Type[MicrosoftResourceType], builder: GraphBuilder, **kwargs: Any
957-
) -> List[MicrosoftResourceType]:
958-
log.debug(f"[Azure:{builder.account.id}] Collecting {cls.__name__} with ({kwargs})")
959-
product_names = {"Standard SSD Managed Disks", "Premium SSD Managed Disks", "Standard HDD Managed Disks"}
960-
sku_items = []
961-
for product_name in product_names:
962-
api_spec = AzureResourceSpec(
963-
service="compute",
964-
version="2023-01-01-preview",
965-
path=f"https://prices.azure.com/api/retail/prices?$filter=productName eq '{product_name}' and armRegionName eq "
966-
+ "'{location}' and unitOfMeasure eq '1/Month' and serviceFamily eq 'Storage' and type eq 'Consumption' and isPrimaryMeterRegion eq true",
967-
path_parameters=["location"],
968-
query_parameters=["api-version"],
969-
access_path="Items",
970-
expect_array=True,
971-
)
972-
973-
items = builder.client.list(api_spec, **kwargs)
974-
sku_items.extend(items)
975-
return cls.collect(sku_items, builder)
976-
977946

978947
VolumeStatusMapping = {
979948
"ActiveSAS": VolumeStatus.IN_USE,
@@ -1109,6 +1078,34 @@ def collect_resources(
11091078
return collected
11101079
return []
11111080

1081+
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
1082+
if location := self.location:
1083+
1084+
def collect_disk_types() -> None:
1085+
log.debug(f"[Azure:{graph_builder.account.id}] Collecting AzureDiskType")
1086+
product_names = {
1087+
"Standard SSD Managed Disks",
1088+
"Premium SSD Managed Disks",
1089+
"Standard HDD Managed Disks",
1090+
}
1091+
sku_items = []
1092+
for product_name in product_names:
1093+
api_spec = AzureResourceSpec(
1094+
service="compute",
1095+
version="2023-01-01-preview",
1096+
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",
1097+
path_parameters=[],
1098+
query_parameters=["api-version"],
1099+
access_path="Items",
1100+
expect_array=True,
1101+
)
1102+
1103+
items = graph_builder.client.list(api_spec)
1104+
sku_items.extend(items)
1105+
AzureDiskType.collect(sku_items, graph_builder)
1106+
1107+
graph_builder.submit_work(service_name, collect_disk_types)
1108+
11121109
@classmethod
11131110
def collect_usage_metrics(
11141111
cls: Type[MicrosoftResource], builder: GraphBuilder, collected_resources: List[MicrosoftResourceType]
@@ -1584,15 +1581,6 @@ class AzureRestorePointCollectionSourceProperties:
15841581
location: Optional[str] = field(default=None, metadata={'description': 'Location of the source resource used to create this restore point collection.'}) # fmt: skip
15851582

15861583

1587-
@define(eq=False, slots=False)
1588-
class AzureProxyResource:
1589-
kind: ClassVar[str] = "azure_proxy_resource"
1590-
mapping: ClassVar[Dict[str, Bender]] = {"id": S("id"), "name": S("name"), "type": S("type")}
1591-
id: Optional[str] = field(default=None, metadata={"description": "Resource id."})
1592-
name: Optional[str] = field(default=None, metadata={"description": "Resource name."})
1593-
type: Optional[str] = field(default=None, metadata={"description": "Resource type."})
1594-
1595-
15961584
@define(eq=False, slots=False)
15971585
class AzureVMSizeProperties:
15981586
kind: ClassVar[str] = "azure_vm_size_properties"
@@ -2947,6 +2935,29 @@ def collect_instance_status() -> None:
29472935
if not instance_status_set:
29482936
self.instance_status = InstanceStatus.UNKNOWN
29492937

2938+
if location := self.location:
2939+
2940+
def collect_vm_sizes() -> None:
2941+
api_spec = AzureResourceSpec(
2942+
service="compute",
2943+
version="2023-03-01",
2944+
path="/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/"
2945+
+ f"{location}/vmSizes",
2946+
path_parameters=["subscriptionId"],
2947+
query_parameters=["api-version"],
2948+
access_path="value",
2949+
expect_array=True,
2950+
)
2951+
items = graph_builder.client.list(api_spec)
2952+
if not items:
2953+
return
2954+
# Set location for further connect_in_graph method
2955+
for item in items:
2956+
item["location"] = location
2957+
AzureVirtualMachineSize.collect(items, graph_builder)
2958+
2959+
graph_builder.submit_work(service_name, collect_vm_sizes)
2960+
29502961
graph_builder.submit_work(service_name, collect_instance_status)
29512962

29522963
@classmethod
@@ -3663,15 +3674,6 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
36633674
@define(eq=False, slots=False)
36643675
class AzureVirtualMachineSize(MicrosoftResource, BaseInstanceType):
36653676
kind: ClassVar[str] = "azure_virtual_machine_size"
3666-
api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec(
3667-
service="compute",
3668-
version="2023-03-01",
3669-
path="/subscriptions/{subscriptionId}/providers/Microsoft.Compute/locations/{location}/vmSizes",
3670-
path_parameters=["subscriptionId", "location"],
3671-
query_parameters=["api-version"],
3672-
access_path="value",
3673-
expect_array=True,
3674-
)
36753677
mapping: ClassVar[Dict[str, Bender]] = {
36763678
"id": S("name"),
36773679
"tags": S("tags", default={}),
@@ -3684,6 +3686,7 @@ class AzureVirtualMachineSize(MicrosoftResource, BaseInstanceType):
36843686
"instance_type": S("name"),
36853687
"instance_cores": S("numberOfCores"),
36863688
"instance_memory": S("memoryInMB") >> F(lambda x: int(x) / 1024),
3689+
"location": S("location"),
36873690
}
36883691
_is_provider_link: ClassVar[bool] = False
36893692
max_data_disk_count: Optional[int] = field(default=None, metadata={'description': 'The maximum number of data disks that can be attached to the virtual machine size.'}) # fmt: skip
@@ -3693,9 +3696,6 @@ class AzureVirtualMachineSize(MicrosoftResource, BaseInstanceType):
36933696
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
36943697
location: Optional[str] = field(default=None, metadata={"description": "Resource location."})
36953698

3696-
def pre_process(self, graph_builder: GraphBuilder, source: Json) -> None:
3697-
self.location = graph_builder.location.name if graph_builder.location else ""
3698-
36993699
def after_collect(self, builder: GraphBuilder, source: Json) -> None:
37003700
if (location := self.location) and (sku_name := self.name):
37013701
api_spec = AzureResourceSpec(

plugins/azure/fix_plugin_azure/resource/containerservice.py

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
from fix_plugin_azure.azure_client import AzureResourceSpec
88
from fix_plugin_azure.resource.base import (
9+
AzureTrackedResource,
910
MicrosoftResource,
10-
AzureSystemData,
1111
GraphBuilder,
1212
AzureExtendedLocation,
1313
AzureUserAssignedIdentity,
@@ -22,25 +22,6 @@
2222
log = logging.getLogger("fix.plugins.azure")
2323

2424

25-
@define(eq=False, slots=False)
26-
class AzureTrackedResource:
27-
kind: ClassVar[str] = "azure_tracked_resource"
28-
mapping: ClassVar[Dict[str, Bender]] = {
29-
"id": S("id"),
30-
"location": S("location"),
31-
"name": S("name"),
32-
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
33-
"tags": S("tags"),
34-
"type": S("type"),
35-
}
36-
id: Optional[str] = field(default=None, metadata={'description': 'Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}'}) # fmt: skip
37-
location: Optional[str] = field(default=None, metadata={'description': 'The geo-location where the resource lives'}) # fmt: skip
38-
name: Optional[str] = field(default=None, metadata={"description": "The name of the resource"})
39-
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
40-
tags: Optional[Dict[str, str]] = field(default=None, metadata={"description": "Resource tags."})
41-
type: Optional[str] = field(default=None, metadata={'description': 'The type of the resource. E.g. Microsoft.Compute/virtualMachines or Microsoft.Storage/storageAccounts '}) # fmt: skip
42-
43-
4425
@define(eq=False, slots=False)
4526
class AzureAPIServerAccessProfile:
4627
kind: ClassVar[str] = "azure_api_server_access_profile"
@@ -915,21 +896,6 @@ def _get_poolnames_and_vmss_ids(self, builder: GraphBuilder) -> List[Tuple[str,
915896
]
916897

917898

918-
@define(eq=False, slots=False)
919-
class AzureProxyResource:
920-
kind: ClassVar[str] = "azure_proxy_resource"
921-
mapping: ClassVar[Dict[str, Bender]] = {
922-
"id": S("id"),
923-
"name": S("name"),
924-
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
925-
"type": S("type"),
926-
}
927-
id: Optional[str] = field(default=None, metadata={'description': 'Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}'}) # fmt: skip
928-
name: Optional[str] = field(default=None, metadata={"description": "The name of the resource"})
929-
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
930-
type: Optional[str] = field(default=None, metadata={'description': 'The type of the resource. E.g. Microsoft.Compute/virtualMachines or Microsoft.Storage/storageAccounts '}) # fmt: skip
931-
932-
933899
@define(eq=False, slots=False)
934900
class AzureCompatibleVersions:
935901
kind: ClassVar[str] = "azure_compatible_versions"

0 commit comments

Comments
 (0)