@@ -43,7 +43,7 @@ def main(
4343 return
4444
4545 api_key = get_api_key (storage_passwords = storage_passwords )
46- tenant_id = get_tenant_id (storage_passwords = storage_passwords )
46+ tenant_ids = get_tenant_ids (storage_passwords = storage_passwords )
4747 ingest_full_event_data = get_ingest_full_event_data (
4848 storage_passwords = storage_passwords
4949 )
@@ -52,36 +52,71 @@ def main(
5252
5353 data_store .set_last_fetch (datetime .now (timezone .utc ))
5454
55- # If the tenant has changed, update the start date so that future requests will be based off today
56- # If you switch tenants, this will avoid the old tenant from ingesting all the events before today and the day
57- # that tenant was switched in the first place.
58- if data_store .get_last_tenant_id () != tenant_id :
59- data_store .set_start_date ((datetime .now (timezone .utc ) - timedelta (days = 30 )))
55+ total_events_fetched_count = 0
6056
61- data_store .set_last_tenant_id (tenant_id )
57+ for tenant_id in tenant_ids :
58+ events_fetched_count = 0
6259
63- events_fetched_count = 0
64- for event , next_token in fetch_feed (
65- logger = logger ,
66- api_key = api_key ,
67- tenant_id = tenant_id ,
68- ingest_full_event_data = ingest_full_event_data ,
69- severities = severities_filter ,
70- source_types = source_types_filter ,
71- flare_api_cls = flare_api_cls ,
72- data_store = data_store ,
73- ):
74- data_store .set_last_fetch (datetime .now (timezone .utc ))
60+ # The earliest ingested date serves as a low water mark to look
61+ # for identifiers 30 days prior to the day a tenant was first configured.
62+ start_date = data_store .get_earliest_ingested_by_tenant (tenant_id )
63+ if not start_date :
64+ start_date = datetime .now (timezone .utc ) - timedelta (days = 30 )
65+ data_store .set_earliest_ingested_by_tenant (tenant_id , start_date )
66+
67+ for event , next_token in fetch_feed (
68+ logger = logger ,
69+ api_key = api_key ,
70+ tenant_id = tenant_id ,
71+ ingest_full_event_data = ingest_full_event_data ,
72+ severities = severities_filter ,
73+ source_types = source_types_filter ,
74+ flare_api_cls = flare_api_cls ,
75+ data_store = data_store ,
76+ ):
77+ data_store .set_last_fetch (datetime .now (timezone .utc ))
78+
79+ data_store .set_next_by_tenant (tenant_id , next_token )
80+
81+ event ["tenant_id" ] = tenant_id
7582
76- data_store .set_next_by_tenant (tenant_id , next_token )
83+ # stdout is picked up by splunk and this is how events
84+ # are ingested after being retrieved from Flare.
85+ print (json .dumps (event ), flush = True )
7786
78- # stdout is picked up by splunk and this is how events
79- # are ingested after being retrieved from Flare.
80- print ( json . dumps ( event ), flush = True )
87+ events_fetched_count += 1
88+ logger . info ( f"Fetched { events_fetched_count } events on tenant { tenant_id } " )
89+ total_events_fetched_count += events_fetched_count
8190
82- events_fetched_count += 1
91+ logger .info (f"Fetched { events_fetched_count } events across all tenants" )
92+
93+
94+ def fetch_feed (
95+ logger : Logger ,
96+ api_key : str ,
97+ tenant_id : int ,
98+ ingest_full_event_data : bool ,
99+ severities : list [str ],
100+ source_types : list [str ],
101+ flare_api_cls : type [FlareAPI ],
102+ data_store : ConfigDataStore ,
103+ ) -> Iterator [tuple [dict , str ]]:
104+ flare_api : FlareAPI = flare_api_cls (api_key = api_key , tenant_id = tenant_id )
83105
84- logger .info (f"Fetched { events_fetched_count } events" )
106+ try :
107+ next = data_store .get_next_by_tenant (tenant_id )
108+ start_date = data_store .get_earliest_ingested_by_tenant (tenant_id )
109+ logger .info (f"Fetching { tenant_id = } , { next = } , { start_date = } " )
110+ for event_next in flare_api .fetch_feed_events (
111+ next = next ,
112+ start_date = start_date ,
113+ ingest_full_event_data = ingest_full_event_data ,
114+ severities = severities ,
115+ source_types = source_types ,
116+ ):
117+ yield event_next
118+ except Exception as e :
119+ logger .error (f"Exception={ e } " )
85120
86121
87122def get_storage_password_value (
@@ -103,18 +138,20 @@ def get_api_key(storage_passwords: client.StoragePasswords) -> str:
103138 return api_key
104139
105140
106- def get_tenant_id (storage_passwords : client .StoragePasswords ) -> int :
107- stored_tenant_id = get_storage_password_value (
108- storage_passwords = storage_passwords , password_key = PasswordKeys .TENANT_ID .value
141+ def get_tenant_ids (storage_passwords : client .StoragePasswords ) -> list [ int ] :
142+ stored_tenant_ids = get_storage_password_value (
143+ storage_passwords = storage_passwords , password_key = PasswordKeys .TENANT_IDS .value
109144 )
110145 try :
111- tenant_id = int (stored_tenant_id ) if stored_tenant_id is not None else None
146+ tenant_ids : Optional [list [int ]] = (
147+ json .loads (stored_tenant_ids ) if stored_tenant_ids else None
148+ )
112149 except Exception :
113150 pass
114151
115- if not tenant_id :
116- raise Exception ("Tenant ID not found" )
117- return tenant_id
152+ if not tenant_ids :
153+ raise Exception ("Tenant IDs not found" )
154+ return tenant_ids
118155
119156
120157def get_ingest_full_event_data (storage_passwords : client .StoragePasswords ) -> bool :
@@ -151,34 +188,6 @@ def get_source_types_filter(storage_passwords: client.StoragePasswords) -> list[
151188 return []
152189
153190
154- def fetch_feed (
155- logger : Logger ,
156- api_key : str ,
157- tenant_id : int ,
158- ingest_full_event_data : bool ,
159- severities : list [str ],
160- source_types : list [str ],
161- flare_api_cls : type [FlareAPI ],
162- data_store : ConfigDataStore ,
163- ) -> Iterator [tuple [dict , str ]]:
164- try :
165- flare_api : FlareAPI = flare_api_cls (api_key = api_key , tenant_id = tenant_id )
166-
167- next = data_store .get_next_by_tenant (tenant_id )
168- start_date = data_store .get_start_date ()
169- logger .info (f"Fetching { tenant_id = } , { next = } , { start_date = } " )
170- for event_next in flare_api .fetch_feed_events (
171- next = next ,
172- start_date = start_date ,
173- ingest_full_event_data = ingest_full_event_data ,
174- severities = severities ,
175- source_types = source_types ,
176- ):
177- yield event_next
178- except Exception as e :
179- logger .error (f"Exception={ e } " )
180-
181-
182191def get_splunk_service (logger : Logger , token : str ) -> client .Service :
183192 try :
184193 splunk_service = client .connect (
0 commit comments