-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathduo_watcher.py
executable file
·110 lines (101 loc) · 3.63 KB
/
duo_watcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""
Duo_watcher: Access Duo logs
"""
import duo_client
import json
import errno
import time
import sys
import os
with open('credentials/duo.json') as fp:
keys = json.load(fp)
# Configuration and information about objects to create.
admin_api = duo_client.Admin(
ikey = keys['ikey'],
skey = keys['skey'],
host = keys['apihost']
)
class LogWatcher:
def __init__(self, name, resource):
self.name = name
self.resource = resource
self.logname = None
self.backoff = 0
try:
with open(name + '/state') as fp:
self.state = json.load(fp)
except IOError as e:
if e.errno == errno.ENOENT:
self.state = {'timestamp': 0, 'count': 0}
else:
print('Unable to load {file}: {msg}'.format(
file = os.path.realpath(name + '/state'), msg = msg))
sys.stdout.flush()
raise
except Exception as msg:
print('Unable to load {file}: {msg}'.format(
file = os.path.realpath(name + '/state'), msg = msg))
sys.stdout.flush()
raise
def fetch(self):
try:
params = {
'mintime': str(self.state.get('timestamp', 0))
}
response = admin_api.json_api_call(
'GET',
'/admin/v1/logs/' + self.resource,
params,
)
except RuntimeError as e:
if e.args == ('Received 429 Too Many Requests',):
self.backoff = 1 + 2 * self.backoff
if self.backoff > 1800:
self.backoff = 1800
if self.backoff > 10:
print('{ts} {pid}: Backing off to {bo} on {name}'.format(
ts = time.strftime('%y-%m-%d %H:%M:%S'),
pid = os.getpid(),
bo = self.backoff,
name = self.name))
sys.stdout.flush()
return False
raise
else:
self.backoff = self.backoff / 2
prev_ts = -1
newRow = False
for row in response:
timestamp = row.get('timestamp', 0)
if timestamp == prev_ts:
count = count + 1
else:
prev_ts = timestamp
count = 1
if (timestamp > self.state.get('timestamp', 0) or
(timestamp == self.state.get('timestamp', 0) and count > self.state.get('count', 0))):
tm = time.localtime(timestamp)
fname = time.strftime(self.name + '/%y%m%d', tm)
if self.logname and self.logname != fname:
self.logfp.close()
self.logname = None
if not self.logname:
self.logname = fname
self.logfp = open(fname, 'a')
print('{ts} {pid}: Advancing to {file}'.format(
ts = time.strftime('%y-%m-%d %H:%M:%S'),
pid = os.getpid(),
file = os.path.realpath(fname)))
sys.stdout.flush()
json.dump(row, self.logfp)
self.logfp.write('\n')
self.state['timestamp'] = timestamp
self.state['count'] = count
newRow = True
if newRow:
self.logfp.flush()
with open(self.name + '/state.new', 'w') as fp:
json.dump(self.state, fp)
fp.write('\n')
os.rename(self.name + '/state.new', self.name + '/state')
return newRow