From 0fbd8e4f721eb029ae6d8cb07094eafea12422da Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 19 Dec 2014 05:51:43 +0000 Subject: [PATCH 1/6] Update ddns only if the IP has changed --- noipy/main.py | 26 +++++++++++++++++++++----- test/test_noipy.py | 5 +++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/noipy/main.py b/noipy/main.py index f148dc1..6e41c15 100644 --- a/noipy/main.py +++ b/noipy/main.py @@ -16,6 +16,7 @@ import sys import re import getpass +import socket try: from . import dnsupdater @@ -57,6 +58,15 @@ def get_ip(): return re.search(r'(\d{1,3}\.?){4}', content).group() +def get_dns_ip(dnsname): + """Return the machine's current IP address in DNS. + """ + try: + return socket.gethostbyname(dnsname) + except: + return "" + + def print_version(): print("== noipy DDNS updater tool v%s ==" % __version__) @@ -138,12 +148,18 @@ def execute_update(args): update_ddns = False if update_ddns: - updater = provider_class(auth, args.hostname, updater_options) ip_address = args.ip if args.ip else get_ip() - print("Updating hostname '%s' with IP address %s [provider: '%s']..." - % (args.hostname, ip_address, args.provider)) - updater.update_dns(ip_address) - process_message = updater.status_message + if ip_address == get_dns_ip(args.hostname): + process_message = "No update required." + exec_result = EXECUTION_RESULT_NOK + else: + updater = provider_class(auth, args.hostname, updater_options) + ip_address = args.ip if args.ip else get_ip() + print("Updating hostname '%s' with IP address %s \ + [provider: '%s']..." + % (args.hostname, ip_address, args.provider)) + updater.update_dns(ip_address) + process_message = updater.status_message return exec_result, process_message diff --git a/test/test_noipy.py b/test/test_noipy.py index eba0c49..f40c5d6 100644 --- a/test/test_noipy.py +++ b/test/test_noipy.py @@ -187,6 +187,11 @@ def test_get_ip(self): self.assertTrue(re.match(VALID_IP_REGEX, ip), 'get_ip() failed.') + def test_get_dns_ip(self): + ip = main.get_dns_ip('localhost') + + self.assertTrue(ip == '127.0.0.1', 'get_dns_ip() failed.') + def test_not_implemented_plugin(self): auth = authinfo.ApiAuth('username', 'password') hostname = "hostname" From ef7efa1b08e52a6b9aeb29dae7acbd8a4f9243b4 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 19 Dec 2014 06:11:02 +0000 Subject: [PATCH 2/6] Deleted an unnecessary, duplicate line --- noipy/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/noipy/main.py b/noipy/main.py index 6e41c15..425475d 100644 --- a/noipy/main.py +++ b/noipy/main.py @@ -154,7 +154,6 @@ def execute_update(args): exec_result = EXECUTION_RESULT_NOK else: updater = provider_class(auth, args.hostname, updater_options) - ip_address = args.ip if args.ip else get_ip() print("Updating hostname '%s' with IP address %s \ [provider: '%s']..." % (args.hostname, ip_address, args.provider)) From 28b4afff2d8fe9912ee41fe7fefc885822eebef2 Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 19 Dec 2014 06:52:17 +0000 Subject: [PATCH 3/6] Skipping the update because the IP has not changed is not an error condition --- noipy/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/noipy/main.py b/noipy/main.py index 425475d..5033cbc 100644 --- a/noipy/main.py +++ b/noipy/main.py @@ -151,7 +151,6 @@ def execute_update(args): ip_address = args.ip if args.ip else get_ip() if ip_address == get_dns_ip(args.hostname): process_message = "No update required." - exec_result = EXECUTION_RESULT_NOK else: updater = provider_class(auth, args.hostname, updater_options) print("Updating hostname '%s' with IP address %s \ From 909170134de790eebf91caa0d9986b300217f34c Mon Sep 17 00:00:00 2001 From: Jay Date: Fri, 19 Dec 2014 06:58:38 +0000 Subject: [PATCH 4/6] Added a better tests for the unchanged IP --- test/test_noipy.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/test_noipy.py b/test/test_noipy.py index f40c5d6..2a092a1 100644 --- a/test/test_noipy.py +++ b/test/test_noipy.py @@ -159,6 +159,39 @@ def test_store_and_load_auth_info(self): "Error loading auth info") +class IpChangeTest(unittest.TestCase): + + def setUp(self): + self.parser = main.create_parser() + + def tearDown(self): + pass + + def test_unchanged_ip(self): + cmd_args = ['-u', 'username', '-p', 'password', + '--url', 'https://dynupdate.no-ip.com/nic/update', + '--provider', 'generic', + '-n', 'localhost', '127.0.0.1'] + args = self.parser.parse_args(cmd_args) + result, status_message = main.execute_update(args) + self.assertTrue(result == main.EXECUTION_RESULT_OK, + "Update with unchanged IP failed.") + self.assertTrue(status_message == "No update required.", + "Status message should be 'No update required'") + + def test_changed_ip(self): + cmd_args = ['-u', 'username', '-p', 'password', + '--url', 'https://dynupdate.no-ip.com/nic/update', + '--provider', 'generic', + '-n', 'localhost', '127.0.0.2'] + args = self.parser.parse_args(cmd_args) + result, status_message = main.execute_update(args) + self.assertTrue(result == main.EXECUTION_RESULT_OK, + "Update with changed IP failed.") + self.assertTrue(status_message.startswith("ERROR:"), + "Status message should be an 'ERROR'") + + class GeneralTest(unittest.TestCase): def setUp(self): From fad6e2b2821ac3b2b0c2366bfd9f3c50eeefd569 Mon Sep 17 00:00:00 2001 From: Pablo O Vieira Date: Fri, 19 Dec 2014 16:46:30 -0200 Subject: [PATCH 5/6] minor test case organization --- noipy/main.py | 4 ++-- test/test_noipy.py | 51 ++++++++++++++++------------------------------ 2 files changed, 20 insertions(+), 35 deletions(-) diff --git a/noipy/main.py b/noipy/main.py index 5033cbc..726781b 100644 --- a/noipy/main.py +++ b/noipy/main.py @@ -153,8 +153,8 @@ def execute_update(args): process_message = "No update required." else: updater = provider_class(auth, args.hostname, updater_options) - print("Updating hostname '%s' with IP address %s \ - [provider: '%s']..." + print("Updating hostname '%s' with IP address %s " + "[provider: '%s']..." % (args.hostname, ip_address, args.provider)) updater.update_dns(ip_address) process_message = updater.status_message diff --git a/test/test_noipy.py b/test/test_noipy.py index 2a092a1..ff6f0e7 100644 --- a/test/test_noipy.py +++ b/test/test_noipy.py @@ -92,8 +92,10 @@ def test_duckdns_plugin(self): def test_generic_plugin(self): cmd_args = ['--provider', 'generic'] + args = self.parser.parse_args(cmd_args) result, status_message = main.execute_update(args) + self.assertTrue(result == main.EXECUTION_RESULT_NOK, "An error should be flagged when --provider is " "'generic' and --url is not specified") @@ -102,8 +104,10 @@ def test_generic_plugin(self): '--url', 'https://dynupdate.no-ip.com/nic/update', '--provider', 'generic', '-n', 'noipy.no-ip.org', self.test_ip] + args = self.parser.parse_args(cmd_args) result, status_message = main.execute_update(args) + self.assertTrue(result == main.EXECUTION_RESULT_OK, "Update with 'No-IP' using generic provider failed.") self.assertTrue(status_message.startswith("ERROR:"), @@ -159,39 +163,6 @@ def test_store_and_load_auth_info(self): "Error loading auth info") -class IpChangeTest(unittest.TestCase): - - def setUp(self): - self.parser = main.create_parser() - - def tearDown(self): - pass - - def test_unchanged_ip(self): - cmd_args = ['-u', 'username', '-p', 'password', - '--url', 'https://dynupdate.no-ip.com/nic/update', - '--provider', 'generic', - '-n', 'localhost', '127.0.0.1'] - args = self.parser.parse_args(cmd_args) - result, status_message = main.execute_update(args) - self.assertTrue(result == main.EXECUTION_RESULT_OK, - "Update with unchanged IP failed.") - self.assertTrue(status_message == "No update required.", - "Status message should be 'No update required'") - - def test_changed_ip(self): - cmd_args = ['-u', 'username', '-p', 'password', - '--url', 'https://dynupdate.no-ip.com/nic/update', - '--provider', 'generic', - '-n', 'localhost', '127.0.0.2'] - args = self.parser.parse_args(cmd_args) - result, status_message = main.execute_update(args) - self.assertTrue(result == main.EXECUTION_RESULT_OK, - "Update with changed IP failed.") - self.assertTrue(status_message.startswith("ERROR:"), - "Status message should be an 'ERROR'") - - class GeneralTest(unittest.TestCase): def setUp(self): @@ -225,6 +196,20 @@ def test_get_dns_ip(self): self.assertTrue(ip == '127.0.0.1', 'get_dns_ip() failed.') + def test_unchanged_ip(self): + cmd_args = ['-u', 'username', '-p', 'password', + '--url', 'https://dynupdate.no-ip.com/nic/update', + '--provider', 'generic', + '-n', 'localhost', '127.0.0.1'] + + args = self.parser.parse_args(cmd_args) + result, status_message = main.execute_update(args) + + self.assertTrue(result == main.EXECUTION_RESULT_OK, + "Update with unchanged IP failed.") + self.assertTrue(status_message == "No update required.", + "Status message should be 'No update required'") + def test_not_implemented_plugin(self): auth = authinfo.ApiAuth('username', 'password') hostname = "hostname" From 1b6a42854dd1127370f5323dcecd8652726603f3 Mon Sep 17 00:00:00 2001 From: Pablo O Vieira Date: Fri, 19 Dec 2014 17:03:15 -0200 Subject: [PATCH 6/6] changelog --- CHANGELOG.rst | 5 +++++ noipy/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ce87f06..010d6a7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,11 @@ Changelog ========= +1.3.1 (2014-12-19) +------------------ + +- Send update to DDNS only if IP address has changed + 1.3.0 (2014-12-16) ------------------ diff --git a/noipy/__init__.py b/noipy/__init__.py index d4c8648..7e7902a 100644 --- a/noipy/__init__.py +++ b/noipy/__init__.py @@ -9,7 +9,7 @@ """ __title__ = "noipy" -__version_info__ = ('1', '3', '0') +__version_info__ = ('1', '3', '1') __version__ = ".".join(__version_info__) __author__ = "Pablo O Vieira" __email__ = "email@povieira.com"