From f2d52251e5f75b8762375cf081ff1c07d96ec6be Mon Sep 17 00:00:00 2001 From: Stefan Schantl Date: Fri, 24 Feb 2017 11:53:50 +0100 Subject: [PATCH] Introduce error type "rate-limited" This error type is used for handling HTTP 429 - Too many requests. When using this error type, a holdoff_time of 30 minutes will be used by default until the next update attempt will be send. The holdoff_time can be specified in the provides class if needed. Signed-off-by: Stefan Schantl --- src/ddns/database.py | 6 ++++-- src/ddns/errors.py | 2 ++ src/ddns/providers.py | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ddns/database.py b/src/ddns/database.py index 70a7363..adce587 100644 --- a/src/ddns/database.py +++ b/src/ddns/database.py @@ -2,7 +2,7 @@ ############################################################################### # # # ddns - A dynamic DNS client for IPFire # -# Copyright (C) 2014 IPFire development team # +# Copyright (C) 2014-2017 IPFire development team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # @@ -111,12 +111,14 @@ class DDNSDatabase(object): def log_failure(self, hostname, exception): if exception: message = "%s: %s" % (exception.__class__.__name__, exception.reason) + error_type = exception.genre else: message = None + error_type = "failure" logger.debug("Logging failed update for %s: %s" % (hostname, message or "")) - return self.add_update(hostname, "failure", message=message) + return self.add_update(hostname, error_type, message=message) def last_update(self, hostname, status=None): """ diff --git a/src/ddns/errors.py b/src/ddns/errors.py index a8a2017..56ab974 100644 --- a/src/ddns/errors.py +++ b/src/ddns/errors.py @@ -27,6 +27,7 @@ class DDNSError(Exception): raised by DDNS. """ reason = N_("Error") + genre = "failure" def __init__(self, message=None): self.message = message @@ -166,6 +167,7 @@ class DDNSTooManyRequests(DDNSError): Raised when too many requests occured. """ reason = N_("Too many requests") + genre = "rate-limited" class DDNSUpdateError(DDNSError): diff --git a/src/ddns/providers.py b/src/ddns/providers.py index ea723e5..cb6dd2d 100644 --- a/src/ddns/providers.py +++ b/src/ddns/providers.py @@ -67,6 +67,10 @@ class DDNSProvider(object): # is tried after the last one has failed. holdoff_failure_days = 0.5 + # holdoff time for rate-limited updates - Number of minutes no update + # is tried after the last one has been sent. + holdoff_rate-limited_minutes = 30 + # True if the provider is able to remove records, too. # Required to remove AAAA records if IPv6 is absent again. can_remove_records = True @@ -223,13 +227,13 @@ class DDNSProvider(object): @property def has_failure(self): """ - Returns True when the last update has failed and no retry - should be performed, yet. + Returns True when the last update has not failed or rate-limited and + no retry should be performed, yet. """ last_status = self.db.last_update_status(self.hostname) - # Return False if the last update has not failed. - if not last_status == "failure": + # Return False if the last update has failed or has been rate-limited. + if not (last_status == "failure" or last_status == "rate-limited"): return False # If there is no holdoff time, we won't update ever again. @@ -242,7 +246,12 @@ class DDNSProvider(object): # Determine when the holdoff time ends last_update = self.db.last_update(self.hostname, status=last_status) - holdoff_end = last_update + datetime.timedelta(days=self.holdoff_failure_days) + + # Calculate holdoff end, based on the last status (failure or rate-limited) + if last_status == "failure" + holdoff_end = last_update + datetime.timedelta(days=self.holdoff_failure_days) + else: + holdoff_end = last_update + datetime.timedelta(minutes=self.holdoff_rate-limited_minutes) now = datetime.datetime.utcnow() if now < holdoff_end: -- 2.47.2