Merge lp:~allenap/maas/dhcp-leases-parsing--1.5 into lp:maas/1.5

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 2273
Proposed branch: lp:~allenap/maas/dhcp-leases-parsing--1.5
Merge into: lp:maas/1.5
Diff against target: 773 lines (+407/-212)
2 files modified
src/provisioningserver/dhcp/leases_parser_fast.py (+87/-0)
src/provisioningserver/dhcp/tests/test_leases_parser.py (+320/-212)
To merge this branch: bzr merge lp:~allenap/maas/dhcp-leases-parsing--1.5
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+220086@code.launchpad.net

Commit message

Backport trunk r2335: Faster DHCP leases parser.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/provisioningserver/dhcp/leases_parser_fast.py'
2--- src/provisioningserver/dhcp/leases_parser_fast.py 1970-01-01 00:00:00 +0000
3+++ src/provisioningserver/dhcp/leases_parser_fast.py 2014-05-19 15:16:18 +0000
4@@ -0,0 +1,87 @@
5+# Copyright 2013 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+"""A speedier version of `leases_parser`.
9+
10+This extracts the relevant stanzas from a leases file, keeping only the
11+most recent "host" and "lease" entries, then uses the existing and
12+properly defined but slow parser to parse them. This massively speeds up
13+parsing a leases file that contains a modest number of unique host and
14+lease entries, but has become very large because of churn.
15+"""
16+
17+from __future__ import (
18+ absolute_import,
19+ print_function,
20+ unicode_literals,
21+ )
22+
23+str = None
24+
25+__metaclass__ = type
26+__all__ = [
27+ 'parse_leases',
28+ ]
29+
30+from collections import defaultdict
31+from datetime import datetime
32+from itertools import chain
33+import re
34+
35+from provisioningserver.dhcp.leases_parser import (
36+ get_host_mac,
37+ has_expired,
38+ is_host,
39+ is_lease,
40+ lease_parser,
41+ )
42+
43+
44+re_entry = re.compile(
45+ r'''
46+ ^\s* # Ignore leading whitespace on each line.
47+ (host|lease) # Look only for host or lease stanzas.
48+ \s+ # Mandatory whitespace.
49+ ([0-9a-fA-F.:]+) # Capture the IP/MAC address for this stanza.
50+ \s*{ # Optional whitespace then an opening brace.
51+ ''',
52+ re.MULTILINE | re.DOTALL | re.VERBOSE)
53+
54+
55+def find_lease_starts(leases_contents):
56+ results = defaultdict(dict)
57+ for match in re_entry.finditer(leases_contents):
58+ stanza, address = match.groups()
59+ results[stanza][address] = match.start()
60+ return chain.from_iterable(
61+ mapping.itervalues() for mapping in results.itervalues())
62+
63+
64+def extract_leases(leases_contents):
65+ starts = find_lease_starts(leases_contents)
66+ for start in sorted(starts):
67+ record = lease_parser.scanString(leases_contents[start:])
68+ try:
69+ token, _, _ = next(record)
70+ except StopIteration:
71+ pass
72+ else:
73+ yield token
74+
75+
76+def parse_leases(leases_contents):
77+ results = {}
78+ now = datetime.utcnow()
79+ for entry in extract_leases(leases_contents):
80+ if is_lease(entry):
81+ if not has_expired(entry, now):
82+ results[entry.ip] = entry.hardware.mac
83+ elif is_host(entry):
84+ mac = get_host_mac(entry)
85+ if mac is None:
86+ # TODO: Test this.
87+ if entry.ip in results:
88+ del results[entry.ip]
89+ else:
90+ results[entry.ip] = mac
91+ return results
92
93=== modified file 'src/provisioningserver/dhcp/tests/test_leases_parser.py'
94--- src/provisioningserver/dhcp/tests/test_leases_parser.py 2013-10-07 09:12:40 +0000
95+++ src/provisioningserver/dhcp/tests/test_leases_parser.py 2014-05-19 15:16:18 +0000
96@@ -20,6 +20,10 @@
97
98 from maastesting.factory import factory
99 from maastesting.testcase import MAASTestCase
100+from provisioningserver.dhcp import (
101+ leases_parser,
102+ leases_parser_fast,
103+ )
104 from provisioningserver.dhcp.leases_parser import (
105 combine_entries,
106 gather_hosts,
107@@ -30,197 +34,51 @@
108 is_host,
109 is_lease,
110 lease_parser,
111- parse_leases,
112- )
113-
114-
115-class TestLeasesParser(MAASTestCase):
116-
117- def fake_parsed_lease(self, ip=None, mac=None, ends=None,
118- entry_type='lease'):
119- """Fake a lease as produced by the parser."""
120- if ip is None:
121- ip = factory.getRandomIPAddress()
122- if mac is None:
123- mac = factory.getRandomMACAddress()
124- Hardware = namedtuple('Hardware', ['mac'])
125- Lease = namedtuple(
126- 'Lease', ['lease_or_host', 'ip', 'hardware', 'ends'])
127- return Lease(entry_type, ip, Hardware(mac), ends)
128-
129- def fake_parsed_host(self, ip=None, mac=None):
130- """Fake a host declaration as produced by the parser."""
131- return self.fake_parsed_lease(ip=ip, mac=mac, entry_type='host')
132-
133- def fake_parsed_rubout(self, ip=None):
134- """Fake a "rubout" host declaration."""
135- if ip is None:
136- ip = factory.getRandomIPAddress()
137- Rubout = namedtuple('Rubout', ['lease_or_host', 'ip'])
138- return Rubout('host', ip)
139-
140- def test_get_expiry_date_parses_expiry_date(self):
141- lease = self.fake_parsed_lease(ends='0 2011/01/02 03:04:05')
142- self.assertEqual(
143- datetime(
144- year=2011, month=01, day=02,
145- hour=03, minute=04, second=05),
146- get_expiry_date(lease))
147-
148- def test_get_expiry_date_returns_None_for_never(self):
149- self.assertIsNone(
150- get_expiry_date(self.fake_parsed_lease(ends='never')))
151-
152- def test_get_expiry_date_returns_None_if_no_expiry_given(self):
153- self.assertIsNone(get_expiry_date(self.fake_parsed_lease(ends=None)))
154-
155- def test_has_expired_returns_False_for_eternal_lease(self):
156- now = datetime.utcnow()
157- self.assertFalse(has_expired(self.fake_parsed_lease(ends=None), now))
158-
159- def test_has_expired_returns_False_for_future_expiry_date(self):
160- now = datetime.utcnow()
161- later = '1 2035/12/31 23:59:59'
162- self.assertFalse(has_expired(self.fake_parsed_lease(ends=later), now))
163-
164- def test_has_expired_returns_True_for_past_expiry_date(self):
165- now = datetime.utcnow()
166- earlier = '1 2001/01/01 00:00:00'
167- self.assertTrue(
168- has_expired(self.fake_parsed_lease(ends=earlier), now))
169-
170- def test_gather_leases_finds_current_leases(self):
171- lease = self.fake_parsed_lease()
172- self.assertEqual(
173- {lease.ip: lease.hardware.mac},
174- gather_leases([lease]))
175-
176- def test_gather_leases_ignores_expired_leases(self):
177- earlier = '1 2001/01/01 00:00:00'
178- lease = self.fake_parsed_lease(ends=earlier)
179- self.assertEqual({}, gather_leases([lease]))
180-
181- def test_gather_leases_combines_expired_and_current_leases(self):
182- earlier = '1 2001/01/01 00:00:00'
183- ip = factory.getRandomIPAddress()
184- old_owner = factory.getRandomMACAddress()
185- new_owner = factory.getRandomMACAddress()
186- leases = [
187- self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
188- self.fake_parsed_lease(ip=ip, mac=new_owner),
189- ]
190- self.assertEqual({ip: new_owner}, gather_leases(leases))
191-
192- def test_gather_leases_ignores_ordering(self):
193- earlier = '1 2001/01/01 00:00:00'
194- ip = factory.getRandomIPAddress()
195- old_owner = factory.getRandomMACAddress()
196- new_owner = factory.getRandomMACAddress()
197- leases = [
198- self.fake_parsed_lease(ip=ip, mac=new_owner),
199- self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
200- ]
201- self.assertEqual({ip: new_owner}, gather_leases(leases))
202-
203- def test_gather_leases_ignores_host_declarations(self):
204- self.assertEqual({}, gather_leases([self.fake_parsed_host()]))
205-
206- def test_gather_hosts_finds_hosts(self):
207- host = self.fake_parsed_host()
208- self.assertEqual({host.ip: host.hardware.mac}, gather_hosts([host]))
209-
210- def test_gather_hosts_ignores_unaccompanied_rubouts(self):
211- self.assertEqual({}, gather_hosts([self.fake_parsed_rubout()]))
212-
213- def test_gather_hosts_ignores_rubbed_out_entries(self):
214- ip = factory.getRandomIPAddress()
215- hosts = [
216- self.fake_parsed_host(ip=ip),
217- self.fake_parsed_rubout(ip=ip),
218- ]
219- self.assertEqual({}, gather_hosts(hosts))
220-
221- def test_gather_hosts_follows_reassigned_host(self):
222- ip = factory.getRandomIPAddress()
223- new_owner = factory.getRandomMACAddress()
224- hosts = [
225- self.fake_parsed_host(ip=ip),
226- self.fake_parsed_rubout(ip=ip),
227- self.fake_parsed_host(ip=ip, mac=new_owner),
228- ]
229- self.assertEqual({ip: new_owner}, gather_hosts(hosts))
230-
231- def test_is_lease_and_is_host_recognize_lease(self):
232- params = {
233- 'ip': factory.getRandomIPAddress(),
234- 'mac': factory.getRandomMACAddress(),
235- }
236- [parsed_lease] = lease_parser.searchString(dedent("""\
237- lease %(ip)s {
238- hardware ethernet %(mac)s;
239- }
240- """ % params))
241- self.assertEqual(
242- (True, False),
243- (is_lease(parsed_lease), is_host(parsed_lease)))
244-
245- def test_is_lease_and_is_host_recognize_host(self):
246- params = {
247- 'ip': factory.getRandomIPAddress(),
248- 'mac': factory.getRandomMACAddress(),
249- }
250- [parsed_host] = lease_parser.searchString(dedent("""\
251- host %(ip)s {
252- hardware ethernet %(mac)s;
253- }
254- """ % params))
255- self.assertEqual(
256- (False, True),
257- (is_lease(parsed_host), is_host(parsed_host)))
258-
259- def test_get_host_mac_returns_None_for_host(self):
260- params = {
261- 'ip': factory.getRandomIPAddress(),
262- 'mac': factory.getRandomMACAddress(),
263- }
264- [parsed_host] = lease_parser.searchString(dedent("""\
265- host %(ip)s {
266- hardware ethernet %(mac)s;
267- }
268- """ % params))
269- self.assertEqual(params['mac'], get_host_mac(parsed_host))
270-
271- def test_get_host_mac_returns_None_for_rubout(self):
272- ip = factory.getRandomIPAddress()
273- [parsed_host] = lease_parser.searchString(dedent("""\
274- host %s {
275- deleted;
276- }
277- """ % ip))
278- self.assertIsNone(get_host_mac(parsed_host))
279-
280- def test_get_host_mac_returns_None_for_rubout_even_with_mac(self):
281- params = {
282- 'ip': factory.getRandomIPAddress(),
283- 'mac': factory.getRandomMACAddress(),
284- }
285- [parsed_host] = lease_parser.searchString(dedent("""\
286- host %(ip)s {
287- deleted;
288- hardware ethernet %(mac)s;
289- }
290- """ % params))
291- self.assertIsNone(get_host_mac(parsed_host))
292+ )
293+
294+
295+def fake_parsed_lease(ip=None, mac=None, ends=None,
296+ entry_type='lease'):
297+ """Fake a lease as produced by the parser."""
298+ if ip is None:
299+ ip = factory.getRandomIPAddress()
300+ if mac is None:
301+ mac = factory.getRandomMACAddress()
302+ Hardware = namedtuple('Hardware', ['mac'])
303+ Lease = namedtuple(
304+ 'Lease', ['lease_or_host', 'ip', 'hardware', 'ends'])
305+ return Lease(entry_type, ip, Hardware(mac), ends)
306+
307+
308+def fake_parsed_host(ip=None, mac=None):
309+ """Fake a host declaration as produced by the parser."""
310+ return fake_parsed_lease(ip=ip, mac=mac, entry_type='host')
311+
312+
313+def fake_parsed_rubout(ip=None):
314+ """Fake a "rubout" host declaration."""
315+ if ip is None:
316+ ip = factory.getRandomIPAddress()
317+ Rubout = namedtuple('Rubout', ['lease_or_host', 'ip'])
318+ return Rubout('host', ip)
319+
320+
321+class TestLeasesParsers(MAASTestCase):
322+
323+ scenarios = (
324+ ("original", dict(parse=leases_parser.parse_leases)),
325+ ("fast", dict(parse=leases_parser_fast.parse_leases)),
326+ )
327
328 def test_parse_leases_copes_with_empty_file(self):
329- self.assertEqual({}, parse_leases(""))
330+ self.assertEqual({}, self.parse(""))
331
332 def test_parse_leases_parses_lease(self):
333 params = {
334 'ip': factory.getRandomIPAddress(),
335 'mac': factory.getRandomMACAddress(),
336 }
337- leases = parse_leases(dedent("""\
338+ leases = self.parse(dedent("""\
339 lease %(ip)s {
340 starts 5 2010/01/01 00:00:01;
341 ends never;
342@@ -254,7 +112,7 @@
343 'ip': factory.getRandomIPAddress(),
344 'mac': factory.getRandomMACAddress(),
345 }
346- leases = parse_leases(dedent("""\
347+ leases = self.parse(dedent("""\
348 host %(ip)s {
349 dynamic;
350 hardware ethernet %(mac)s;
351@@ -263,8 +121,36 @@
352 """ % params))
353 self.assertEqual({params['ip']: params['mac']}, leases)
354
355+ def test_parse_leases_copes_with_misleading_values(self):
356+ params = {
357+ 'ip1': factory.getRandomIPAddress(),
358+ 'mac1': factory.getRandomMACAddress(),
359+ 'ip2': factory.getRandomIPAddress(),
360+ 'mac2': factory.getRandomMACAddress(),
361+ }
362+ leases = self.parse(dedent("""\
363+ host %(ip1)s {
364+ dynamic;
365+ ### NOTE the following value has a closing brace, and
366+ ### also looks like a host record.
367+ uid "foo}host 12.34.56.78 { }";
368+ hardware ethernet %(mac1)s;
369+ fixed-address %(ip1)s;
370+ }
371+ ### NOTE the extra indent on the line below.
372+ host %(ip2)s {
373+ dynamic;
374+ hardware ethernet %(mac2)s;
375+ fixed-address %(ip2)s;
376+ }
377+ """ % params))
378+ self.assertEqual(
379+ {params['ip1']: params['mac1'],
380+ params['ip2']: params['mac2']},
381+ leases)
382+
383 def test_parse_leases_parses_host_rubout(self):
384- leases = parse_leases(dedent("""\
385+ leases = self.parse(dedent("""\
386 host %s {
387 deleted;
388 }
389@@ -277,7 +163,7 @@
390 'mac': factory.getRandomMACAddress(),
391 'incomplete_ip': factory.getRandomIPAddress(),
392 }
393- leases = parse_leases(dedent("""\
394+ leases = self.parse(dedent("""\
395 lease %(ip)s {
396 hardware ethernet %(mac)s;
397 }
398@@ -291,7 +177,7 @@
399 'ip': factory.getRandomIPAddress(),
400 'mac': factory.getRandomMACAddress(),
401 }
402- leases = parse_leases(dedent("""\
403+ leases = self.parse(dedent("""\
404 # Top comment (ignored).
405 lease %(ip)s { # End-of-line comment (ignored).
406 # Comment in lease block (ignored).
407@@ -306,7 +192,7 @@
408 'ip': factory.getRandomIPAddress(),
409 'mac': factory.getRandomMACAddress(),
410 }
411- leases = parse_leases(dedent("""\
412+ leases = self.parse(dedent("""\
413 lease %(ip)s {
414 hardware ethernet %(mac)s;
415 ends 1 2001/01/01 00:00:00;
416@@ -319,7 +205,7 @@
417 'ip': factory.getRandomIPAddress(),
418 'mac': factory.getRandomMACAddress(),
419 }
420- leases = parse_leases(dedent("""\
421+ leases = self.parse(dedent("""\
422 lease %(ip)s {
423 hardware ethernet %(mac)s;
424 ends never;
425@@ -332,7 +218,7 @@
426 'ip': factory.getRandomIPAddress(),
427 'mac': factory.getRandomMACAddress(),
428 }
429- leases = parse_leases(dedent("""\
430+ leases = self.parse(dedent("""\
431 lease %(ip)s {
432 hardware ethernet %(mac)s;
433 }
434@@ -345,7 +231,7 @@
435 'old_owner': factory.getRandomMACAddress(),
436 'new_owner': factory.getRandomMACAddress(),
437 }
438- leases = parse_leases(dedent("""\
439+ leases = self.parse(dedent("""\
440 lease %(ip)s {
441 hardware ethernet %(old_owner)s;
442 }
443@@ -360,7 +246,7 @@
444 'ip': factory.getRandomIPAddress(),
445 'mac': factory.getRandomMACAddress(),
446 }
447- leases = parse_leases(dedent("""\
448+ leases = self.parse(dedent("""\
449 host %(ip)s {
450 dynamic;
451 hardware ethernet %(mac)s;
452@@ -375,7 +261,7 @@
453 'ip': factory.getRandomIPAddress(),
454 'mac': factory.getRandomMACAddress(),
455 }
456- leases = parse_leases(dedent("""\
457+ leases = self.parse(dedent("""\
458 host %(ip)s {
459 hardware ethernet %(mac)s;
460 fixed-address %(ip)s;
461@@ -383,13 +269,235 @@
462 """ % params))
463 self.assertEqual({params['ip']: params['mac']}, leases)
464
465+
466+class TestLeasesParserFast(MAASTestCase):
467+
468+ def test_expired_lease_does_not_shadow_earlier_host_stanza(self):
469+ params = {
470+ 'ip': factory.getRandomIPAddress(),
471+ 'mac1': factory.getRandomMACAddress(),
472+ 'mac2': factory.getRandomMACAddress(),
473+ }
474+ leases = leases_parser_fast.parse_leases(dedent("""\
475+ host %(ip)s {
476+ dynamic;
477+ hardware ethernet %(mac1)s;
478+ fixed-address %(ip)s;
479+ }
480+ lease %(ip)s {
481+ starts 5 2010/01/01 00:00:01;
482+ ends 1 2010/01/01 00:00:02;
483+ hardware ethernet %(mac2)s;
484+ }
485+ """ % params))
486+ # The lease has expired so it doesn't shadow the host stanza,
487+ # and so the MAC returned is from the host stanza.
488+ self.assertEqual({params["ip"]: params["mac1"]}, leases)
489+
490+ def test_active_lease_shadows_earlier_host_stanza(self):
491+ params = {
492+ 'ip': factory.getRandomIPAddress(),
493+ 'mac1': factory.getRandomMACAddress(),
494+ 'mac2': factory.getRandomMACAddress(),
495+ }
496+ leases = leases_parser_fast.parse_leases(dedent("""\
497+ host %(ip)s {
498+ dynamic;
499+ hardware ethernet %(mac1)s;
500+ fixed-address %(ip)s;
501+ }
502+ lease %(ip)s {
503+ starts 5 2010/01/01 00:00:01;
504+ hardware ethernet %(mac2)s;
505+ }
506+ """ % params))
507+ # The lease hasn't expired, so shadows the earlier host stanza.
508+ self.assertEqual({params["ip"]: params["mac2"]}, leases)
509+
510+ def test_host_stanza_shadows_earlier_active_lease(self):
511+ params = {
512+ 'ip': factory.getRandomIPAddress(),
513+ 'mac1': factory.getRandomMACAddress(),
514+ 'mac2': factory.getRandomMACAddress(),
515+ }
516+ leases = leases_parser_fast.parse_leases(dedent("""\
517+ lease %(ip)s {
518+ starts 5 2010/01/01 00:00:01;
519+ hardware ethernet %(mac2)s;
520+ }
521+ host %(ip)s {
522+ dynamic;
523+ hardware ethernet %(mac1)s;
524+ fixed-address %(ip)s;
525+ }
526+ """ % params))
527+ # The lease hasn't expired, but the host entry is later, so it
528+ # shadows the earlier lease stanza.
529+ self.assertEqual({params["ip"]: params["mac1"]}, leases)
530+
531+
532+class TestLeasesParserFunctions(MAASTestCase):
533+
534+ def test_get_expiry_date_parses_expiry_date(self):
535+ lease = fake_parsed_lease(ends='0 2011/01/02 03:04:05')
536+ self.assertEqual(
537+ datetime(
538+ year=2011, month=01, day=02,
539+ hour=03, minute=04, second=05),
540+ get_expiry_date(lease))
541+
542+ def test_get_expiry_date_returns_None_for_never(self):
543+ self.assertIsNone(
544+ get_expiry_date(fake_parsed_lease(ends='never')))
545+
546+ def test_get_expiry_date_returns_None_if_no_expiry_given(self):
547+ self.assertIsNone(get_expiry_date(fake_parsed_lease(ends=None)))
548+
549+ def test_has_expired_returns_False_for_eternal_lease(self):
550+ now = datetime.utcnow()
551+ self.assertFalse(has_expired(fake_parsed_lease(ends=None), now))
552+
553+ def test_has_expired_returns_False_for_future_expiry_date(self):
554+ now = datetime.utcnow()
555+ later = '1 2035/12/31 23:59:59'
556+ self.assertFalse(has_expired(fake_parsed_lease(ends=later), now))
557+
558+ def test_has_expired_returns_True_for_past_expiry_date(self):
559+ now = datetime.utcnow()
560+ earlier = '1 2001/01/01 00:00:00'
561+ self.assertTrue(
562+ has_expired(fake_parsed_lease(ends=earlier), now))
563+
564+ def test_gather_leases_finds_current_leases(self):
565+ lease = fake_parsed_lease()
566+ self.assertEqual(
567+ {lease.ip: lease.hardware.mac},
568+ gather_leases([lease]))
569+
570+ def test_gather_leases_ignores_expired_leases(self):
571+ earlier = '1 2001/01/01 00:00:00'
572+ lease = fake_parsed_lease(ends=earlier)
573+ self.assertEqual({}, gather_leases([lease]))
574+
575+ def test_gather_leases_combines_expired_and_current_leases(self):
576+ earlier = '1 2001/01/01 00:00:00'
577+ ip = factory.getRandomIPAddress()
578+ old_owner = factory.getRandomMACAddress()
579+ new_owner = factory.getRandomMACAddress()
580+ leases = [
581+ fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
582+ fake_parsed_lease(ip=ip, mac=new_owner),
583+ ]
584+ self.assertEqual({ip: new_owner}, gather_leases(leases))
585+
586+ def test_gather_leases_ignores_ordering(self):
587+ earlier = '1 2001/01/01 00:00:00'
588+ ip = factory.getRandomIPAddress()
589+ old_owner = factory.getRandomMACAddress()
590+ new_owner = factory.getRandomMACAddress()
591+ leases = [
592+ fake_parsed_lease(ip=ip, mac=new_owner),
593+ fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
594+ ]
595+ self.assertEqual({ip: new_owner}, gather_leases(leases))
596+
597+ def test_gather_leases_ignores_host_declarations(self):
598+ self.assertEqual({}, gather_leases([fake_parsed_host()]))
599+
600+ def test_gather_hosts_finds_hosts(self):
601+ host = fake_parsed_host()
602+ self.assertEqual({host.ip: host.hardware.mac}, gather_hosts([host]))
603+
604+ def test_gather_hosts_ignores_unaccompanied_rubouts(self):
605+ self.assertEqual({}, gather_hosts([fake_parsed_rubout()]))
606+
607+ def test_gather_hosts_ignores_rubbed_out_entries(self):
608+ ip = factory.getRandomIPAddress()
609+ hosts = [
610+ fake_parsed_host(ip=ip),
611+ fake_parsed_rubout(ip=ip),
612+ ]
613+ self.assertEqual({}, gather_hosts(hosts))
614+
615+ def test_gather_hosts_follows_reassigned_host(self):
616+ ip = factory.getRandomIPAddress()
617+ new_owner = factory.getRandomMACAddress()
618+ hosts = [
619+ fake_parsed_host(ip=ip),
620+ fake_parsed_rubout(ip=ip),
621+ fake_parsed_host(ip=ip, mac=new_owner),
622+ ]
623+ self.assertEqual({ip: new_owner}, gather_hosts(hosts))
624+
625+ def test_is_lease_and_is_host_recognize_lease(self):
626+ params = {
627+ 'ip': factory.getRandomIPAddress(),
628+ 'mac': factory.getRandomMACAddress(),
629+ }
630+ [parsed_lease] = lease_parser.searchString(dedent("""\
631+ lease %(ip)s {
632+ hardware ethernet %(mac)s;
633+ }
634+ """ % params))
635+ self.assertEqual(
636+ (True, False),
637+ (is_lease(parsed_lease), is_host(parsed_lease)))
638+
639+ def test_is_lease_and_is_host_recognize_host(self):
640+ params = {
641+ 'ip': factory.getRandomIPAddress(),
642+ 'mac': factory.getRandomMACAddress(),
643+ }
644+ [parsed_host] = lease_parser.searchString(dedent("""\
645+ host %(ip)s {
646+ hardware ethernet %(mac)s;
647+ }
648+ """ % params))
649+ self.assertEqual(
650+ (False, True),
651+ (is_lease(parsed_host), is_host(parsed_host)))
652+
653+ def test_get_host_mac_returns_None_for_host(self):
654+ params = {
655+ 'ip': factory.getRandomIPAddress(),
656+ 'mac': factory.getRandomMACAddress(),
657+ }
658+ [parsed_host] = lease_parser.searchString(dedent("""\
659+ host %(ip)s {
660+ hardware ethernet %(mac)s;
661+ }
662+ """ % params))
663+ self.assertEqual(params['mac'], get_host_mac(parsed_host))
664+
665+ def test_get_host_mac_returns_None_for_rubout(self):
666+ ip = factory.getRandomIPAddress()
667+ [parsed_host] = lease_parser.searchString(dedent("""\
668+ host %s {
669+ deleted;
670+ }
671+ """ % ip))
672+ self.assertIsNone(get_host_mac(parsed_host))
673+
674+ def test_get_host_mac_returns_None_for_rubout_even_with_mac(self):
675+ params = {
676+ 'ip': factory.getRandomIPAddress(),
677+ 'mac': factory.getRandomMACAddress(),
678+ }
679+ [parsed_host] = lease_parser.searchString(dedent("""\
680+ host %(ip)s {
681+ deleted;
682+ hardware ethernet %(mac)s;
683+ }
684+ """ % params))
685+ self.assertIsNone(get_host_mac(parsed_host))
686+
687 def test_combine_entries_accepts_host_followed_by_expired_lease(self):
688 ip = factory.getRandomIPAddress()
689 mac = factory.getRandomMACAddress()
690 earlier = '1 2001/01/01 00:00:00'
691 entries = [
692- self.fake_parsed_host(ip=ip, mac=mac),
693- self.fake_parsed_lease(ip=ip, ends=earlier),
694+ fake_parsed_host(ip=ip, mac=mac),
695+ fake_parsed_lease(ip=ip, ends=earlier),
696 ]
697 self.assertEqual({ip: mac}, combine_entries(entries))
698
699@@ -398,8 +506,8 @@
700 mac = factory.getRandomMACAddress()
701 earlier = '1 2001/01/01 00:00:00'
702 entries = [
703- self.fake_parsed_lease(ip=ip, ends=earlier),
704- self.fake_parsed_host(ip=ip, mac=mac),
705+ fake_parsed_lease(ip=ip, ends=earlier),
706+ fake_parsed_host(ip=ip, mac=mac),
707 ]
708 self.assertEqual({ip: mac}, combine_entries(entries))
709
710@@ -407,9 +515,9 @@
711 ip = factory.getRandomIPAddress()
712 mac = factory.getRandomMACAddress()
713 entries = [
714- self.fake_parsed_host(ip=ip),
715- self.fake_parsed_rubout(ip=ip),
716- self.fake_parsed_lease(ip=ip, mac=mac),
717+ fake_parsed_host(ip=ip),
718+ fake_parsed_rubout(ip=ip),
719+ fake_parsed_lease(ip=ip, mac=mac),
720 ]
721 self.assertEqual({ip: mac}, combine_entries(entries))
722
723@@ -418,9 +526,9 @@
724 mac = factory.getRandomMACAddress()
725 earlier = '1 2001/01/01 00:00:00'
726 entries = [
727- self.fake_parsed_host(ip=ip),
728- self.fake_parsed_rubout(ip=ip),
729- self.fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
730+ fake_parsed_host(ip=ip),
731+ fake_parsed_rubout(ip=ip),
732+ fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
733 ]
734 self.assertEqual({}, combine_entries(entries))
735
736@@ -429,9 +537,9 @@
737 mac = factory.getRandomMACAddress()
738 earlier = '1 2001/01/01 00:00:00'
739 entries = [
740- self.fake_parsed_host(ip=ip),
741- self.fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
742- self.fake_parsed_rubout(ip=ip),
743+ fake_parsed_host(ip=ip),
744+ fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
745+ fake_parsed_rubout(ip=ip),
746 ]
747 self.assertEqual({}, combine_entries(entries))
748
749@@ -439,9 +547,9 @@
750 ip = factory.getRandomIPAddress()
751 mac = factory.getRandomMACAddress()
752 entries = [
753- self.fake_parsed_host(ip=ip),
754- self.fake_parsed_lease(ip=ip, mac=mac),
755- self.fake_parsed_rubout(ip=ip),
756+ fake_parsed_host(ip=ip),
757+ fake_parsed_lease(ip=ip, mac=mac),
758+ fake_parsed_rubout(ip=ip),
759 ]
760 self.assertEqual({ip: mac}, combine_entries(entries))
761
762@@ -449,8 +557,8 @@
763 ip = factory.getRandomIPAddress()
764 mac = factory.getRandomMACAddress()
765 entries = [
766- self.fake_parsed_host(ip=ip),
767- self.fake_parsed_rubout(ip=ip),
768- self.fake_parsed_host(ip=ip, mac=mac),
769+ fake_parsed_host(ip=ip),
770+ fake_parsed_rubout(ip=ip),
771+ fake_parsed_host(ip=ip, mac=mac),
772 ]
773 self.assertEqual({ip: mac}, combine_entries(entries))

Subscribers

People subscribed via source and target branches

to all changes: