11import logging
22from concurrent .futures import ThreadPoolExecutor
3- from typing import Type , List , Any , Optional
3+ from datetime import datetime , timezone
4+ from typing import Type , List , Any , Optional , cast
45
56from fix_plugin_gcp .config import GcpConfig
67from fix_plugin_gcp .gcp_client import GcpApiSpec
1415 firestore ,
1516 filestore ,
1617 cloudfunctions ,
18+ monitoring ,
1719)
1820from fix_plugin_gcp .resources .base import GcpResource , GcpProject , ExecutorQueue , GraphBuilder , GcpRegion , GcpZone
1921from fix_plugin_gcp .utils import Credentials
2022from fixlib .baseresources import Cloud
2123from fixlib .core .actions import CoreFeedback , ErrorAccumulator
2224from fixlib .graph import Graph
25+ from fixlib .json import value_in_path
26+ from fixlib .types import Json
2327
2428log = logging .getLogger ("fix.plugins.gcp" )
2529all_resources : List [Type [GcpResource ]] = (
@@ -58,6 +62,7 @@ def __init__(
5862 cloud : Cloud ,
5963 project : GcpProject ,
6064 core_feedback : CoreFeedback ,
65+ task_data : Json ,
6166 max_resources_per_account : Optional [int ] = None ,
6267 ) -> None :
6368 self .config = config
@@ -67,6 +72,7 @@ def __init__(
6772 self .error_accumulator = ErrorAccumulator ()
6873 self .graph = Graph (root = self .project , max_nodes = max_resources_per_account )
6974 self .credentials = Credentials .get (self .project .id )
75+ self .task_data = task_data
7076
7177 def collect (self ) -> None :
7278 with ThreadPoolExecutor (
@@ -77,7 +83,20 @@ def collect(self) -> None:
7783 # It should only be used in scenarios, where it is safe to do so.
7884 # This executor is shared between all regions.
7985 shared_queue = ExecutorQueue (executor , self .project .safe_name )
86+
87+ def get_last_run () -> Optional [datetime ]:
88+ td = self .task_data
89+ if not td :
90+ return None
91+ timestamp = value_in_path (td , ["timing" , td .get ("step" , "" ), "started_at" ])
92+
93+ if timestamp is None :
94+ return None
95+
96+ return datetime .fromtimestamp (timestamp , timezone .utc )
97+
8098 project_global_region = GcpRegion .fallback_global_region (self .project )
99+ last_run = get_last_run ()
81100 global_builder = GraphBuilder (
82101 self .graph ,
83102 self .cloud ,
@@ -87,6 +106,8 @@ def collect(self) -> None:
87106 self .core_feedback ,
88107 self .error_accumulator ,
89108 project_global_region ,
109+ config = self .config ,
110+ last_run_started_at = last_run ,
90111 )
91112 global_builder .add_node (project_global_region , {})
92113
@@ -113,6 +134,13 @@ def collect(self) -> None:
113134
114135 self .error_accumulator .report_all (global_builder .core_feedback )
115136
137+ if global_builder .config .collect_usage_metrics :
138+ try :
139+ log .info (f"[GCP:{ self .project .id } ] Collect usage metrics." )
140+ self .collect_usage_metrics (global_builder )
141+ global_builder .executor .wait_for_submitted_work ()
142+ except Exception as e :
143+ log .warning (f"Failed to collect usage metrics in project { self .project .id } : { e } " )
116144 log .info (f"[GCP:{ self .project .id } ] Connect resources and create edges." )
117145 # connect nodes
118146 for node , data in list (self .graph .nodes (data = True )):
@@ -128,9 +156,19 @@ def collect(self) -> None:
128156 if isinstance (node , GcpResource ):
129157 node .post_process_instance (global_builder , data .get ("source" , {}))
130158
159+ global_builder .executor .wait_for_submitted_work ()
160+
131161 self .core_feedback .progress_done (self .project .id , 1 , 1 , context = [self .cloud .id ])
132162 log .info (f"[GCP:{ self .project .id } ] Collecting resources done." )
133163
164+ def collect_usage_metrics (self , builder : GraphBuilder ) -> None :
165+ for resource in builder .graph .nodes :
166+ if isinstance (resource , GcpResource ) and (mq := resource .collect_usage_metrics (builder )):
167+ start_at = builder .created_at - builder .metrics_delta
168+ region = cast (GcpRegion , resource .region ())
169+ rb = builder .for_region (region )
170+ monitoring .GcpMonitoringMetricData .query_for (rb , resource , mq , start_at , builder .created_at )
171+
134172 def remove_unconnected_nodes (self , builder : GraphBuilder ) -> None :
135173 def rm_leaf_nodes (clazz : Any , ignore_kinds : Optional [Type [Any ]] = None ) -> None :
136174 remove_nodes = set ()
0 commit comments