Jared Dunbar 2 years ago
parent
commit
603ad1beb8
Signed by: Jared Dunbar <jrddunbr@gmail.com> GPG Key ID: CF202CC859BAC692
2 changed files with 181 additions and 144 deletions
  1. 2
    0
      .gitignore
  2. 179
    144
      leases.py

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
1
+dhcpd.leases
2
+dhcpd6.leases

+ 179
- 144
leases.py View File

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

Loading…
Cancel
Save