Browse Source

initial commit, source from a stackoverflow post

Jared Dunbar 1 year ago
commit
f6ab9a6236
Signed by: Jared Dunbar <jrddunbr@gmail.com> GPG Key ID: CF202CC859BAC692
2 changed files with 204 additions and 0 deletions
  1. 203
    0
      leases.py
  2. 1
    0
      src.loc

+ 203
- 0
leases.py View File

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

+ 1
- 0
src.loc View File

@@ -0,0 +1 @@
1
+https://askubuntu.com/questions/219609/how-do-i-show-active-dhcp-leases

Loading…
Cancel
Save