9c56aaad9145d8f9f41a35b618bcfb868139808b
[myslice.git] / activity / __init__.py
1 #
2 # Activity monitor
3 #
4 # Client is authenticated with an API key and a secret
5 # The API key is a 64 chars string (digits and letters) that is passed to the request
6 # The secret is a 64 chars string that is used to sign the request
7 # The generated signature is a SHA256 hes digest
8
9 import urllib, urllib2
10 import threading
11 import hmac
12 import hashlib
13 import base64
14 import time
15 import datetime
16 from myslice.configengine import ConfigEngine
17 from myslice.settings import logger
18 import random
19 import os
20 import sqlite3 as lite
21 import json
22 import syslog
23
24 config = ConfigEngine()
25 if config.activity and config.activity.apikey :
26     apikey = config.activity.apikey
27 else :
28     # apikey will be necessary
29     apikey = None
30
31 if config.activity and config.activity.secret :
32     secret = config.activity.secret
33 else :
34     # secret will be necessary
35     secret = None
36
37 if config.activity and config.activity.server :
38     server = config.activity.server
39 else :
40     # default log server
41     server = "http://athos.ipv6.lip6.fr/activity/push/log"
42
43 def logWrite(request, action, message, objects = None):
44     
45     if not apikey :
46         logger.info("===============>> activity: no apikey")
47         return
48     if not secret :
49         logger.info("===============>> activity: no secret")
50         return
51     
52     timestamp = time.mktime(datetime.datetime.today().timetuple())
53     ip = getClientIp(request)
54     log = {
55         "timestamp" : timestamp,
56         "client_ip" : ip,
57         "host"      : request.get_host(),
58         "referrer"  : request.META.get('HTTP_REFERER'),
59         "user"      : request.user,
60         "action"    : action,
61         "message"   : message,
62         "apikey"    : apikey,
63         "signature" : sign(secret, "%s%s%s%s" % (timestamp, ip, request.user, action)),
64         "slice"     : None,
65         "resource"  : None,
66         "resource_type"     : None,
67         "facility"      : None,
68         "testbed"       : None,
69     }
70     
71     if objects is not None:
72         for o in objects :
73             if (o in log) :
74                 log[o] = objects[o]
75     
76     try :
77         result = urllib2.urlopen(server, urllib.urlencode(log))
78         logger.info("===============>> activity: {} <{}> {}".format(action, request.user,message))
79         content = result.read()
80
81         #checking for not sent data and sending it (50% probability)
82         if random.randint(0,100) < 50:
83             logCheck()
84
85     except urllib2.URLError as e:
86         logger.error("===============>> activity: connection to {} impossible, could not log action".format(server))
87         logger.error(e.strerror)
88
89         dbfile = ''.join([os.path.dirname(os.path.abspath(__file__)), "/errors.db"])
90         conn = None
91         try:
92             conn = lite.connect(dbfile)
93             cur = conn.cursor()
94             cur.execute("""INSERT INTO logs(log) values('%s')""" % json.dumps(log))
95             conn.commit()
96         except lite.Error, e:
97             # this means that writing log into db also failed :(
98             # Last chance to preserve log is to send it to system syslog
99             # however there is no mechanism to pull it from this log - just manually.
100             logger.error('[activity] Error while inserting into sql db: %s' % str(e.args))
101             logger.error("[activity] data to send: '%s'" % json.dumps(log))
102         if conn:
103             conn.close()
104
105 def log(request, action, message, objects = None):
106     # Create a new thread in Daemon mode to send the log entry
107     t = threading.Thread(target=logWrite, args=(request, action, message, objects))
108     t.setDaemon(True)
109     t.start()
110
111 def getClientIp(request):
112     try :
113         x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
114         ip = x_forwarded_for.split(',')[0]
115     except:
116         ip = request.META.get('REMOTE_ADDR')
117     return ip
118
119 #
120 # sign the request with the secret key
121 def sign(secret, message):
122     return hmac.new(secret, msg=message, digestmod=hashlib.sha256).hexdigest()
123
124 #
125 # sending the logs cached in sqlite database
126
127 def logCheck():
128     """Checking local database for logs adn sending it to monitoring server"""
129     dbfile = ''.join([os.path.dirname(os.path.abspath(__file__)), "/errors.db"])
130     conn = None
131
132     #trying to connect local db adn pull unsent logs
133     try:
134         conn = lite.connect(dbfile)
135         cur = conn.cursor()
136         cur.execute("SELECT rowid, log from logs")
137         notsent = cur.fetchall()
138         for row in notsent:
139             #trying to send unsent data from sqlite db
140             try :
141                 urllib2.urlopen(config.activity.server, urllib.urlencode(json.loads(row[1])))
142                 #delete those who were sent
143                 cur.execute("""DELETE FROM logs where rowid = %s""" % row[0] )
144                 conn.commit()
145             except urllib2.URLError as e:
146                 # this is just to inform that DB is not working properly
147                 logger.error('[activity] Error while sending stats')
148                 logger.error(e.strerror)
149
150     except lite.Error, e:
151         #this need to be updated to store information via syslog
152         logger.error('[activity] Error while pulling from local sqlite3 db: %s' % str(e.args))
153         logger.error(e.strerror)
154     if conn:
155         conn.close()
156
157     return