@@ -95,7 +95,55 @@ def job_maint_remove_old_partitions(*args, **kwargs):
9595 c .execute (f"DROP TABLE { tablename } ;" )
9696 else :
9797 log .info (f"MAINT: Leaving { tablename } (today is { today_seq } )" )
98- log .info ("MAINT: Maintenance finished." )
98+ log .info ("MAINT: Maintenance finished (removing old partitions)." )
99+
100+
101+ def job_maint_suggest_entities (* args , ** job_params ):
102+ log .info ("MAINT: Maintenance started - making suggestions for device entities" )
103+
104+ backend_url = job_params ['backend_url' ]
105+ bot_token = job_params ['bot_token' ]
106+ requests_session = requests .Session ()
107+
108+ # for each account, add any new netflow exporters (entities) that might not exist yet:
109+ # find all the accounts we have access to:
110+ r = requests_session .get (f'{ backend_url } /accounts/?b={ bot_token } ' )
111+ if r .status_code != 200 :
112+ raise Exception ("Invalid bot token or network error, got status {} while retrieving {}/accounts" .format (r .status_code , backend_url ))
113+ j = r .json ()
114+ accounts_ids = [a ["id" ] for a in j ["list" ]]
115+
116+ # find all entities for each of the accounts:
117+ for account_id in accounts_ids :
118+ r = requests_session .get ('{}/accounts/{}/entities/?b={}' .format (backend_url , account_id , bot_token ))
119+ if r .status_code != 200 :
120+ raise Exception ("Network error, got status {} while retrieving {}/accounts/{}/entities" .format (r .status_code , backend_url , account_id ))
121+ j = r .json ()
122+ entities_ips = [e ["details" ]["ipv4" ] for e in j ["list" ] if e ["entity_type" ] == "device" ]
123+
124+ with get_db_cursor () as c :
125+ # Ideally, we would just run "select distinct(client_ip) from netflow_flows;", but unfortunately
126+ # I was unable to find a performant way to run this query. So we are using netflow_exporters:
127+ c .execute (f"SELECT ip FROM { DB_PREFIX } exporters;" )
128+ for client_ip , in c .fetchall ():
129+ if client_ip in entities_ips :
130+ log .info (f"MAINT: We already know exporter [{ client_ip } ]" )
131+ continue
132+
133+ log .info (f"MAINT: Unknown exporter found, inserting [{ client_ip } ] to account [{ account_id } ]" )
134+ url = f'{ backend_url } /accounts/{ account_id } /entities/?b={ bot_token } '
135+ params = {
136+ "name" : f'{ client_ip } (NetFlow exporter)' ,
137+ "entity_type" : "device" ,
138+ "details" : {
139+ "ipv4" : client_ip ,
140+ },
141+ }
142+ r = requests_session .post (url , json = params )
143+ if r .status_code > 299 :
144+ raise Exception ("Network error, got status {} while posting to {}/accounts/{}/entities: {}" .format (r .status_code , backend_url , account_id , r .content ))
145+
146+ log .info ("MAINT: Maintenance finished (device entities suggestions)." )
99147
100148
101149class NetFlowBot (Collector ):
@@ -105,6 +153,14 @@ def jobs(self):
105153 job_id = 'maint/remove_old_data'
106154 yield job_id , [3600 ], job_maint_remove_old_partitions , {}, 50
107155
156+ # suggest new netflow exporters / entities:
157+ job_id = f'maint/suggest_entities'
158+ job_params = {
159+ "backend_url" : self .backend_url ,
160+ "bot_token" : self .bot_token ,
161+ }
162+ yield job_id , [2 * 60 ], job_maint_suggest_entities , job_params , 10
163+
108164 # first merge together entity infos so that those entities from the same account are together:
109165 accounts_infos = defaultdict (list )
110166 for entity_info in self .fetch_job_configs ('netflow' ):
@@ -157,6 +213,8 @@ def perform_account_aggr_job(*args, **job_params):
157213 if last_used_ts is None :
158214 log .info (f"Counter was not yet initialized for job { job_id } , skipping." )
159215 return
216+
217+ # WATCH OUT! This hack changes all of the units from Bps to B! (should be cleaned up)
160218 #time_between = float(max_ts - last_used_ts)
161219 time_between = 1 # we want to use bytes as unit, not bytes per second
162220
0 commit comments