@@ -95,7 +95,55 @@ def job_maint_remove_old_partitions(*args, **kwargs):
95
95
c .execute (f"DROP TABLE { tablename } ;" )
96
96
else :
97
97
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)." )
99
147
100
148
101
149
class NetFlowBot (Collector ):
@@ -105,6 +153,14 @@ def jobs(self):
105
153
job_id = 'maint/remove_old_data'
106
154
yield job_id , [3600 ], job_maint_remove_old_partitions , {}, 50
107
155
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
+
108
164
# first merge together entity infos so that those entities from the same account are together:
109
165
accounts_infos = defaultdict (list )
110
166
for entity_info in self .fetch_job_configs ('netflow' ):
@@ -157,6 +213,8 @@ def perform_account_aggr_job(*args, **job_params):
157
213
if last_used_ts is None :
158
214
log .info (f"Counter was not yet initialized for job { job_id } , skipping." )
159
215
return
216
+
217
+ # WATCH OUT! This hack changes all of the units from Bps to B! (should be cleaned up)
160
218
#time_between = float(max_ts - last_used_ts)
161
219
time_between = 1 # we want to use bytes as unit, not bytes per second
162
220
0 commit comments