start working on xoslib objects
[plstackapi.git] / planetstack / observer / event_manager.py
1 import threading
2 import requests, json
3
4 from planetstack.config import Config
5 from observer.deleter import Deleter
6
7 import uuid
8 import os
9 import imp
10 import inspect
11 import base64
12 from fofum import Fofum
13 import json
14 import traceback
15
16 random_client_id=None
17 def get_random_client_id():
18     global random_client_id
19
20     if (random_client_id is None) and os.path.exists("/opt/planetstack/random_client_id"):
21         # try to use the last one we used, if we saved it
22         try:
23             random_client_id = open("/opt/planetstack/random_client_id","r").readline().strip()
24             print "get_random_client_id: loaded %s" % random_client_id
25         except:
26             print "get_random_client_id: failed to read /opt/planetstack/random_client_id"
27
28     if random_client_id is None:
29         random_client_id = base64.urlsafe_b64encode(os.urandom(12))
30         print "get_random_client_id: generated new id %s" % random_client_id
31
32         # try to save it for later (XXX: could race with another client here)
33         try:
34             open("/opt/planetstack/random_client_id","w").write("%s\n" % random_client_id)
35         except:
36             print "get_random_client_id: failed to write /opt/planetstack/random_client_id"
37
38     return random_client_id
39
40 # decorator that marks dispatachable event methods
41 def event(func):
42         setattr(func, 'event', func.__name__)
43         return func
44
45 class EventHandler:
46         # This code is currently not in use.
47         def __init__(self):
48                 pass
49
50         @staticmethod
51         def get_events():
52                 events = []
53                 for name in dir(EventHandler):
54                         attribute = getattr(EventHandler, name)
55                         if hasattr(attribute, 'event'):
56                                 events.append(getattr(attribute, 'event'))
57                 return events
58
59         def dispatch(self, event, *args, **kwds):
60                 if hasattr(self, event):
61                         return getattr(self, event)(*args, **kwds)
62                         
63
64 class EventSender:
65         def __init__(self,user=None,clientid=None):
66                 try:
67                         user = Config().feefie_client_user
68                 except:
69                         user = 'pl'
70
71                 try:
72                         clid = Config().feefie_client_id
73                 except:
74                         clid = get_random_client_id()
75                         print "EventSender: no feefie_client_id configured. Using random id %s" % clid
76
77                 self.fofum = Fofum(user=user)
78                 self.fofum.make(clid)
79
80         def fire(self,**kwargs):
81                 kwargs["uuid"] = str(uuid.uuid1())
82                 self.fofum.fire(json.dumps(kwargs))
83
84 class EventListener:
85         def __init__(self,wake_up=None):
86                 self.handler = EventHandler()
87                 self.wake_up = wake_up
88                 self.deleters = {}
89                 self.load_deleter_modules()
90
91         def load_deleter_modules(self, deleter_dir=None):
92             if deleter_dir is None:
93                 if hasattr(Config(), "observer_deleters_dir"):
94                     deleter_dir = Config().observer_deleters_dir
95                 else:
96                     deleter_dir = "/opt/planetstack/observer/deleters"
97
98             for fn in os.listdir(deleter_dir):
99                 pathname = os.path.join(deleter_dir,fn)
100                 if os.path.isfile(pathname) and fn.endswith(".py") and (fn!="__init__.py"):
101                     module = imp.load_source(fn[:-3],pathname)
102                     for classname in dir(module):
103                         c = getattr(module, classname, None)
104
105                         # make sure 'c' is a descendent of Deleter and has a
106                         # provides field (this eliminates the abstract base classes
107                         # since they don't have a provides)
108
109                         if inspect.isclass(c) and issubclass(c, Deleter) and hasattr(c,"model") and c.model!=None:
110                             modelName = c.model
111                             if not modelName in self.deleters:
112                                 self.deleters[modelName] = []
113                             if not (c in self.deleters[modelName]):
114                                 self.deleters[modelName].append(c)
115             print 'loaded deleters: %s' % ",".join(self.deleters.keys())
116
117
118         def handle_event(self, payload):
119                 payload_dict = json.loads(payload)
120
121                 try:
122                         deletion = payload_dict.get('delete_flag', False)
123                         if (deletion):
124                                 model = payload_dict['model']
125                                 pk = payload_dict['pk']
126                                 model_dict = payload_dict['model_dict']
127
128                                 for deleter in self.deleters[model]:
129                                         try:
130                                             deleter()(pk, model_dict)
131                                         except:
132                                             # something is silently eating these
133                                             # exceptions...
134                                             traceback.print_exc()
135                                             raise
136
137                 except:
138                         deletion = False
139
140                 if (not deletion and self.wake_up):
141                         self.wake_up()
142
143         def run(self):
144                 # This is our unique client id, to be used when firing and receiving events
145                 # It needs to be generated once and placed in the config file
146
147                 try:
148                         user = Config().feefie_client_user
149                 except:
150                         user = 'pl'
151
152                 try:
153                         clid = Config().feefie_client_id
154                 except:
155                         clid = get_random_client_id()
156                         print "EventListener: no feefie_client_id configured. Using random id %s" % clid
157
158                 f = Fofum(user=user)
159                 
160                 listener_thread = threading.Thread(target=f.listen_for_event,args=(clid,self.handle_event))
161                 listener_thread.start()