diff --git a/flowspec/snmpstats.py b/flowspec/snmpstats.py index c0a3845d701b885cf5f981168f8c69f1dea40ef1..ed8b7c2ffd35b047901b28b4262120c1985dcb42 100644 --- a/flowspec/snmpstats.py +++ b/flowspec/snmpstats.py @@ -456,7 +456,8 @@ def poll_snmp_statistics(): try: if xtype==xtype_default: logger.info("poll_snmp_statistics(): 1a STATISTICS_PER_RULE rule_id="+str(rule_id)) - counter = {"ts": nowstr, "value": newdata[flowspec_params_str][xtype_default]} + val_dropped = newdata[flowspec_params_str][xtype_default] + counter = {"ts": nowstr, "value": val_dropped} else: logger.info("poll_snmp_statistics(): 1b STATISTICS_PER_RULE rule_id="+str(rule_id)) try: @@ -526,6 +527,7 @@ def poll_snmp_statistics(): elif not last_is_null and not rule_newer_than_last: logger.debug("poll_snmp_statistics(): STATISTICS_PER_RULE: rule_id="+str(rule_id)+" case existing active 00") rec.insert(0, counter) + history_per_rule[str(rule_id)+".last"] = counter history_per_rule[rule_id] = rec[:samplecount] except Exception as e: @@ -607,3 +609,103 @@ def add_initial_zero_value(rule_id, route_obj, zero_or_null=True): unlock_history_file() +## + +# workaround for rate limiting rules whose rate limit is changed while the rule is active -> old (absoluet) matched statistics value would be lost afterwards and not be counted as part of the future (absolute) matched statistics value; because the oid keys for the matched value (depending on the rate limit) in the SNMP table have changed +# +# to be called before the rule's rate-limit is changed on the router +# +# TODO; call this function on change of an active rate limit rule whose rate limit is changed: remember_oldmatched__for_changed_ratelimitrules_whileactive() +# TODO: on decativation of the rule the remembered matched value offset set in this function has to be cleared (add new function for this and call it appropriately): clean_oldmatched__for_changed_ratelimitrules_whileactive() +# TODO: use the remembered matched value offset in get_snmp_stats (add to matched value gathered from SNMP) +# + +def remember_oldmatched__for_changed_ratelimitrules_whileactive(rule_id, route_obj): + rule_id=str(rule_id) + logger.debug("remember_oldmatched__for_changed_ratelimitrules_whileactive(): rule_id="+str(rule_id)) + + # get new data + now = datetime.now() + nowstr = now.isoformat() + + key_last_measurement = str(rule_id)+".last" + key_remember_oldmatched = str(rule_id)+".remember_oldmatched_offset" + + # lock history file access + success = lock_history_file(wait=1, reason="remember_oldmatched__for_changed_ratelimitrules_whileactive("+str(rule_id)+","+str(zero_or_null)+")") + if not success: + logger.error("remember_oldmatched__for_changed_ratelimitrules_whileactive(): locking history file failed, aborting"); + return False + + # load history + history = load_history() + + try: + history_per_rule = history['_per_rule'] + except Exception as e: + history_per_rule = {} + + try: + last_matched__measurement_value = history_per_rule[key_last_measurement]["value_matched"] + except: + last_matched__measurement_value = 0 + + try: + last_matched__remember_offset_value = history_per_rule[key_remember_oldmatched]["value_matched"] + except: + last_matched__remember_offset_value = 0 + + # + + last_matched__remember_offset_value = last_matched__remember_offset_value + last_matched__measurement_value + + counter = { "ts": nowstr, "value_matched": last_matched__remember_offset_value } + + try: + history_per_rule[key_remember_oldmatched] = counter + + history['_per_rule'] = history_per_rule + + # store updated history + save_history(history, nowstr) + + except Exception as e: + logger.error("remember_oldmatched__for_changed_ratelimitrules_whileactive(): failure: exception: "+str(e)) + + unlock_history_file() + + +def clean_oldmatched__for_changed_ratelimitrules_whileactive(rule_id, route_obj): + rule_id=str(rule_id) + logger.debug("clean_oldmatched__for_changed_ratelimitrules_whileactive(): rule_id="+str(rule_id)) + + key_remember_oldmatched = str(rule_id)+".remember_oldmatched_offset" + + # lock history file access + success = lock_history_file(wait=1, reason="clean_oldmatched__for_changed_ratelimitrules_whileactive("+str(rule_id)+","+str(zero_or_null)+")") + if not success: + logger.error("clean_oldmatched__for_changed_ratelimitrules_whileactive(): locking history file failed, aborting"); + return False + + # load history + history = load_history() + + try: + history_per_rule = history['_per_rule'] + except Exception as e: + history_per_rule = {} + + try: + history_per_rule[key_remember_oldmatched] = {} + + history['_per_rule'] = history_per_rule + + # store updated history + save_history(history, nowstr) + + except Exception as e: + logger.error("clean_oldmatched__for_changed_ratelimitrules_whileactive(): failure: exception: "+str(e)) + + unlock_history_file() + + diff --git a/flowspec/tasks.py b/flowspec/tasks.py index 7947c3079b4c040f2c8e313ad35fbf1e0e32675b..2d35de444c698542f09c9245485c474692cad77a 100644 --- a/flowspec/tasks.py +++ b/flowspec/tasks.py @@ -55,7 +55,7 @@ def add(routepk, callback=None): route.response = response route.save() #snmp_add_initial_zero_value.delay(str(route.id), True) - snmp_add_initial_zero_value(routepk, route.id, True) # route must exist, so that snmp_add_initial_zero_value can find it in DB + snmp_add_initial_zero_value(routepk, route.id, add_initial_zero_value=True, zero_or_null=True, reset_remember_last_value=True, update_remember_last_value=False) # route must exist, so that snmp_add_initial_zero_value can find it in DB elif response=="Task timeout": if deactivate_route.request.retries < settings.NETCONF_MAX_RETRY_BEFORE_ERROR: # repeat the action @@ -82,10 +82,15 @@ def edit(routepk, callback=None): route.status = "ACTIVE" route.response = response route.save() # save() has to be called before snmp_add_initial_zero_value, as last_updated DB filed is updated now on every call of save() and last db_measurement time must become >= this new last_updated value - if False: # actually wrong to use it for an edited active rule, as the stats values do not drop to zero on the JUNOS router + + update_remember_last_value = False; # TODO check whether rate-limit changed ... + + if update_remember_last_value: + # actually wrong to use add_initial_zero_value=True for an edited active rule, as the stats values do not drop to zero on the JUNOS router try: #snmp_add_initial_zero_value.delay(str(route.id), True) - snmp_add_initial_zero_value(routepk, route.id, True) + #snmp_add_initial_zero_value(routepk, route.id, True, True, True) + snmp_add_initial_zero_value(routepk, route.id, add_initial_zero_value=False, zero_or_null=True, reset_remember_last_value=False, update_remember_last_value=True) except Exception as e: logger.error("tasks::edit(): route="+str(route)+", ACTIVE, add_initial_zero_value failed: "+str(e)) elif response=="Task timeout": @@ -131,7 +136,7 @@ def deactivate_route(routepk, **kwargs): if commit: route.status="INACTIVE" try: - snmp_add_initial_zero_value(routepk, route.id, False) + snmp_add_initial_zero_value(routepk, route.id, add_initial_zero_value=True, zero_or_null=False, reset_remember_last_value=True, update_remember_last_value=False) except Exception as e: logger.error("tasks::deactivate_route(): route="+str(route)+", INACTIVE, add_null_value failed: "+str(e)) @@ -443,7 +448,7 @@ def poll_snmp_statistics(): logger.info("poll_snmp_statistics(): after os._exit() in child process (pid="+str(pid)+", ppid="+str(ppid)+")") @shared_task(ignore_result=True, max_retries=0) -def snmp_add_initial_zero_value(routepk, route_id, zero_or_null=True): +def snmp_add_initial_zero_value(routepk, route_id, add_initial_value=True, zero_or_null=True, reset_remember_last_value=True, update_remember_last_value=False): from flowspec import snmpstats logger.info("snmp_add_initial_zero_value(): routepk="+str(routepk)+" route_id="+str(route_id)) @@ -475,11 +480,27 @@ def snmp_add_initial_zero_value(routepk, route_id, zero_or_null=True): pid = os.getpid() logger.info("snmp_add_initial_zero_value(): in child process (pid="+str(pid)+", ppid="+str(ppid)+")") - try: - snmpstats.add_initial_zero_value(route_id, route, zero_or_null) - logger.debug("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" sucesss") - except Exception as e: - logger.error("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" failed: "+str(e)) + if add_initial_zero_value: + try: + snmpstats.add_initial_zero_value(route_id, route, zero_or_null) + logger.debug("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" add_initial_zero_value sucesss") + except Exception as e: + logger.error("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" add_initial_zero_value failed: "+str(e)) + + if reset_remember_last_value: + try: + snmpstats.clean_oldmatched__for_changed_ratelimitrules_whileactive(route_id, route) + logger.debug("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" clean_oldmatched__for_changed_ratelimitrules_whileactive sucesss") + except Exception as e: + logger.error("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" clean_oldmatched__for_changed_ratelimitrules_whileactive failed: "+str(e)) + + else if update_remember_last_value: + try: + snmpstats.remember_oldmatched__for_changed_ratelimitrules_whileactive(route_id, route) + logger.debug("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" remember_oldmatched__for_changed_ratelimitrules_whileactive sucesss") + except Exception as e: + logger.error("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" remember_oldmatched__for_changed_ratelimitrules_whileactive failed: "+str(e)) + #exit_process()