From 0e8bc9a3d70f507a2409b1bca732f15711aee8aa Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Tue, 20 Jun 2017 14:43:27 -0700 Subject: [PATCH] Dyn monitor config support, includes ability to update Also fixes some mocking data to match what the Dyn client libs are expecting. --- octodns/provider/dyn.py | 33 +++++++++-- tests/test_octodns_provider_dyn.py | 92 ++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/octodns/provider/dyn.py b/octodns/provider/dyn.py index 61b56bc..1b454ba 100644 --- a/octodns/provider/dyn.py +++ b/octodns/provider/dyn.py @@ -133,6 +133,10 @@ class DynProvider(BaseProvider): 'AN': 17, # Continental Antartica } + MONITOR_HEADER = 'User-Agent: Dyn Monitor' + MONITOR_PORT = 443 + MONITOR_TIMEOUT = 10 + _sess_create_lock = Lock() def __init__(self, id, customer, username, password, @@ -454,12 +458,33 @@ class DynProvider(BaseProvider): fqdn = record.fqdn try: - return self._traffic_director_monitors[fqdn] + monitor = self._traffic_director_monitors[fqdn] + if monitor._host != record.healthcheck_host or \ + monitor._path != record.healthcheck_path: + self.log.info('_traffic_director_monitor: updating monitor ' + 'for %s:%s', record.fqdn, record._type) + # I can't see how to actually do this with the client lib so + # I'm having to hack around it. Have to provide all the + # options or else things complain + monitor._update({ + 'options': { + 'host': record.healthcheck_host, + 'path': record.healthcheck_path, + 'port': self.MONITOR_PORT, + 'timeout': self.MONITOR_TIMEOUT, + 'header': self.MONITOR_HEADER, + } + }) + return monitor except KeyError: + self.log.info('_traffic_director_monitor: creating monitor ' + 'for %s:%s', record.fqdn, record._type) monitor = DSFMonitor(fqdn, protocol='HTTPS', response_count=2, - probe_interval=60, retries=2, port=443, - active='Y', host=record.healthcheck_host, - timeout=10, header='User-Agent: Dyn Monitor', + probe_interval=60, retries=2, + port=self.MONITOR_PORT, active='Y', + host=record.healthcheck_host, + timeout=self.MONITOR_TIMEOUT, + header=self.MONITOR_HEADER, path=record.healthcheck_path) self._traffic_director_monitors[fqdn] = monitor return monitor diff --git a/tests/test_octodns_provider_dyn.py b/tests/test_octodns_provider_dyn.py index f48e4bf..acd248f 100644 --- a/tests/test_octodns_provider_dyn.py +++ b/tests/test_octodns_provider_dyn.py @@ -527,21 +527,22 @@ class TestDynProviderGeo(TestCase): monitors_response = { 'data': [{ 'active': 'Y', + 'agent_scheme': 'geo', 'dsf_monitor_id': monitor_id, 'endpoints': [], 'label': 'unit.tests.', - 'notifier': '', - 'options': { - 'expected': '', - 'header': 'User-Agent: Dyn Monitor', - 'host': 'unit.tests', - 'path': '/_dns', - 'port': '443', - 'timeout': '10'}, + 'notifier': [], + 'expected': '', + 'header': 'User-Agent: Dyn Monitor', + 'host': 'unit.tests', + 'path': '/_dns', + 'port': '443', + 'timeout': '10', 'probe_interval': '60', 'protocol': 'HTTPS', 'response_count': '2', - 'retries': '2' + 'retries': '2', + 'services': ['12311'] }], 'job_id': 3376281406, 'msgs': [{ @@ -648,14 +649,12 @@ class TestDynProviderGeo(TestCase): 'endpoints': [], 'label': 'geo.unit.tests.', 'notifier': '', - 'options': { - 'expected': '', - 'header': 'User-Agent: Dyn Monitor', - 'host': 'geo.unit.tests.', - 'path': '/_dns', - 'port': '443', - 'timeout': '10' - }, + 'expected': '', + 'header': 'User-Agent: Dyn Monitor', + 'host': 'geo.unit.tests.', + 'path': '/_dns', + 'port': '443', + 'timeout': '10', 'probe_interval': '60', 'protocol': 'HTTPS', 'response_count': '2', @@ -721,6 +720,65 @@ class TestDynProviderGeo(TestCase): # should have resulted in no calls b/c exists & we've cached the list mock.assert_not_called() + # and finally for a monitor that exists, but with a differing config + record = Record.new(existing, '', { + 'octodns': { + 'healthcheck': { + 'host': 'bleep.bloop', + 'path': '/_nope' + } + }, + 'ttl': 60, + 'type': 'A', + 'value': '1.2.3.4' + }) + mock.reset_mock() + mock.side_effect = [{ + 'data': { + 'active': 'Y', + 'dsf_monitor_id': self.monitor_id, + 'endpoints': [], + 'label': 'unit.tests.', + 'notifier': '', + 'expected': '', + 'header': 'User-Agent: Dyn Monitor', + 'host': 'bleep.bloop', + 'path': '/_nope', + 'port': '443', + 'timeout': '10', + 'probe_interval': '60', + 'protocol': 'HTTPS', + 'response_count': '2', + 'retries': '2' + }, + 'job_id': 3376259461, + 'msgs': [{'ERR_CD': None, + 'INFO': 'add: Here is the new monitor', + 'LVL': 'INFO', + 'SOURCE': 'BLL'}], + 'status': 'success' + }] + monitor = provider._traffic_director_monitor(record) + self.assertEquals(self.monitor_id, monitor.dsf_monitor_id) + # should have resulted an update + mock.assert_has_calls([ + call('/DSFMonitor/42a/', 'PUT', { + 'options': { + 'path': '/_nope', + 'host': 'bleep.bloop', + 'header': 'User-Agent: Dyn Monitor', + 'port': 443, + 'timeout': 10 + } + }) + ]) + # cached monitor should have been updated + self.assertTrue('unit.tests.' in + provider._traffic_director_monitors) + monitor = provider._traffic_director_monitors['unit.tests.'] + self.assertEquals('bleep.bloop', monitor._host) + self.assertEquals('/_nope', monitor._path) + @patch('dyn.core.SessionEngine.execute') def test_populate_traffic_directors_empty(self, mock): provider = DynProvider('test', 'cust', 'user', 'pass',