]> git.uio.no Git - u/vegarko/korvald.git/blob - fetchReplays.py
Changed comment in config.ini
[u/vegarko/korvald.git] / fetchReplays.py
1 # -*- coding: utf-8 -*-
2 import base64
3 import hashlib
4 import json
5 import logging
6 import logging.handlers
7 import os
8 import shutil
9 import re
10 import sys
11 import urllib2
12 from ConfigParser import SafeConfigParser
13 from datetime import date
14
15 import fcntl
16 import zlib
17 from suds.client import Client
18 from suds.transport.http import HttpAuthenticated
19
20 LOCK = None
21
22
23 # Initializes the logger, log, and gives it a name, name
24 def init_logger(name):
25     form = "[%(asctime)s] - %(levelname)8s - %(funcName)15s()] %(message)s"
26     datefmt = '%Y-%m-%d %H:%M:%S'
27     l = logging.getLogger(name)
28     handler = logging.handlers.SysLogHandler(
29         facility=logging.handlers.SysLogHandler.LOG_LOCAL0, address='/dev/log')
30     l.addHandler(handler)
31     formatter = logging.Formatter(form, datefmt=datefmt)
32     handler.setFormatter(formatter)
33
34
35 # Authenticates and returns the connection
36 def connect(usern, passwd):
37     trans = HttpAuthenticated(username=usern, password=passwd)
38     url = "https://vidyo-replay1.uio.no/replay/services/VidyoReplayContentManagementService?wsdl"
39     loc = "https://vidyo-replay1.uio.no/replay/services/VidyoReplayContentManagementService"
40     return Client(url, location=loc, transport=trans, faults=False)
41
42
43 # Returns the records from VidyoReplay
44 # def get_records(client):
45 def get_records(client):
46     try:
47         records = client.service.RecordsSearch(sortby='date')
48     except Exception as detail:
49         s = "Exception: {}".format(detail)
50         log.error(s)
51         sys.exit(1)
52     return records
53
54
55 # Checks if the script has the right amount of arguments
56 def check_cmd_args(argv):
57     if len(argv) is not 2:
58         log.error("1 argument needed, found %d", len(argv)-1)
59         sys.exit(1)
60
61
62 # Check to see if the recording is owned by the "tsd-import" user.
63 def check_username(rec):
64     username = 'tsd-import'
65
66     if rec.userName == username:
67         return True
68     else:
69         return False
70
71
72 # Returns True if there is not enough space for the record.
73 def check_usage(path, filesize):
74     st = os.statvfs(path)
75     free = st.f_bavail * st.f_frsize
76
77     if filesize < free:
78         return True
79     else:
80         return False
81
82
83 # Checks if the script is already running
84 def single_instance(path):
85     global LOCK
86
87     try:
88         LOCK = open(path, 'w+')
89     except IOError as e:
90         s = "IOError: {}".format(e)
91         log.error(s)
92         sys.exit(1)
93
94     try:
95         fcntl.lockf(LOCK, fcntl.LOCK_EX | fcntl.LOCK_NB)
96     except IOError:
97         s = "the script is already running"
98         log.warning(s)
99         sys.exit(1)
100
101
102 # Returns true if the user har permissions to read directory d
103 def read_dir(d):
104     if not os.path.isdir(d):
105         s = "{} is not a directory".format(d)
106         log.error(s)
107         sys.exit(1)
108
109     if not os.access(d, os.R_OK):
110         s = "No permission to read in directory {}".format(d)
111         log.error(s)
112         sys.exit(1)
113
114     return True
115
116
117 # Returns true if the user har permissions to write to directory d
118 def write_dir(d):
119     read_dir(d)
120
121     if not os.access(d, os.W_OK):
122         s = "No permission to write in directory: {}".format(d)
123         log.error(s)
124         sys.exit(1)
125
126     return True
127
128
129 # Returns true if the user har permissions to write to file f
130 def write_file(f):
131     if not os.access(f, os.W_OK):
132         s = "No permission to write to file: {}".format(f)
133         log.error(s)
134         sys.exit(1)
135
136     return True
137
138
139 # Parses the config
140 def read_config(argv):
141     config_f = argv[0]
142
143     if not os.path.isfile(config_f):
144         s = "{} does not exist".format(config_f)
145         log.error(s)
146         sys.exit(1)
147
148     parser = SafeConfigParser()
149     parser.read(config_f)
150
151     try:
152         config = {
153             'usern': parser.get('auth', 'username'),
154             'passwd': parser.get('auth', 'password'),
155             'lock': parser.get('config', 'lock'),
156             'dest': parser.get('config', 'videoDest'),
157             'delete': parser.getboolean('config', 'delete'),
158             'dryrun': parser.getboolean('config', 'dryrun')
159             }
160     except:
161         s = "configuration error: {}".format(sys.exc_info()[0])
162         log.error(s)
163         sys.exit(1)
164
165     if config['dryrun']:
166         print "--- Config ---"
167         print config
168         print "\n"
169
170     try:
171         config['hash'] = parser.get('config', 'hash')   # not mandatory
172     except:
173         config['hash'] = 'sha256'
174         pass
175
176     if not os.path.isfile(config['lock']):
177         s = "{} does not exist".format(config['lock'])
178         log.error(s)
179         sys.exit(1)
180
181     write_file(config['lock'])
182     write_dir(config['dest'])
183
184     if not os.path.isdir(config['dest']+"completed"):
185         os.mkdir(config['dest']+"completed", 0770)
186
187     return config
188
189
190 # Makes a JSON-file from record metadata
191 def makeJSON(record, dest, display_name, checksum, hashfunc, dryrun):
192
193     d = {
194         'id': record.id,
195         'guid': record.guid,
196         'tenantName': record.tenantName,
197         'userName': record.userName,
198         'userFullName': record.userFullName,
199         'recordedBy': display_name,
200         'dateCreated': record.dateCreated.strftime("%Y-%m-%d %H:%M:%S"),
201         'dateCreatedString': record.dateCreatedString,
202         # 'endTime': record.endTime,
203         'duration': record.duration,
204         'resolution': record.resolution,
205         'framerate': record.framerate,
206         'pin': record.pin,
207         'recordScope': record.recordScope,
208         'title': record.title,
209         'roomName': record.roomName,
210         'fileLink': record.fileLink,
211         'recorderId': record.recorderId,
212         'webcast': record.webcast,
213         'tags': record.tags,
214         'comments': record.comments,
215         'locked': record.locked,
216         'externalPlaybackLink': record.externalPlaybackLink,
217         'fileSize': record.fileSize,
218         'checksum': checksum,
219         'hashfunc': hashfunc,
220         }
221
222     if dryrun:
223         print "--- JSON ---"
224         print d
225         print "Checksum is always -2 with dryrun"
226         print "------------"
227         return -1
228
229     if os.path.isfile(dest):
230         oldfp = open(dest, 'r')
231         oldjson = json.load(oldfp)
232         oldfp.close()
233     else:
234         oldjson = ''
235
236     if oldjson != d:
237         with open(dest, 'w') as f:
238             json.dump(d, f)
239
240
241 def checksum_and_download(path, hashfunc, f):
242     def crc32(path):
243         BLOCKSIZE = 65536
244         csum = 0
245         with open(path, 'ab') as afile:
246             buf = f.read(BLOCKSIZE)
247             while len(buf) > 0:
248                 afile.write(buf)
249                 csum = zlib.crc32(buf, csum)
250                 buf = f.read(BLOCKSIZE)
251         return csum & 0xffffffff
252
253     def sha256(path):
254         BLOCKSIZE = 65536
255         hasher = hashlib.sha256()
256         with open(path, 'ab') as afile:
257             buf = f.read(BLOCKSIZE)
258             while len(buf) > 0:
259                 afile.write(buf)
260                 hasher.update(buf)
261                 buf = f.read(BLOCKSIZE)
262         return hasher.hexdigest()
263
264     if hashfunc == 'crc32':
265         return crc32(path)
266     else:
267         return sha256(path)
268
269
270 def getVideo(config, url):
271     username = config['usern']
272     passwd = config['passwd']
273     dest = config['dest']
274     hashfunc = config['hash']
275     dryrun = config['dryrun']
276
277     req = urllib2.Request(url)
278     base64string = base64.encodestring('%s:%s' % (username, passwd))[:-1]
279     req.add_header("Authorization", "Basic %s" % base64string)
280
281     try:
282         f = urllib2.urlopen(req)
283         condis = f.info()['Content-Disposition']
284         result = re.search('\"(.*)\"', condis)
285         filename = result.group(1)
286     except IOError, e:
287         log.error(e)
288
289     if dryrun:
290         return filename, -2
291     elif os.path.isfile(dest+"completed/"+filename):
292         return filename, -1
293
294     checks = checksum_and_download(dest+filename, hashfunc, f)
295     return filename, checks
296
297
298 def run(config, client, records):
299     username = config['usern']
300     passwd = config['passwd']
301     dest = config['dest']
302     delete = config['delete']
303     hashfunc = config['hash']
304     dryrun = config['dryrun']
305
306     for record in records[1].records:
307         if check_username(record):
308             if not check_usage(config['dest'], int(record.fileSize)):
309                 s = "Disk space full. Record.id: {} Filesize: {}".format(record.id, record.fileSize)
310                 log.error(s)
311                 sys.exit(1)
312
313             if dryrun:
314                 print "--- ID: {} ---".format(record.id)
315
316             filename, checksum = getVideo(config, record.fileLink)
317             result = re.search('(.*)_\d\d\d\d', filename)
318
319             if checksum != -1:
320                 if checksum != -2:
321                     shutil.move(dest+filename, dest+"completed/"+filename)
322                 makeJSON(record, dest +"completed/"+filename[:-4] + '.json',
323                     result.group(1), checksum, hashfunc, dryrun)
324
325
326             # elif checksum == -1 and delete == True:
327             #     s = "The file {} already exist. delete = {}".format(dest+filename, delete)
328             #     log.warning(s)
329
330             if dryrun and delete:
331                 print "Record would be deleted"
332             elif delete:
333                 retval = client.service.DeleteRecord(record.id)
334                 if retval[1] != "OK":
335                     s = "DeleteRecord({}) should return (200, OK). Returned {}.".format(
336                         record.id, retval)
337                     log.error(s)
338             if dryrun:
339                 print "\n"
340
341
342 if __name__ == "__main__":
343     init_logger('log')
344     log = logging.getLogger('log')
345     check_cmd_args(sys.argv)
346     config = read_config(sys.argv[1:])
347     single_instance(config['lock'])
348
349     client = connect(config['usern'], config['passwd'])
350     records = get_records(client)
351     run(config, client, records)