Skip to content

Commit 15f5348

Browse files
author
piky
committed
Added dhcpd-lease.py script
1 parent baa8f3e commit 15f5348

File tree

1 file changed

+258
-0
lines changed

1 file changed

+258
-0
lines changed

dhcpd-lease.py

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#!/usr/bin/python
2+
import datetime, bisect
3+
4+
def parse_timestamp(raw_str):
5+
tokens = raw_str.split()
6+
7+
if len(tokens) == 1:
8+
if tokens[0].lower() == 'never':
9+
return 'never';
10+
11+
else:
12+
raise Exception('Parse error in timestamp')
13+
14+
elif len(tokens) == 3:
15+
return datetime.datetime.strptime(' '.join(tokens[1:]),
16+
'%Y/%m/%d %H:%M:%S')
17+
18+
else:
19+
raise Exception('Parse error in timestamp')
20+
21+
22+
def timestamp_is_ge(t1, t2):
23+
if t1 == 'never':
24+
return True
25+
26+
elif t2 == 'never':
27+
return False
28+
29+
else:
30+
return t1 >= t2
31+
32+
33+
def timestamp_is_lt(t1, t2):
34+
if t1 == 'never':
35+
return False
36+
37+
elif t2 == 'never':
38+
return t1 != 'never'
39+
40+
else:
41+
return t1 < t2
42+
43+
44+
def timestamp_is_between(t, tstart, tend):
45+
return timestamp_is_ge(t, tstart) and timestamp_is_lt(t, tend)
46+
47+
48+
def parse_hardware(raw_str):
49+
tokens = raw_str.split()
50+
51+
if len(tokens) == 2:
52+
return tokens[1]
53+
54+
else:
55+
raise Exception('Parse error in hardware')
56+
57+
58+
def strip_endquotes(raw_str):
59+
return raw_str.strip('"')
60+
61+
62+
def identity(raw_str):
63+
return raw_str
64+
65+
66+
def parse_binding_state(raw_str):
67+
tokens = raw_str.split()
68+
69+
if len(tokens) == 2:
70+
return tokens[1]
71+
72+
else:
73+
raise Exception('Parse error in binding state')
74+
75+
76+
def parse_next_binding_state(raw_str):
77+
tokens = raw_str.split()
78+
79+
if len(tokens) == 3:
80+
return tokens[2]
81+
82+
else:
83+
raise Exception('Parse error in next binding state')
84+
85+
86+
def parse_rewind_binding_state(raw_str):
87+
tokens = raw_str.split()
88+
89+
if len(tokens) == 3:
90+
return tokens[2]
91+
92+
else:
93+
raise Exception('Parse error in next binding state')
94+
95+
96+
def parse_leases_file(leases_file):
97+
valid_keys = {
98+
'starts': parse_timestamp,
99+
'ends': parse_timestamp,
100+
'tstp': parse_timestamp,
101+
'tsfp': parse_timestamp,
102+
'atsfp': parse_timestamp,
103+
'cltt': parse_timestamp,
104+
'hardware': parse_hardware,
105+
'binding': parse_binding_state,
106+
'next': parse_next_binding_state,
107+
'rewind': parse_rewind_binding_state,
108+
'uid': strip_endquotes,
109+
'client-hostname': strip_endquotes,
110+
'option': identity,
111+
'set': identity,
112+
'on': identity,
113+
'abandoned': None,
114+
'bootp': None,
115+
'reserved': None,
116+
}
117+
118+
leases_db = {}
119+
120+
lease_rec = {}
121+
in_lease = False
122+
in_failover = False
123+
124+
for line in leases_file:
125+
if line.lstrip().startswith('#'):
126+
continue
127+
128+
tokens = line.split()
129+
130+
if len(tokens) == 0:
131+
continue
132+
133+
key = tokens[0].lower()
134+
135+
if key == 'lease':
136+
if not in_lease:
137+
ip_address = tokens[1]
138+
139+
lease_rec = {'ip_address' : ip_address}
140+
in_lease = True
141+
142+
else:
143+
raise Exception('Parse error in leases file')
144+
145+
elif key == 'failover':
146+
in_failover = True
147+
elif key == '}':
148+
if in_lease:
149+
for k in valid_keys:
150+
if callable(valid_keys[k]):
151+
lease_rec[k] = lease_rec.get(k, '')
152+
else:
153+
lease_rec[k] = False
154+
155+
ip_address = lease_rec['ip_address']
156+
157+
if ip_address in leases_db:
158+
leases_db[ip_address].insert(0, lease_rec)
159+
160+
else:
161+
leases_db[ip_address] = [lease_rec]
162+
163+
lease_rec = {}
164+
in_lease = False
165+
166+
elif in_failover:
167+
in_failover = False
168+
continue
169+
else:
170+
raise Exception('Parse error in leases file')
171+
172+
elif key in valid_keys:
173+
if in_lease:
174+
value = line[(line.index(key) + len(key)):]
175+
value = value.strip().rstrip(';').rstrip()
176+
177+
if callable(valid_keys[key]):
178+
lease_rec[key] = valid_keys[key](value)
179+
else:
180+
lease_rec[key] = True
181+
182+
else:
183+
raise Exception('Parse error in leases file')
184+
185+
else:
186+
if in_lease:
187+
raise Exception('Parse error in leases file')
188+
189+
if in_lease:
190+
raise Exception('Parse error in leases file')
191+
192+
return leases_db
193+
194+
195+
def round_timedelta(tdelta):
196+
return datetime.timedelta(tdelta.days,
197+
tdelta.seconds + (0 if tdelta.microseconds < 500000 else 1))
198+
199+
200+
def timestamp_now():
201+
n = datetime.datetime.utcnow()
202+
return datetime.datetime(n.year, n.month, n.day, n.hour, n.minute,
203+
n.second + (0 if n.microsecond < 500000 else 1))
204+
205+
206+
def lease_is_active(lease_rec, as_of_ts):
207+
return timestamp_is_between(as_of_ts, lease_rec['starts'],
208+
lease_rec['ends'])
209+
210+
211+
def ipv4_to_int(ipv4_addr):
212+
parts = ipv4_addr.split('.')
213+
return (int(parts[0]) << 24) + (int(parts[1]) << 16) + \
214+
(int(parts[2]) << 8) + int(parts[3])
215+
216+
217+
def select_active_leases(leases_db, as_of_ts):
218+
retarray = []
219+
sortedarray = []
220+
221+
for ip_address in leases_db:
222+
lease_rec = leases_db[ip_address][0]
223+
224+
if lease_is_active(lease_rec, as_of_ts):
225+
ip_as_int = ipv4_to_int(ip_address)
226+
insertpos = bisect.bisect(sortedarray, ip_as_int)
227+
sortedarray.insert(insertpos, ip_as_int)
228+
retarray.insert(insertpos, lease_rec)
229+
230+
return retarray
231+
232+
233+
##############################################################################
234+
235+
236+
myfile = open('/var/lib/dhcp/dhcpd.leases', 'r')
237+
leases = parse_leases_file(myfile)
238+
myfile.close()
239+
240+
now = timestamp_now()
241+
report_dataset = select_active_leases(leases, now)
242+
243+
print('+-----------------------------------------------------------------------------------------------')
244+
print('| DHCPD ACTIVE LEASES REPORT')
245+
print('+-----------------+-------------------+----------------------+----------------------------------')
246+
print('| IP Address | MAC Address | Expires (days,H:M:S) | Client Hostname ')
247+
print('+-----------------+-------------------+----------------------+----------------------------------')
248+
249+
for lease in report_dataset:
250+
print('| ' + format(lease['ip_address'], '<15') + ' | ' + \
251+
format(lease['hardware'], '<17') + ' | ' + \
252+
format(str((lease['ends'] - now) if lease['ends'] != 'never' else 'never'), '>20') + ' | ' + \
253+
lease['client-hostname'])
254+
255+
print('+-----------------+-------------------+----------------------+----------------------------------')
256+
print('| Total Active Leases: ' + str(len(report_dataset)))
257+
print('| Report generated (UTC): ' + str(now))
258+
print('+-----------------------------------------------------------------------------------------------')

0 commit comments

Comments
 (0)