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
=== added file 'src/provisioningserver/dhcp/leases_parser_fast.py'
--- src/provisioningserver/dhcp/leases_parser_fast.py 1970-01-01 00:00:00 +0000
+++ src/provisioningserver/dhcp/leases_parser_fast.py 2014-05-19 15:16:18 +0000
@@ -0,0 +1,87 @@
1# Copyright 2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""A speedier version of `leases_parser`.
5
6This extracts the relevant stanzas from a leases file, keeping only the
7most recent "host" and "lease" entries, then uses the existing and
8properly defined but slow parser to parse them. This massively speeds up
9parsing a leases file that contains a modest number of unique host and
10lease entries, but has become very large because of churn.
11"""
12
13from __future__ import (
14 absolute_import,
15 print_function,
16 unicode_literals,
17 )
18
19str = None
20
21__metaclass__ = type
22__all__ = [
23 'parse_leases',
24 ]
25
26from collections import defaultdict
27from datetime import datetime
28from itertools import chain
29import re
30
31from provisioningserver.dhcp.leases_parser import (
32 get_host_mac,
33 has_expired,
34 is_host,
35 is_lease,
36 lease_parser,
37 )
38
39
40re_entry = re.compile(
41 r'''
42 ^\s* # Ignore leading whitespace on each line.
43 (host|lease) # Look only for host or lease stanzas.
44 \s+ # Mandatory whitespace.
45 ([0-9a-fA-F.:]+) # Capture the IP/MAC address for this stanza.
46 \s*{ # Optional whitespace then an opening brace.
47 ''',
48 re.MULTILINE | re.DOTALL | re.VERBOSE)
49
50
51def find_lease_starts(leases_contents):
52 results = defaultdict(dict)
53 for match in re_entry.finditer(leases_contents):
54 stanza, address = match.groups()
55 results[stanza][address] = match.start()
56 return chain.from_iterable(
57 mapping.itervalues() for mapping in results.itervalues())
58
59
60def extract_leases(leases_contents):
61 starts = find_lease_starts(leases_contents)
62 for start in sorted(starts):
63 record = lease_parser.scanString(leases_contents[start:])
64 try:
65 token, _, _ = next(record)
66 except StopIteration:
67 pass
68 else:
69 yield token
70
71
72def parse_leases(leases_contents):
73 results = {}
74 now = datetime.utcnow()
75 for entry in extract_leases(leases_contents):
76 if is_lease(entry):
77 if not has_expired(entry, now):
78 results[entry.ip] = entry.hardware.mac
79 elif is_host(entry):
80 mac = get_host_mac(entry)
81 if mac is None:
82 # TODO: Test this.
83 if entry.ip in results:
84 del results[entry.ip]
85 else:
86 results[entry.ip] = mac
87 return results
088
=== modified file 'src/provisioningserver/dhcp/tests/test_leases_parser.py'
--- src/provisioningserver/dhcp/tests/test_leases_parser.py 2013-10-07 09:12:40 +0000
+++ src/provisioningserver/dhcp/tests/test_leases_parser.py 2014-05-19 15:16:18 +0000
@@ -20,6 +20,10 @@
2020
21from maastesting.factory import factory21from maastesting.factory import factory
22from maastesting.testcase import MAASTestCase22from maastesting.testcase import MAASTestCase
23from provisioningserver.dhcp import (
24 leases_parser,
25 leases_parser_fast,
26 )
23from provisioningserver.dhcp.leases_parser import (27from provisioningserver.dhcp.leases_parser import (
24 combine_entries,28 combine_entries,
25 gather_hosts,29 gather_hosts,
@@ -30,197 +34,51 @@
30 is_host,34 is_host,
31 is_lease,35 is_lease,
32 lease_parser,36 lease_parser,
33 parse_leases,37 )
34 )38
3539
3640def fake_parsed_lease(ip=None, mac=None, ends=None,
37class TestLeasesParser(MAASTestCase):41 entry_type='lease'):
3842 """Fake a lease as produced by the parser."""
39 def fake_parsed_lease(self, ip=None, mac=None, ends=None,43 if ip is None:
40 entry_type='lease'):44 ip = factory.getRandomIPAddress()
41 """Fake a lease as produced by the parser."""45 if mac is None:
42 if ip is None:46 mac = factory.getRandomMACAddress()
43 ip = factory.getRandomIPAddress()47 Hardware = namedtuple('Hardware', ['mac'])
44 if mac is None:48 Lease = namedtuple(
45 mac = factory.getRandomMACAddress()49 'Lease', ['lease_or_host', 'ip', 'hardware', 'ends'])
46 Hardware = namedtuple('Hardware', ['mac'])50 return Lease(entry_type, ip, Hardware(mac), ends)
47 Lease = namedtuple(51
48 'Lease', ['lease_or_host', 'ip', 'hardware', 'ends'])52
49 return Lease(entry_type, ip, Hardware(mac), ends)53def fake_parsed_host(ip=None, mac=None):
5054 """Fake a host declaration as produced by the parser."""
51 def fake_parsed_host(self, ip=None, mac=None):55 return fake_parsed_lease(ip=ip, mac=mac, entry_type='host')
52 """Fake a host declaration as produced by the parser."""56
53 return self.fake_parsed_lease(ip=ip, mac=mac, entry_type='host')57
5458def fake_parsed_rubout(ip=None):
55 def fake_parsed_rubout(self, ip=None):59 """Fake a "rubout" host declaration."""
56 """Fake a "rubout" host declaration."""60 if ip is None:
57 if ip is None:61 ip = factory.getRandomIPAddress()
58 ip = factory.getRandomIPAddress()62 Rubout = namedtuple('Rubout', ['lease_or_host', 'ip'])
59 Rubout = namedtuple('Rubout', ['lease_or_host', 'ip'])63 return Rubout('host', ip)
60 return Rubout('host', ip)64
6165
62 def test_get_expiry_date_parses_expiry_date(self):66class TestLeasesParsers(MAASTestCase):
63 lease = self.fake_parsed_lease(ends='0 2011/01/02 03:04:05')67
64 self.assertEqual(68 scenarios = (
65 datetime(69 ("original", dict(parse=leases_parser.parse_leases)),
66 year=2011, month=01, day=02,70 ("fast", dict(parse=leases_parser_fast.parse_leases)),
67 hour=03, minute=04, second=05),71 )
68 get_expiry_date(lease))
69
70 def test_get_expiry_date_returns_None_for_never(self):
71 self.assertIsNone(
72 get_expiry_date(self.fake_parsed_lease(ends='never')))
73
74 def test_get_expiry_date_returns_None_if_no_expiry_given(self):
75 self.assertIsNone(get_expiry_date(self.fake_parsed_lease(ends=None)))
76
77 def test_has_expired_returns_False_for_eternal_lease(self):
78 now = datetime.utcnow()
79 self.assertFalse(has_expired(self.fake_parsed_lease(ends=None), now))
80
81 def test_has_expired_returns_False_for_future_expiry_date(self):
82 now = datetime.utcnow()
83 later = '1 2035/12/31 23:59:59'
84 self.assertFalse(has_expired(self.fake_parsed_lease(ends=later), now))
85
86 def test_has_expired_returns_True_for_past_expiry_date(self):
87 now = datetime.utcnow()
88 earlier = '1 2001/01/01 00:00:00'
89 self.assertTrue(
90 has_expired(self.fake_parsed_lease(ends=earlier), now))
91
92 def test_gather_leases_finds_current_leases(self):
93 lease = self.fake_parsed_lease()
94 self.assertEqual(
95 {lease.ip: lease.hardware.mac},
96 gather_leases([lease]))
97
98 def test_gather_leases_ignores_expired_leases(self):
99 earlier = '1 2001/01/01 00:00:00'
100 lease = self.fake_parsed_lease(ends=earlier)
101 self.assertEqual({}, gather_leases([lease]))
102
103 def test_gather_leases_combines_expired_and_current_leases(self):
104 earlier = '1 2001/01/01 00:00:00'
105 ip = factory.getRandomIPAddress()
106 old_owner = factory.getRandomMACAddress()
107 new_owner = factory.getRandomMACAddress()
108 leases = [
109 self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
110 self.fake_parsed_lease(ip=ip, mac=new_owner),
111 ]
112 self.assertEqual({ip: new_owner}, gather_leases(leases))
113
114 def test_gather_leases_ignores_ordering(self):
115 earlier = '1 2001/01/01 00:00:00'
116 ip = factory.getRandomIPAddress()
117 old_owner = factory.getRandomMACAddress()
118 new_owner = factory.getRandomMACAddress()
119 leases = [
120 self.fake_parsed_lease(ip=ip, mac=new_owner),
121 self.fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
122 ]
123 self.assertEqual({ip: new_owner}, gather_leases(leases))
124
125 def test_gather_leases_ignores_host_declarations(self):
126 self.assertEqual({}, gather_leases([self.fake_parsed_host()]))
127
128 def test_gather_hosts_finds_hosts(self):
129 host = self.fake_parsed_host()
130 self.assertEqual({host.ip: host.hardware.mac}, gather_hosts([host]))
131
132 def test_gather_hosts_ignores_unaccompanied_rubouts(self):
133 self.assertEqual({}, gather_hosts([self.fake_parsed_rubout()]))
134
135 def test_gather_hosts_ignores_rubbed_out_entries(self):
136 ip = factory.getRandomIPAddress()
137 hosts = [
138 self.fake_parsed_host(ip=ip),
139 self.fake_parsed_rubout(ip=ip),
140 ]
141 self.assertEqual({}, gather_hosts(hosts))
142
143 def test_gather_hosts_follows_reassigned_host(self):
144 ip = factory.getRandomIPAddress()
145 new_owner = factory.getRandomMACAddress()
146 hosts = [
147 self.fake_parsed_host(ip=ip),
148 self.fake_parsed_rubout(ip=ip),
149 self.fake_parsed_host(ip=ip, mac=new_owner),
150 ]
151 self.assertEqual({ip: new_owner}, gather_hosts(hosts))
152
153 def test_is_lease_and_is_host_recognize_lease(self):
154 params = {
155 'ip': factory.getRandomIPAddress(),
156 'mac': factory.getRandomMACAddress(),
157 }
158 [parsed_lease] = lease_parser.searchString(dedent("""\
159 lease %(ip)s {
160 hardware ethernet %(mac)s;
161 }
162 """ % params))
163 self.assertEqual(
164 (True, False),
165 (is_lease(parsed_lease), is_host(parsed_lease)))
166
167 def test_is_lease_and_is_host_recognize_host(self):
168 params = {
169 'ip': factory.getRandomIPAddress(),
170 'mac': factory.getRandomMACAddress(),
171 }
172 [parsed_host] = lease_parser.searchString(dedent("""\
173 host %(ip)s {
174 hardware ethernet %(mac)s;
175 }
176 """ % params))
177 self.assertEqual(
178 (False, True),
179 (is_lease(parsed_host), is_host(parsed_host)))
180
181 def test_get_host_mac_returns_None_for_host(self):
182 params = {
183 'ip': factory.getRandomIPAddress(),
184 'mac': factory.getRandomMACAddress(),
185 }
186 [parsed_host] = lease_parser.searchString(dedent("""\
187 host %(ip)s {
188 hardware ethernet %(mac)s;
189 }
190 """ % params))
191 self.assertEqual(params['mac'], get_host_mac(parsed_host))
192
193 def test_get_host_mac_returns_None_for_rubout(self):
194 ip = factory.getRandomIPAddress()
195 [parsed_host] = lease_parser.searchString(dedent("""\
196 host %s {
197 deleted;
198 }
199 """ % ip))
200 self.assertIsNone(get_host_mac(parsed_host))
201
202 def test_get_host_mac_returns_None_for_rubout_even_with_mac(self):
203 params = {
204 'ip': factory.getRandomIPAddress(),
205 'mac': factory.getRandomMACAddress(),
206 }
207 [parsed_host] = lease_parser.searchString(dedent("""\
208 host %(ip)s {
209 deleted;
210 hardware ethernet %(mac)s;
211 }
212 """ % params))
213 self.assertIsNone(get_host_mac(parsed_host))
21472
215 def test_parse_leases_copes_with_empty_file(self):73 def test_parse_leases_copes_with_empty_file(self):
216 self.assertEqual({}, parse_leases(""))74 self.assertEqual({}, self.parse(""))
21775
218 def test_parse_leases_parses_lease(self):76 def test_parse_leases_parses_lease(self):
219 params = {77 params = {
220 'ip': factory.getRandomIPAddress(),78 'ip': factory.getRandomIPAddress(),
221 'mac': factory.getRandomMACAddress(),79 'mac': factory.getRandomMACAddress(),
222 }80 }
223 leases = parse_leases(dedent("""\81 leases = self.parse(dedent("""\
224 lease %(ip)s {82 lease %(ip)s {
225 starts 5 2010/01/01 00:00:01;83 starts 5 2010/01/01 00:00:01;
226 ends never;84 ends never;
@@ -254,7 +112,7 @@
254 'ip': factory.getRandomIPAddress(),112 'ip': factory.getRandomIPAddress(),
255 'mac': factory.getRandomMACAddress(),113 'mac': factory.getRandomMACAddress(),
256 }114 }
257 leases = parse_leases(dedent("""\115 leases = self.parse(dedent("""\
258 host %(ip)s {116 host %(ip)s {
259 dynamic;117 dynamic;
260 hardware ethernet %(mac)s;118 hardware ethernet %(mac)s;
@@ -263,8 +121,36 @@
263 """ % params))121 """ % params))
264 self.assertEqual({params['ip']: params['mac']}, leases)122 self.assertEqual({params['ip']: params['mac']}, leases)
265123
124 def test_parse_leases_copes_with_misleading_values(self):
125 params = {
126 'ip1': factory.getRandomIPAddress(),
127 'mac1': factory.getRandomMACAddress(),
128 'ip2': factory.getRandomIPAddress(),
129 'mac2': factory.getRandomMACAddress(),
130 }
131 leases = self.parse(dedent("""\
132 host %(ip1)s {
133 dynamic;
134 ### NOTE the following value has a closing brace, and
135 ### also looks like a host record.
136 uid "foo}host 12.34.56.78 { }";
137 hardware ethernet %(mac1)s;
138 fixed-address %(ip1)s;
139 }
140 ### NOTE the extra indent on the line below.
141 host %(ip2)s {
142 dynamic;
143 hardware ethernet %(mac2)s;
144 fixed-address %(ip2)s;
145 }
146 """ % params))
147 self.assertEqual(
148 {params['ip1']: params['mac1'],
149 params['ip2']: params['mac2']},
150 leases)
151
266 def test_parse_leases_parses_host_rubout(self):152 def test_parse_leases_parses_host_rubout(self):
267 leases = parse_leases(dedent("""\153 leases = self.parse(dedent("""\
268 host %s {154 host %s {
269 deleted;155 deleted;
270 }156 }
@@ -277,7 +163,7 @@
277 'mac': factory.getRandomMACAddress(),163 'mac': factory.getRandomMACAddress(),
278 'incomplete_ip': factory.getRandomIPAddress(),164 'incomplete_ip': factory.getRandomIPAddress(),
279 }165 }
280 leases = parse_leases(dedent("""\166 leases = self.parse(dedent("""\
281 lease %(ip)s {167 lease %(ip)s {
282 hardware ethernet %(mac)s;168 hardware ethernet %(mac)s;
283 }169 }
@@ -291,7 +177,7 @@
291 'ip': factory.getRandomIPAddress(),177 'ip': factory.getRandomIPAddress(),
292 'mac': factory.getRandomMACAddress(),178 'mac': factory.getRandomMACAddress(),
293 }179 }
294 leases = parse_leases(dedent("""\180 leases = self.parse(dedent("""\
295 # Top comment (ignored).181 # Top comment (ignored).
296 lease %(ip)s { # End-of-line comment (ignored).182 lease %(ip)s { # End-of-line comment (ignored).
297 # Comment in lease block (ignored).183 # Comment in lease block (ignored).
@@ -306,7 +192,7 @@
306 'ip': factory.getRandomIPAddress(),192 'ip': factory.getRandomIPAddress(),
307 'mac': factory.getRandomMACAddress(),193 'mac': factory.getRandomMACAddress(),
308 }194 }
309 leases = parse_leases(dedent("""\195 leases = self.parse(dedent("""\
310 lease %(ip)s {196 lease %(ip)s {
311 hardware ethernet %(mac)s;197 hardware ethernet %(mac)s;
312 ends 1 2001/01/01 00:00:00;198 ends 1 2001/01/01 00:00:00;
@@ -319,7 +205,7 @@
319 'ip': factory.getRandomIPAddress(),205 'ip': factory.getRandomIPAddress(),
320 'mac': factory.getRandomMACAddress(),206 'mac': factory.getRandomMACAddress(),
321 }207 }
322 leases = parse_leases(dedent("""\208 leases = self.parse(dedent("""\
323 lease %(ip)s {209 lease %(ip)s {
324 hardware ethernet %(mac)s;210 hardware ethernet %(mac)s;
325 ends never;211 ends never;
@@ -332,7 +218,7 @@
332 'ip': factory.getRandomIPAddress(),218 'ip': factory.getRandomIPAddress(),
333 'mac': factory.getRandomMACAddress(),219 'mac': factory.getRandomMACAddress(),
334 }220 }
335 leases = parse_leases(dedent("""\221 leases = self.parse(dedent("""\
336 lease %(ip)s {222 lease %(ip)s {
337 hardware ethernet %(mac)s;223 hardware ethernet %(mac)s;
338 }224 }
@@ -345,7 +231,7 @@
345 'old_owner': factory.getRandomMACAddress(),231 'old_owner': factory.getRandomMACAddress(),
346 'new_owner': factory.getRandomMACAddress(),232 'new_owner': factory.getRandomMACAddress(),
347 }233 }
348 leases = parse_leases(dedent("""\234 leases = self.parse(dedent("""\
349 lease %(ip)s {235 lease %(ip)s {
350 hardware ethernet %(old_owner)s;236 hardware ethernet %(old_owner)s;
351 }237 }
@@ -360,7 +246,7 @@
360 'ip': factory.getRandomIPAddress(),246 'ip': factory.getRandomIPAddress(),
361 'mac': factory.getRandomMACAddress(),247 'mac': factory.getRandomMACAddress(),
362 }248 }
363 leases = parse_leases(dedent("""\249 leases = self.parse(dedent("""\
364 host %(ip)s {250 host %(ip)s {
365 dynamic;251 dynamic;
366 hardware ethernet %(mac)s;252 hardware ethernet %(mac)s;
@@ -375,7 +261,7 @@
375 'ip': factory.getRandomIPAddress(),261 'ip': factory.getRandomIPAddress(),
376 'mac': factory.getRandomMACAddress(),262 'mac': factory.getRandomMACAddress(),
377 }263 }
378 leases = parse_leases(dedent("""\264 leases = self.parse(dedent("""\
379 host %(ip)s {265 host %(ip)s {
380 hardware ethernet %(mac)s;266 hardware ethernet %(mac)s;
381 fixed-address %(ip)s;267 fixed-address %(ip)s;
@@ -383,13 +269,235 @@
383 """ % params))269 """ % params))
384 self.assertEqual({params['ip']: params['mac']}, leases)270 self.assertEqual({params['ip']: params['mac']}, leases)
385271
272
273class TestLeasesParserFast(MAASTestCase):
274
275 def test_expired_lease_does_not_shadow_earlier_host_stanza(self):
276 params = {
277 'ip': factory.getRandomIPAddress(),
278 'mac1': factory.getRandomMACAddress(),
279 'mac2': factory.getRandomMACAddress(),
280 }
281 leases = leases_parser_fast.parse_leases(dedent("""\
282 host %(ip)s {
283 dynamic;
284 hardware ethernet %(mac1)s;
285 fixed-address %(ip)s;
286 }
287 lease %(ip)s {
288 starts 5 2010/01/01 00:00:01;
289 ends 1 2010/01/01 00:00:02;
290 hardware ethernet %(mac2)s;
291 }
292 """ % params))
293 # The lease has expired so it doesn't shadow the host stanza,
294 # and so the MAC returned is from the host stanza.
295 self.assertEqual({params["ip"]: params["mac1"]}, leases)
296
297 def test_active_lease_shadows_earlier_host_stanza(self):
298 params = {
299 'ip': factory.getRandomIPAddress(),
300 'mac1': factory.getRandomMACAddress(),
301 'mac2': factory.getRandomMACAddress(),
302 }
303 leases = leases_parser_fast.parse_leases(dedent("""\
304 host %(ip)s {
305 dynamic;
306 hardware ethernet %(mac1)s;
307 fixed-address %(ip)s;
308 }
309 lease %(ip)s {
310 starts 5 2010/01/01 00:00:01;
311 hardware ethernet %(mac2)s;
312 }
313 """ % params))
314 # The lease hasn't expired, so shadows the earlier host stanza.
315 self.assertEqual({params["ip"]: params["mac2"]}, leases)
316
317 def test_host_stanza_shadows_earlier_active_lease(self):
318 params = {
319 'ip': factory.getRandomIPAddress(),
320 'mac1': factory.getRandomMACAddress(),
321 'mac2': factory.getRandomMACAddress(),
322 }
323 leases = leases_parser_fast.parse_leases(dedent("""\
324 lease %(ip)s {
325 starts 5 2010/01/01 00:00:01;
326 hardware ethernet %(mac2)s;
327 }
328 host %(ip)s {
329 dynamic;
330 hardware ethernet %(mac1)s;
331 fixed-address %(ip)s;
332 }
333 """ % params))
334 # The lease hasn't expired, but the host entry is later, so it
335 # shadows the earlier lease stanza.
336 self.assertEqual({params["ip"]: params["mac1"]}, leases)
337
338
339class TestLeasesParserFunctions(MAASTestCase):
340
341 def test_get_expiry_date_parses_expiry_date(self):
342 lease = fake_parsed_lease(ends='0 2011/01/02 03:04:05')
343 self.assertEqual(
344 datetime(
345 year=2011, month=01, day=02,
346 hour=03, minute=04, second=05),
347 get_expiry_date(lease))
348
349 def test_get_expiry_date_returns_None_for_never(self):
350 self.assertIsNone(
351 get_expiry_date(fake_parsed_lease(ends='never')))
352
353 def test_get_expiry_date_returns_None_if_no_expiry_given(self):
354 self.assertIsNone(get_expiry_date(fake_parsed_lease(ends=None)))
355
356 def test_has_expired_returns_False_for_eternal_lease(self):
357 now = datetime.utcnow()
358 self.assertFalse(has_expired(fake_parsed_lease(ends=None), now))
359
360 def test_has_expired_returns_False_for_future_expiry_date(self):
361 now = datetime.utcnow()
362 later = '1 2035/12/31 23:59:59'
363 self.assertFalse(has_expired(fake_parsed_lease(ends=later), now))
364
365 def test_has_expired_returns_True_for_past_expiry_date(self):
366 now = datetime.utcnow()
367 earlier = '1 2001/01/01 00:00:00'
368 self.assertTrue(
369 has_expired(fake_parsed_lease(ends=earlier), now))
370
371 def test_gather_leases_finds_current_leases(self):
372 lease = fake_parsed_lease()
373 self.assertEqual(
374 {lease.ip: lease.hardware.mac},
375 gather_leases([lease]))
376
377 def test_gather_leases_ignores_expired_leases(self):
378 earlier = '1 2001/01/01 00:00:00'
379 lease = fake_parsed_lease(ends=earlier)
380 self.assertEqual({}, gather_leases([lease]))
381
382 def test_gather_leases_combines_expired_and_current_leases(self):
383 earlier = '1 2001/01/01 00:00:00'
384 ip = factory.getRandomIPAddress()
385 old_owner = factory.getRandomMACAddress()
386 new_owner = factory.getRandomMACAddress()
387 leases = [
388 fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
389 fake_parsed_lease(ip=ip, mac=new_owner),
390 ]
391 self.assertEqual({ip: new_owner}, gather_leases(leases))
392
393 def test_gather_leases_ignores_ordering(self):
394 earlier = '1 2001/01/01 00:00:00'
395 ip = factory.getRandomIPAddress()
396 old_owner = factory.getRandomMACAddress()
397 new_owner = factory.getRandomMACAddress()
398 leases = [
399 fake_parsed_lease(ip=ip, mac=new_owner),
400 fake_parsed_lease(ip=ip, mac=old_owner, ends=earlier),
401 ]
402 self.assertEqual({ip: new_owner}, gather_leases(leases))
403
404 def test_gather_leases_ignores_host_declarations(self):
405 self.assertEqual({}, gather_leases([fake_parsed_host()]))
406
407 def test_gather_hosts_finds_hosts(self):
408 host = fake_parsed_host()
409 self.assertEqual({host.ip: host.hardware.mac}, gather_hosts([host]))
410
411 def test_gather_hosts_ignores_unaccompanied_rubouts(self):
412 self.assertEqual({}, gather_hosts([fake_parsed_rubout()]))
413
414 def test_gather_hosts_ignores_rubbed_out_entries(self):
415 ip = factory.getRandomIPAddress()
416 hosts = [
417 fake_parsed_host(ip=ip),
418 fake_parsed_rubout(ip=ip),
419 ]
420 self.assertEqual({}, gather_hosts(hosts))
421
422 def test_gather_hosts_follows_reassigned_host(self):
423 ip = factory.getRandomIPAddress()
424 new_owner = factory.getRandomMACAddress()
425 hosts = [
426 fake_parsed_host(ip=ip),
427 fake_parsed_rubout(ip=ip),
428 fake_parsed_host(ip=ip, mac=new_owner),
429 ]
430 self.assertEqual({ip: new_owner}, gather_hosts(hosts))
431
432 def test_is_lease_and_is_host_recognize_lease(self):
433 params = {
434 'ip': factory.getRandomIPAddress(),
435 'mac': factory.getRandomMACAddress(),
436 }
437 [parsed_lease] = lease_parser.searchString(dedent("""\
438 lease %(ip)s {
439 hardware ethernet %(mac)s;
440 }
441 """ % params))
442 self.assertEqual(
443 (True, False),
444 (is_lease(parsed_lease), is_host(parsed_lease)))
445
446 def test_is_lease_and_is_host_recognize_host(self):
447 params = {
448 'ip': factory.getRandomIPAddress(),
449 'mac': factory.getRandomMACAddress(),
450 }
451 [parsed_host] = lease_parser.searchString(dedent("""\
452 host %(ip)s {
453 hardware ethernet %(mac)s;
454 }
455 """ % params))
456 self.assertEqual(
457 (False, True),
458 (is_lease(parsed_host), is_host(parsed_host)))
459
460 def test_get_host_mac_returns_None_for_host(self):
461 params = {
462 'ip': factory.getRandomIPAddress(),
463 'mac': factory.getRandomMACAddress(),
464 }
465 [parsed_host] = lease_parser.searchString(dedent("""\
466 host %(ip)s {
467 hardware ethernet %(mac)s;
468 }
469 """ % params))
470 self.assertEqual(params['mac'], get_host_mac(parsed_host))
471
472 def test_get_host_mac_returns_None_for_rubout(self):
473 ip = factory.getRandomIPAddress()
474 [parsed_host] = lease_parser.searchString(dedent("""\
475 host %s {
476 deleted;
477 }
478 """ % ip))
479 self.assertIsNone(get_host_mac(parsed_host))
480
481 def test_get_host_mac_returns_None_for_rubout_even_with_mac(self):
482 params = {
483 'ip': factory.getRandomIPAddress(),
484 'mac': factory.getRandomMACAddress(),
485 }
486 [parsed_host] = lease_parser.searchString(dedent("""\
487 host %(ip)s {
488 deleted;
489 hardware ethernet %(mac)s;
490 }
491 """ % params))
492 self.assertIsNone(get_host_mac(parsed_host))
493
386 def test_combine_entries_accepts_host_followed_by_expired_lease(self):494 def test_combine_entries_accepts_host_followed_by_expired_lease(self):
387 ip = factory.getRandomIPAddress()495 ip = factory.getRandomIPAddress()
388 mac = factory.getRandomMACAddress()496 mac = factory.getRandomMACAddress()
389 earlier = '1 2001/01/01 00:00:00'497 earlier = '1 2001/01/01 00:00:00'
390 entries = [498 entries = [
391 self.fake_parsed_host(ip=ip, mac=mac),499 fake_parsed_host(ip=ip, mac=mac),
392 self.fake_parsed_lease(ip=ip, ends=earlier),500 fake_parsed_lease(ip=ip, ends=earlier),
393 ]501 ]
394 self.assertEqual({ip: mac}, combine_entries(entries))502 self.assertEqual({ip: mac}, combine_entries(entries))
395503
@@ -398,8 +506,8 @@
398 mac = factory.getRandomMACAddress()506 mac = factory.getRandomMACAddress()
399 earlier = '1 2001/01/01 00:00:00'507 earlier = '1 2001/01/01 00:00:00'
400 entries = [508 entries = [
401 self.fake_parsed_lease(ip=ip, ends=earlier),509 fake_parsed_lease(ip=ip, ends=earlier),
402 self.fake_parsed_host(ip=ip, mac=mac),510 fake_parsed_host(ip=ip, mac=mac),
403 ]511 ]
404 self.assertEqual({ip: mac}, combine_entries(entries))512 self.assertEqual({ip: mac}, combine_entries(entries))
405513
@@ -407,9 +515,9 @@
407 ip = factory.getRandomIPAddress()515 ip = factory.getRandomIPAddress()
408 mac = factory.getRandomMACAddress()516 mac = factory.getRandomMACAddress()
409 entries = [517 entries = [
410 self.fake_parsed_host(ip=ip),518 fake_parsed_host(ip=ip),
411 self.fake_parsed_rubout(ip=ip),519 fake_parsed_rubout(ip=ip),
412 self.fake_parsed_lease(ip=ip, mac=mac),520 fake_parsed_lease(ip=ip, mac=mac),
413 ]521 ]
414 self.assertEqual({ip: mac}, combine_entries(entries))522 self.assertEqual({ip: mac}, combine_entries(entries))
415523
@@ -418,9 +526,9 @@
418 mac = factory.getRandomMACAddress()526 mac = factory.getRandomMACAddress()
419 earlier = '1 2001/01/01 00:00:00'527 earlier = '1 2001/01/01 00:00:00'
420 entries = [528 entries = [
421 self.fake_parsed_host(ip=ip),529 fake_parsed_host(ip=ip),
422 self.fake_parsed_rubout(ip=ip),530 fake_parsed_rubout(ip=ip),
423 self.fake_parsed_lease(ip=ip, mac=mac, ends=earlier),531 fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
424 ]532 ]
425 self.assertEqual({}, combine_entries(entries))533 self.assertEqual({}, combine_entries(entries))
426534
@@ -429,9 +537,9 @@
429 mac = factory.getRandomMACAddress()537 mac = factory.getRandomMACAddress()
430 earlier = '1 2001/01/01 00:00:00'538 earlier = '1 2001/01/01 00:00:00'
431 entries = [539 entries = [
432 self.fake_parsed_host(ip=ip),540 fake_parsed_host(ip=ip),
433 self.fake_parsed_lease(ip=ip, mac=mac, ends=earlier),541 fake_parsed_lease(ip=ip, mac=mac, ends=earlier),
434 self.fake_parsed_rubout(ip=ip),542 fake_parsed_rubout(ip=ip),
435 ]543 ]
436 self.assertEqual({}, combine_entries(entries))544 self.assertEqual({}, combine_entries(entries))
437545
@@ -439,9 +547,9 @@
439 ip = factory.getRandomIPAddress()547 ip = factory.getRandomIPAddress()
440 mac = factory.getRandomMACAddress()548 mac = factory.getRandomMACAddress()
441 entries = [549 entries = [
442 self.fake_parsed_host(ip=ip),550 fake_parsed_host(ip=ip),
443 self.fake_parsed_lease(ip=ip, mac=mac),551 fake_parsed_lease(ip=ip, mac=mac),
444 self.fake_parsed_rubout(ip=ip),552 fake_parsed_rubout(ip=ip),
445 ]553 ]
446 self.assertEqual({ip: mac}, combine_entries(entries))554 self.assertEqual({ip: mac}, combine_entries(entries))
447555
@@ -449,8 +557,8 @@
449 ip = factory.getRandomIPAddress()557 ip = factory.getRandomIPAddress()
450 mac = factory.getRandomMACAddress()558 mac = factory.getRandomMACAddress()
451 entries = [559 entries = [
452 self.fake_parsed_host(ip=ip),560 fake_parsed_host(ip=ip),
453 self.fake_parsed_rubout(ip=ip),561 fake_parsed_rubout(ip=ip),
454 self.fake_parsed_host(ip=ip, mac=mac),562 fake_parsed_host(ip=ip, mac=mac),
455 ]563 ]
456 self.assertEqual({ip: mac}, combine_entries(entries))564 self.assertEqual({ip: mac}, combine_entries(entries))

Subscribers

People subscribed via source and target branches

to all changes: