66import time
77import logging
88from datetime import date , datetime , timedelta
9+ from collections import defaultdict
910from dateutil .relativedelta import relativedelta
1011from botocore .exceptions import NoCredentialsError , ClientError
1112
13+ from .utils_db import connect , load_data
14+
1215logger = logging .getLogger ("core.engine.aws" )
1316
1417def aws_api_call_with_retry (client , function_name , parameters , max_retries , retry_delay ):
@@ -59,19 +62,20 @@ def build_aws_resource_inventory(cloud_service_provider, provider_details, repor
5962 region_name = region
6063 )
6164
62- # Load the ResourceType mapping to include both `id` and `name`
63- with open ("datasets/resourcetype.json" , "r" , encoding = "utf-8" ) as f :
64- resource_type_mapping = {
65- item ["code" ]: {"id" : item ["id" ], "name" : item ["name" ]}
66- for item in json .load (f )
67- if item ["csp" ] == "2" and item ["status" ] == "t"
68- }
65+ db_path = os .path .join (report_path , "data" , "assessment.db" )
66+
67+ # Load the ResourceType mapping
68+ resource_type_mapping = {
69+ item ["code" ]: {"id" : item ["id" ], "name" : item ["name" ]}
70+ for item in load_data ("resourcetype" )
71+ if item ["csp" ] == 2 and item ["status" ] == "t"
72+ }
6973
70- resource_summary = {}
74+ # Save raw data for debugging and auditing purposes
7175 raw_data = []
7276
73- # Initialize a custom counter
74- resource_inventory_id_counter = 1
77+ # Aggregate resources by type and location
78+ aggregated_resources = defaultdict ( int )
7579
7680 # Iterate through each resource type in the JSON
7781 for idx , (resource_type_code , resource_info ) in enumerate (resource_type_mapping .items (), start = 1 ):
@@ -108,17 +112,9 @@ def build_aws_resource_inventory(cloud_service_provider, provider_details, repor
108112 #logger.warning(f"No valid response found for {service_name} operation {operation_name}. Skipping.")
109113 continue
110114
111- # Count resources and add to summary if count > 0
112- resource_count = len (resources )
113- if resource_count > 0 :
114- resource_inventory_id = str (resource_inventory_id_counter )
115- resource_summary [resource_inventory_id ] = {
116- "resource_name" : resource_info ["name" ],
117- "resource_type" : resource_info ["id" ],
118- "location" : region ,
119- "count" : resource_count
120- }
121- resource_inventory_id_counter += 1
115+ # Aggregate the resources
116+ for resource in resources :
117+ aggregated_resources [(resource_type_code , region )] += 1
122118
123119 # Store raw data
124120 raw_data .append ({
@@ -127,9 +123,6 @@ def build_aws_resource_inventory(cloud_service_provider, provider_details, repor
127123 "resources" : resources
128124 })
129125
130-
131- #logger.info(f"Processed {resource_count} resources for service {service_name} with operation {operation_name}")
132-
133126 except (NoCredentialsError , ClientError , Exception ) as e :
134127 #logger.error(f"Error while processing {service_name}: {str(e)}", exc_info=True)
135128 continue
@@ -140,13 +133,35 @@ def build_aws_resource_inventory(cloud_service_provider, provider_details, repor
140133 raw_file_path = os .path .join (raw_data_path , "resource_inventory_raw_data.json" )
141134 with open (raw_file_path , "w" , encoding = "utf-8" ) as raw_file :
142135 json .dump (raw_data , raw_file , indent = 4 )
143- #logger.info(f"AWS raw resource inventory saved to {raw_file_path}")
144136
145- # Save structured data to a JSON file
146- structured_file_path = os .path .join (report_path , "resource_inventory_standard_data.json" )
147- with open (structured_file_path , "w" , encoding = "utf-8" ) as structured_file :
148- json .dump (resource_summary , structured_file , indent = 4 )
149- #logger.info(f"AWS structured resource inventory saved to {structured_file_path}")
137+ # Insert aggregated data into SQLite
138+ with connect (db_path = db_path ) as conn :
139+ cursor = conn .cursor ()
140+
141+ for (resource_type_code , resource_location ), resource_count in aggregated_resources .items ():
142+ try :
143+ # Map resource type code to resource_type_id
144+ resource_info = resource_type_mapping .get (resource_type_code )
145+ if not resource_info :
146+ #logger.warning(f"Resource type {resource_type_code} not found in resourcetype mapping. Skipping.")
147+ continue
148+
149+ resource_type_id = resource_info ["id" ]
150+
151+ cursor .execute (
152+ """
153+ INSERT INTO resource_inventory (resource_type, location, count)
154+ VALUES (?, ?, ?)
155+ ON CONFLICT(resource_type, location) DO UPDATE SET count = excluded.count
156+ """ ,
157+ (resource_type_id , resource_location , resource_count )
158+ )
159+ except sqlite3 .Error as e :
160+ logger .error (f"SQLite error while processing aggregated resource: { e } " , exc_info = True )
161+ except Exception as e :
162+ logger .error (f"Unexpected error while processing aggregated resource: { e } " , exc_info = True )
163+
164+ conn .commit ()
150165
151166 except Exception as e :
152167 logger .error (f"Error creating AWS resource inventory: { str (e )} " , exc_info = True )
@@ -172,6 +187,8 @@ def build_aws_cost_inventory(cloud_service_provider, provider_details, report_pa
172187 )
173188 cost_explorer = session .client ('ce' , region_name = 'us-east-1' )
174189
190+ db_path = os .path .join (report_path , "data" , "assessment.db" )
191+
175192 end_time = date .today ()
176193 start_time = end_time .replace (day = 1 ) - timedelta (days = 180 )
177194 start_time = start_time .replace (day = 1 )
@@ -189,24 +206,54 @@ def build_aws_cost_inventory(cloud_service_provider, provider_details, report_pa
189206 }
190207 )
191208
192- cost_inventory_raw_path = os .path .join (report_path , "cost_inventory_raw_data.json" )
209+ cost_inventory_raw_path = os .path .join (raw_data_path , "cost_inventory_raw_data.json" )
193210 with open (cost_inventory_raw_path , "w" , encoding = "utf-8" ) as raw_file :
194211 json .dump (cost_and_usage , raw_file , indent = 4 )
195212
196- structured_costs = {}
197- for result in cost_and_usage ['ResultsByTime' ]:
198- month_str = result ['TimePeriod' ]['Start' ]
199- total_cost = sum (float (group ['Metrics' ]['UnblendedCost' ]['Amount' ]) for group in result ['Groups' ])
200- currency = result ['Groups' ][0 ]['Metrics' ]['UnblendedCost' ]['Unit' ] if result ['Groups' ] else 'USD'
201- structured_costs [month_str ] = {"cost" : total_cost , "currency" : currency }
202-
203- missing_months = get_missing_months_aws (structured_costs .keys (), 6 )
204- for missing_month in missing_months :
205- structured_costs [missing_month .isoformat ()] = {"cost" : 0.00 , "currency" : currency }
206-
207- cost_inventory_standard_path = os .path .join (report_path , "cost_inventory_standard_data.json" )
208- with open (cost_inventory_standard_path , "w" , encoding = "utf-8" ) as structured_file :
209- json .dump (structured_costs , structured_file , indent = 4 )
213+ # Insert structured data into SQLite
214+ with connect (db_path = db_path ) as conn :
215+ cursor = conn .cursor ()
216+
217+ for result in cost_and_usage ['ResultsByTime' ]:
218+ month_str = result ['TimePeriod' ]['Start' ]
219+ total_cost = sum (float (group ['Metrics' ]['UnblendedCost' ]['Amount' ]) for group in result ['Groups' ])
220+ currency = result ['Groups' ][0 ]['Metrics' ]['UnblendedCost' ]['Unit' ] if result ['Groups' ] else 'USD'
221+ month_date = datetime .strptime (month_str , '%Y-%m-%d' ).date ().replace (day = 1 ).isoformat ()
222+
223+ # Insert or update the cost data for the month
224+ cursor .execute (
225+ """
226+ INSERT INTO cost_inventory (month, cost, currency)
227+ VALUES (?, ?, ?)
228+ ON CONFLICT(month) DO UPDATE SET
229+ cost = excluded.cost,
230+ currency = excluded.currency
231+ """ ,
232+ (month_date , total_cost , currency )
233+ )
234+
235+ # Handle missing months
236+ structured_months = {datetime .strptime (result ['TimePeriod' ]['Start' ], '%Y-%m-%d' ).date () for result in cost_and_usage ['ResultsByTime' ]}
237+ missing_months = get_missing_months_aws ({month .isoformat () for month in structured_months }, 6 )
238+
239+ for missing_month in missing_months :
240+ cursor .execute (
241+ """
242+ INSERT INTO cost_inventory (month, cost, currency)
243+ VALUES (?, 0.00, ?)
244+ ON CONFLICT(month) DO UPDATE SET
245+ currency = excluded.currency
246+ """ ,
247+ (missing_month .isoformat (), currency )
248+ )
249+
250+ conn .commit ()
251+
252+ except sqlite3 .Error as e :
253+ logger .error (f"SQLite error: { str (e )} " , exc_info = True )
254+ except Exception as e :
255+ logger .error (f"Error creating AWS cost inventory: { str (e )} " , exc_info = True )
256+ raise
210257
211258 except Exception as e :
212259 logger .error (f"Error creating AWS cost inventory: { str (e )} " , exc_info = True )
0 commit comments