Merge lp:~jseutter/landscape-client/autodiscover-config into lp:~landscape/landscape-client/trunk

Proposed by Jerry Seutter
Status: Merged
Approved by: Chad Smith
Approved revision: 457
Merged at revision: 451
Proposed branch: lp:~jseutter/landscape-client/autodiscover-config
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 433 lines (+100/-38)
9 files modified
landscape-client.conf (+0/-2)
landscape/broker/config.py (+10/-0)
landscape/broker/dnslookup.py (+14/-9)
landscape/broker/ping.py (+22/-4)
landscape/broker/service.py (+6/-2)
landscape/broker/tests/test_config.py (+4/-4)
landscape/broker/tests/test_dnslookup.py (+22/-13)
landscape/broker/tests/test_ping.py (+3/-1)
landscape/broker/transport.py (+19/-3)
To merge this branch: bzr merge lp:~jseutter/landscape-client/autodiscover-config
Reviewer Review Type Date Requested Status
Chad Smith Approve
Free Ekanayaka (community) Approve
Review via email: mp+91691@code.launchpad.net

Description of the change

This branch exposes config file settings for server autodiscovery. The autodiscover_srv_query_string is used when querying the DNS server with a SRV query. The autodiscover_a_query_string is used when querying the DNS server with an A query.

To post a comment you must log in.
455. By Jerry Seutter

query strings were being passed incorrectly for client pings.

456. By Jerry Seutter

Fixed a bug where autodiscovery was unsuccessful for message exchanges.

457. By Jerry Seutter

Reverting changes to landscape-client.conf

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :
Download full text (4.7 KiB)

Looks good, only a couple of points about style. +1!

[1]

+def discover_server(resolver=None, autodiscover_srv_query_string="",
+ autodiscover_a_query_string=""):

and

+ lookup_deferred = discover_server(
+ None, self._autodiscover_srv_query_string,
+ self._autodiscover_a_query_string)

It feels a bit weird to pass None as first argument, the casual reader is probably going to wonder what that means, especially considering that this is the only use of discover_server() in non-test code.

If the resolver argument is used for testing only, please consider moving it at the end of the arguments list:

def discover_server(autodiscover_srv_query_string="",
                    autodiscover_a_query_string="", resolver=None):

and use it like:

+ lookup_deferred = discover_server(
+ self._autodiscover_srv_query_string,
+ self._autodiscover_a_query_string)

[2]

Not really related to this branch, but while I was reading some context, I've noticed that the ping method has grown a bit:

    def ping(self):
        """Ask the question.

        Hits the URL previously specified with the insecure_id gotten
        from the identity.

        @return: A deferred resulting in True if there are messages
            and False otherwise.
        """
        def handle_result(result):
            if result is None:
                logging.warning("Autodiscovery failed. Reverting to previous "
                                "settings.")
            else:
                result = "http://%s/ping" % result
            return result

        def do_rest(result):
            if result is not None:
                self.url = result

            insecure_id = self._identity.insecure_id
            if insecure_id is not None:
                headers = {"Content-Type": "application/x-www-form-urlencoded"}
                data = urllib.urlencode({"insecure_id": insecure_id})
                page_deferred = defer.Deferred()

                def errback(type, value, tb):
                    page_deferred.errback(Failure(value, type, tb))
                self._reactor.call_in_thread(page_deferred.callback, errback,
                                             self.get_page, self.url,
                                             post=True, data=data,
                                             headers=headers)
                page_deferred.addCallback(self._got_result)
                return page_deferred
            return defer.succeed(False)

        if self._server_autodiscover:
            lookup_deferred = discover_server(
                None, self._autodiscover_srv_query_string,
                self._autodiscover_a_query_string)
            lookup_deferred.addCallback(handle_result)
            lookup_deferred.addCallback(do_rest)
            return lookup_deferred
        else:
            return do_rest(None)

What do you think of splitting it in methods like:

    def ping(self):
        """Ask the question.

        Hits the URL previously specified with the insecure_id gotten
        from the identity.

        @return: A deferred resulting in True if there a...

Read more...

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

+1 looks good tests seem to exercise the added config params.

[1] This might be a pain to set up: In transport.py this section of code doesn't seem to have a test case associated with it. I tried mangling the code to throw errors but it doesn't seem to be covered by a test. It'd be nice if we can mock the discover_server call and test _url result or warning messages logged.
        if self._server_autodiscover:
            result = blockingCallFromThread(self._reactor, discover_server,
                                            autodiscover_srv_query_string,
                                            autodiscover_a_query_string)
            if result is not None:
                self._url = "https://%s/message-system" % result
            else:
                logging.warn("Autodiscovery failed. Falling back to previous "
                             "settings.")

[2] Maybe a separate MP or maybe we already decided this to be something done during boot. Correct me if this is the wrong thought.

Upon successful auto-discover during PingClient connect, should we write those discovered values to the existing client config file so we don't autodiscover every ping attempt?

        def handle_result(result):
            if result is None:
                logging.warning("Autodiscovery failed. Reverting to previous "
                                "settings.")
            else:
                result = "http://%s/ping" % result
+? self.autodiscover = False
+? self.url = result
+? configuration.ping_url = self._url
            return result

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape-client.conf'
2--- landscape-client.conf 2012-01-21 00:07:53 +0000
3+++ landscape-client.conf 2012-02-06 22:25:22 +0000
4@@ -9,5 +9,3 @@
5 log_level = debug
6 pid_file = /tmp/landscape/landscape-client.pid
7 ping_url = http://localhost:8081/ping
8-server_autodiscover = false
9-
10
11=== modified file 'landscape/broker/config.py'
12--- landscape/broker/config.py 2012-02-02 02:49:13 +0000
13+++ landscape/broker/config.py 2012-02-06 22:25:22 +0000
14@@ -45,6 +45,9 @@
15 - C{otp}
16 - C{record}
17 - C{provisioning_otp}
18+ - C{server_autodiscover}
19+ - C{autodiscover_srv_query_string}
20+ - C{autodiscover_a_query_string}
21 """
22 parser = super(BrokerConfiguration, self).make_parser()
23
24@@ -91,6 +94,13 @@
25 help="The OTP to use for a provisioned machine.")
26 parser.add_option("--server-autodiscover", type="string",
27 default="false", help="Enable server autodiscovery.")
28+ parser.add_option("--autodiscover-srv-query-string", type="string",
29+ default="_tcp._landscape.localdomain",
30+ help="autodiscovery string for DNS SRV queries")
31+ parser.add_option("--autodiscover-a-query-string", type="string",
32+ default="landscape.localdomain",
33+ help="autodiscovery string for DNS A queries")
34+
35 return parser
36
37 @property
38
39=== modified file 'landscape/broker/dnslookup.py'
40--- landscape/broker/dnslookup.py 2012-02-02 01:39:15 +0000
41+++ landscape/broker/dnslookup.py 2012-02-06 22:25:22 +0000
42@@ -5,31 +5,36 @@
43 from twisted.names.client import Resolver
44
45
46-def discover_server(resolver=None):
47+def discover_server(resolver=None, autodiscover_srv_query_string="",
48+ autodiscover_a_query_string=""):
49 """
50 Look up the dns location of the landscape server.
51
52 @type resolver: The resolver to use. If none is specified a resolver that
53 uses settings from /etc/resolv.conf will be created.
54+ @param autodiscover_srv_query_string: The query string to send to the DNS
55+ server when making a SRV query.
56+ @param autodiscover_a_query_string: The query string to send to the DNS
57+ server when making a A query.
58 """
59 if not resolver:
60 resolver = Resolver("/etc/resolv.conf")
61- d = lookup_server_record(resolver)
62- d.addErrback(lookup_hostname, resolver)
63+ d = lookup_server_record(resolver, autodiscover_srv_query_string)
64+ d.addErrback(lookup_hostname, resolver, autodiscover_a_query_string)
65 return d
66
67
68-def lookup_server_record(resolver):
69+def lookup_server_record(resolver, service_name):
70 """
71 Do a DNS SRV record lookup for the location of the landscape server.
72
73 @type resolver: A resolver to use for DNS lookups
74 L{twisted.names.client.Resolver}.
75+ @param service_name: The query string to send to the DNS server when
76+ making a SRV query.
77 @return: A deferred containing either the hostname of the landscape server
78 if found or an empty string if not found.
79 """
80- service_name = "_landscape._tcp.mylandscapehost.com"
81-
82 def lookup_done(result):
83 name = ""
84 for item in result:
85@@ -49,17 +54,17 @@
86 return d
87
88
89-def lookup_hostname(result, resolver):
90+def lookup_hostname(result, resolver, hostname):
91 """
92 Do a DNS name lookup for the location of the landscape server.
93
94 @param result: The result from a call to lookup_server_record.
95 @param resolver: The resolver to use for DNS lookups.
96+ @param hostname: The query string to send to the DNS server when making
97+ a A query.
98 @param return: A deferred containing the ip address of the landscape
99 server if found or None if not found.
100 """
101- hostname = "landscape.localdomain"
102-
103 def lookup_done(result):
104 return result
105
106
107=== modified file 'landscape/broker/ping.py'
108--- landscape/broker/ping.py 2012-01-27 22:00:02 +0000
109+++ landscape/broker/ping.py 2012-02-06 22:25:22 +0000
110@@ -22,10 +22,19 @@
111 @param url: The URL to ask the question to.
112 @type identity: L{landscape.broker.registration.Identity}
113 @param identity: This client's identity.
114+ @param get_page: The method to use to retrieve content. If not specified,
115+ landscape.lib.fetch.fetch is used.
116+ @param server_autodiscovery: Server autodiscovery is performed if True,
117+ otherwise server autodiscover is not done.
118+ @param autodiscover_srv_query_string: If server autodiscovery is done, this
119+ string will be sent to the DNS server when making a SRV query.
120+ @param autodiscover_a_query_string: If server autodiscovery is done, this
121+ string will be sent to the DNS server when making an A query.
122 """
123
124 def __init__(self, reactor, url, identity, get_page=None,
125- server_autodiscover=False):
126+ server_autodiscover=False, autodiscover_srv_query_string="",
127+ autodiscover_a_query_string=""):
128 if get_page is None:
129 get_page = fetch
130 self._reactor = reactor
131@@ -33,6 +42,8 @@
132 self.get_page = get_page
133 self.url = url
134 self._server_autodiscover = server_autodiscover
135+ self._autodiscover_srv_query_string = autodiscover_srv_query_string
136+ self._autodiscover_a_query_string = autodiscover_a_query_string
137
138 def ping(self):
139 """Ask the question.
140@@ -72,7 +83,9 @@
141 return defer.succeed(False)
142
143 if self._server_autodiscover:
144- lookup_deferred = discover_server()
145+ lookup_deferred = discover_server(
146+ None, self._autodiscover_srv_query_string,
147+ self._autodiscover_a_query_string)
148 lookup_deferred.addCallback(handle_result)
149 lookup_deferred.addCallback(do_rest)
150 return lookup_deferred
151@@ -103,7 +116,8 @@
152
153 def __init__(self, reactor, url, identity, exchanger,
154 interval=30, ping_client_factory=PingClient,
155- server_autodiscover=False):
156+ server_autodiscover=False, autodiscover_srv_query_string="",
157+ autodiscover_a_query_string=""):
158 self._url = url
159 self._interval = interval
160 self._identity = identity
161@@ -113,6 +127,8 @@
162 self._ping_client = None
163 self.ping_client_factory = ping_client_factory
164 self._server_autodiscover = server_autodiscover
165+ self._autodiscover_srv_query_string = autodiscover_srv_query_string
166+ self._autodiscover_a_query_string = autodiscover_a_query_string
167 reactor.call_on("message", self._handle_set_intervals)
168
169 def get_url(self):
170@@ -130,7 +146,9 @@
171 """Start pinging."""
172 self._ping_client = self.ping_client_factory(
173 self._reactor, self._url, self._identity,
174- server_autodiscover=self._server_autodiscover)
175+ server_autodiscover=self._server_autodiscover,
176+ autodiscover_srv_query_string=self._autodiscover_srv_query_string,
177+ autodiscover_a_query_string=self._autodiscover_a_query_string)
178 self._call_id = self._reactor.call_every(self._interval, self.ping)
179
180 def ping(self):
181
182=== modified file 'landscape/broker/service.py'
183--- landscape/broker/service.py 2012-01-30 16:11:20 +0000
184+++ landscape/broker/service.py 2012-02-06 22:25:22 +0000
185@@ -55,7 +55,9 @@
186
187 self.transport = self.transport_factory(
188 self.reactor, config.url, config.ssl_public_key,
189- self.payload_recorder, config.server_autodiscover)
190+ self.payload_recorder, config.server_autodiscover,
191+ config.autodiscover_srv_query_string,
192+ config.autodiscover_a_query_string)
193 self.message_store = get_default_message_store(
194 self.persist, config.message_store_path)
195 self.identity = Identity(self.config, self.persist)
196@@ -66,7 +68,9 @@
197 self.pinger = self.pinger_factory(
198 self.reactor, config.ping_url, self.identity, self.exchanger,
199 interval=config.ping_interval,
200- server_autodiscover=config.server_autodiscover)
201+ server_autodiscover=config.server_autodiscover,
202+ autodiscover_srv_query_string=config.autodiscover_srv_query_string,
203+ autodiscover_a_query_string=config.autodiscover_a_query_string)
204 self.registration = RegistrationHandler(
205 config, self.identity, self.reactor, self.exchanger, self.pinger,
206 self.message_store, fetch_async)
207
208=== modified file 'landscape/broker/tests/test_config.py'
209--- landscape/broker/tests/test_config.py 2012-02-02 02:49:13 +0000
210+++ landscape/broker/tests/test_config.py 2012-02-06 22:25:22 +0000
211@@ -92,19 +92,19 @@
212 self.assertEqual(configuration.url,
213 "https://landscape.canonical.com/message-system")
214
215- def test_server_autodiscovery_handling(self):
216+ def test_server_autodiscover_handling(self):
217 """
218 server_autodiscover is parsed and converted to a boolean value by
219 load().
220 """
221 configuration = BrokerConfiguration()
222 configuration.load([])
223- self.assertEquals(configuration.server_autodiscover, False)
224+ self.assertEqual(configuration.server_autodiscover, False)
225
226 configuration = BrokerConfiguration()
227 configuration.load(["--server-autodiscover=true"])
228- self.assertEquals(configuration.server_autodiscover, True)
229+ self.assertEqual(configuration.server_autodiscover, True)
230
231 configuration = BrokerConfiguration()
232 configuration.load(["--server-autodiscover=false"])
233- self.assertEquals(configuration.server_autodiscover, False)
234+ self.assertEqual(configuration.server_autodiscover, False)
235
236=== modified file 'landscape/broker/tests/test_dnslookup.py'
237--- landscape/broker/tests/test_dnslookup.py 2012-02-02 01:39:15 +0000
238+++ landscape/broker/tests/test_dnslookup.py 2012-02-06 22:25:22 +0000
239@@ -46,13 +46,16 @@
240 def __init__(self):
241 self.results = None
242 self.name = None
243+ self.queried = None
244
245 def lookupService(self, arg1):
246+ self.queried = arg1
247 deferred = defer.Deferred()
248 deferred.callback(self.results)
249 return deferred
250
251 def getHostByName(self, arg1):
252+ self.queried = arg1
253 deferred = defer.Deferred()
254 deferred.callback(self.name)
255 return deferred
256@@ -84,11 +87,13 @@
257 fake_result.payload.target.name = "a.b.com"
258 fake_resolver = FakeResolver()
259 fake_resolver.results = [[fake_result]]
260+ query_string = "_landscape._tcp.mylandscapehost.com"
261
262 def check(result):
263- self.assertEquals("a.b.com", result)
264+ self.assertEqual(fake_resolver.queried, query_string)
265+ self.assertEqual("a.b.com", result)
266
267- d = lookup_server_record(fake_resolver)
268+ d = lookup_server_record(fake_resolver, query_string)
269 d.addCallback(check)
270 return d
271
272@@ -101,9 +106,10 @@
273 fake_resolver.results = [[]]
274
275 def check(result):
276- self.assertEquals("", result)
277+ self.assertEqual("", result)
278
279- d = lookup_server_record(fake_resolver)
280+ d = lookup_server_record(fake_resolver,
281+ "_landscape._tcp.mylandscapehost.com")
282 d.addCallback(check)
283 return d
284
285@@ -115,7 +121,8 @@
286 "failed.")
287 self.mocker.replay()
288
289- d = lookup_server_record(BadResolver())
290+ d = lookup_server_record(BadResolver(),
291+ "_landscape._tcp.mylandscapehost.com")
292 self.assertFailure(d, ResolverError)
293 return d
294
295@@ -127,11 +134,13 @@
296 """
297 fake_resolver = FakeResolver()
298 fake_resolver.name = "a.b.com"
299+ query_string = "landscape.localdomain"
300
301 def check(result):
302- self.assertEquals("a.b.com", result)
303+ self.assertEqual(fake_resolver.queried, query_string)
304+ self.assertEqual("a.b.com", result)
305
306- d = lookup_hostname(None, fake_resolver)
307+ d = lookup_hostname(None, fake_resolver, query_string)
308 d.addCallback(check)
309 return d
310
311@@ -144,9 +153,9 @@
312 fake_resolver.name = None
313
314 def check(result):
315- self.assertEquals(None, result)
316+ self.assertEqual(None, result)
317
318- d = lookup_hostname(None, fake_resolver)
319+ d = lookup_hostname(None, fake_resolver, "landscape.localdomain")
320 d.addCallback(check)
321 return d
322
323@@ -157,7 +166,7 @@
324 logging_mock("Name lookup of landscape.localdomain failed.")
325 self.mocker.replay()
326
327- d = lookup_hostname(None, BadResolver())
328+ d = lookup_hostname(None, BadResolver(), "landscape.localdomain")
329 self.assertFailure(d, ResolverError)
330 return d
331
332@@ -174,7 +183,7 @@
333 d = discover_server(fake_resolver)
334
335 def check(result):
336- self.assertEquals("a.b.com", result)
337+ self.assertEqual("a.b.com", result)
338
339 d.addCallback(check)
340 return d
341@@ -187,13 +196,13 @@
342 d = discover_server(fake_resolver)
343
344 def check(result):
345- self.assertEquals("x.y.com", result)
346+ self.assertEqual("x.y.com", result)
347
348 d.addCallback(check)
349 return d
350
351 def test_failed_lookup(self):
352 """A resolver error is returned when server autodiscovery fails."""
353- d = lookup_server_record(BadResolver())
354+ d = lookup_server_record(BadResolver(), "landscape.localdomain")
355 self.assertFailure(d, ResolverError)
356 return d
357
358=== modified file 'landscape/broker/tests/test_ping.py'
359--- landscape/broker/tests/test_ping.py 2012-01-19 23:34:55 +0000
360+++ landscape/broker/tests/test_ping.py 2012-02-06 22:25:22 +0000
361@@ -128,7 +128,9 @@
362 self.url = "http://localhost:8081/whatever"
363 self.page_getter = FakePageGetter(None)
364
365- def factory(reactor, url, insecure_id, server_autodiscover):
366+ def factory(reactor, url, insecure_id, server_autodiscover,
367+ autodiscover_srv_query_string,
368+ autodiscover_a_query_string):
369 return PingClient(reactor, url, insecure_id,
370 get_page=self.page_getter.get_page)
371 self.pinger = Pinger(self.broker_service.reactor,
372
373=== modified file 'landscape/broker/transport.py'
374--- landscape/broker/transport.py 2012-01-26 23:37:53 +0000
375+++ landscape/broker/transport.py 2012-02-06 22:25:22 +0000
376@@ -22,15 +22,24 @@
377 @param pubkey: SSH public key used for secure communication.
378 @param payload_recorder: PayloadRecorder used for recording exchanges
379 with the server. If C{None}, exchanges will not be recorded.
380+ @param server_autodiscovery: Server autodiscovery is performed if True,
381+ otherwise server autodiscover is not done.
382+ @param autodiscover_srv_query_string: If server autodiscovery is done, this
383+ string will be sent to the DNS server when making a SRV query.
384+ @param autodiscover_a_query_string: If server autodiscovery is done, this
385+ string will be sent to the DNS server when making an A query.
386 """
387
388 def __init__(self, reactor, url, pubkey=None, payload_recorder=None,
389- server_autodiscover=False):
390+ server_autodiscover=False, autodiscover_srv_query_string="",
391+ autodiscover_a_query_string=""):
392 self._reactor = reactor
393 self._url = url
394 self._pubkey = pubkey
395 self._payload_recorder = payload_recorder
396 self._server_autodiscover = server_autodiscover
397+ self._autodiscover_srv_query_string = autodiscover_srv_query_string
398+ self._autodiscover_a_query_string = autodiscover_a_query_string
399
400 def get_url(self):
401 """Get the URL of the remote message system."""
402@@ -42,7 +51,10 @@
403
404 def _curl(self, payload, computer_id, message_api):
405 if self._server_autodiscover:
406- result = blockingCallFromThread(self._reactor, discover_server)
407+ result = blockingCallFromThread(
408+ self._reactor, discover_server, None,
409+ self._autodiscover_srv_query_string,
410+ self._autodiscover_a_query_string)
411 if result is not None:
412 self._url = "https://%s/message-system" % result
413 else:
414@@ -166,7 +178,9 @@
415 """Fake transport for testing purposes."""
416
417 def __init__(self, reactor=None, url=None, pubkey=None,
418- payload_recorder=None, server_autodiscover=False):
419+ payload_recorder=None, server_autodiscover=False,
420+ autodiscover_srv_query_string="",
421+ autodiscover_a_query_string=""):
422 self._pubkey = pubkey
423 self._payload_recorder = payload_recorder
424 self.payloads = []
425@@ -179,6 +193,8 @@
426 self._url = url
427 self._reactor = reactor
428 self._server_autodiscover = server_autodiscover
429+ self._autodiscover_srv_query_string = autodiscover_srv_query_string
430+ self._autodiscover_a_query_string = autodiscover_a_query_string
431
432 def get_url(self):
433 return self._url

Subscribers

People subscribed via source and target branches

to all changes: