# the IP address has changed.
holdoff_days = 30
+ # holdoff time for update failures - Number of days no update
+ # is tried after the last one has failed.
+ holdoff_failure_days = 0.5
+
# True if the provider is able to remove records, too.
# Required to remove AAAA records if IPv6 is absent again.
can_remove_records = True
if force:
logger.debug(_("Updating %s forced") % self.hostname)
- # Do nothing if no update is required
- elif not self.requires_update:
+ # Do nothing if the last update has failed or no update is required
+ elif self.has_failure or not self.requires_update:
return
# Execute the update.
return False
+ @property
+ def has_failure(self):
+ """
+ Returns True when the last update has failed 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 there is no holdoff time, we won't update ever again.
+ if self.holdoff_failure_days is None:
+ logger.warning(_("An update has not been performed because earlier updates failed for %s") \
+ % self.hostname)
+ logger.warning(_("There will be no retries"))
+
+ return True
+
+ # 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)
+
+ now = datetime.datetime.utcnow()
+ if now < holdoff_end:
+ failure_message = self.db.last_update_failure_message(self.hostname)
+
+ logger.warning(_("An update has not been performed because earlier updates failed for %s") \
+ % self.hostname)
+
+ if failure_message:
+ logger.warning(_("Last failure message:"))
+
+ for line in failure_message.splitlines():
+ logger.warning(" %s" % line)
+
+ logger.warning(_("Further updates will be withheld until %s") % holdoff_end)
+
+ return True
+
+ return False
+
def ip_address_changed(self, protos):
"""
Returns True if this host is already up to date
return False
# Get the timestamp of the last successfull update
- last_update = self.db.last_update(self.hostname)
+ last_update = self.db.last_update(self.hostname, status="success")
# If no timestamp has been recorded, no update has been
# performed. An update should be performed now.
return "\n".join(scriptlet)
+class DDNSProviderChangeIP(DDNSProvider):
+ handle = "changeip.com"
+ name = "ChangeIP.com"
+ website = "https://changeip.com"
+ protocols = ("ipv4",)
+
+ # Detailed information about the update api can be found here.
+ # http://www.changeip.com/accounts/knowledgebase.php?action=displayarticle&id=34
+
+ url = "https://nic.changeip.com/nic/update"
+ can_remove_records = False
+
+ def update_protocol(self, proto):
+ data = {
+ "hostname" : self.hostname,
+ "myip" : self.get_address(proto),
+ }
+
+ # Send update to the server.
+ try:
+ response = self.send_request(self.url, username=self.username, password=self.password,
+ data=data)
+
+ # Handle error codes.
+ except urllib2.HTTPError, e:
+ if e.code == 422:
+ raise DDNSRequestError(_("Domain not found."))
+
+ raise
+
+ # Handle success message.
+ if response.code == 200:
+ return
+
+ # If we got here, some other update error happened.
+ raise DDNSUpdateError(_("Server response: %s") % output)
+
+
class DDNSProviderDHS(DDNSProvider):
handle = "dhs.org"
name = "DHS International"
# after login on the provider user interface and here:
# http://nsupdateinfo.readthedocs.org/en/latest/user.html
+ url = "https://nsupdate.info/nic/update"
+
# TODO nsupdate.info can actually do this, but the functionality
# has not been implemented here, yet.
can_remove_records = False
+ # After a failed update, there will be no retries
+ # https://bugzilla.ipfire.org/show_bug.cgi?id=10603
+ holdoff_failure_days = None
+
# Nsupdate.info uses the hostname as user part for the HTTP basic auth,
# and for the password a so called secret.
@property
def password(self):
return self.token or self.get("secret")
- @property
- def url(self):
- # The update URL is different by the used protocol.
- if self.proto == "ipv4":
- return "https://ipv4.nsupdate.info/nic/update"
- elif self.proto == "ipv6":
- return "https://ipv6.nsupdate.info/nic/update"
- else:
- raise DDNSUpdateError(_("Invalid protocol has been given"))
-
def prepare_request_data(self, proto):
data = {
"myip" : self.get_address(proto),