026a9aec5f541f39122d29aac447def46881b7d7
[monitor.git] / web / MonitorWeb / monitorweb / monitor_xmlrpc.py
1 import sys
2 import xmlrpclib
3 import cherrypy
4 import turbogears
5 from datetime import datetime, timedelta
6 import time
7 from monitor.wrapper import plc
8
9 try:
10         from monitor.database.info.model import *
11         from monitor.database.info.interface import *
12 except:
13         pass
14
15 try:
16     from PLC.Parameter import Parameter, Mixed
17 except:
18     def Parameter(a = None, b = None): pass
19     def Mixed(a = None, b = None, c = None): pass
20
21 def export_to_docbook(**kwargs):
22
23     keywords = {
24         "group" : "Monitor",
25         "status" : "current",
26         "name": None,
27         "args": None,
28         "roles": [],
29         "accepts": [],
30         "returns": [],
31     }
32     def export(method):
33         def args():
34             # Inspect method. Remove self from the argument list.
35             max_args = method.func_code.co_varnames[0:method.func_code.co_argcount]
36             defaults = method.func_defaults
37             if defaults is None:
38                 defaults = ()
39             min_args = max_args[0:len(max_args) - len(defaults)]
40
41             defaults = tuple([None for arg in min_args]) + defaults
42             return (min_args, max_args, defaults)
43
44         keywords['name'] = method.__name__
45         keywords['args'] = args
46         for arg in keywords:
47             method.__setattr__(arg, keywords[arg])
48
49         for arg in kwargs:
50             method.__setattr__(arg, kwargs[arg])
51         return method
52
53     return export
54
55
56 class MonitorXmlrpcServerMethods:
57         @cherrypy.expose
58         def listMethods(self):
59                 mod = MonitorXmlrpcServer()
60                 ret_list = []
61                 for f in dir(mod):
62                         if isinstance(mod.__getattribute__(f),type(mod.__getattribute__('addDowntime'))):
63                                 ret_list += [f]
64                 return ret_list
65
66 def convert_datetime(d, keys=None):
67         ret = d.copy()
68         n = datetime.now()
69         if keys == None:
70                 keys = d.keys()
71         for k in keys:
72                 if type(d[k]) == type(n):
73                         ret[k] = time.mktime(d[k].utctimetuple())
74         
75         return ret
76
77 class MonitorXmlrpcServer(object):
78
79         @cherrypy.expose
80         def listMethods(self):
81                 mod = MonitorXmlrpcServer()
82                 ret_list = []
83                 for f in dir(mod):
84                         if isinstance(mod.__getattribute__(f),type(mod.__getattribute__('addDowntime'))):
85                                 ret_list += [f]
86                 return ret_list
87
88         @turbogears.expose()
89         def XMLRPC(self):
90                 params, method = xmlrpclib.loads(cherrypy.request.body.read())
91                 try:
92                         if method == "xmlrpc":
93                                 # prevent recursion
94                                 raise AssertionError("method cannot be 'xmlrpc'")
95                         # Get the function and make sure it's exposed.
96                         method = getattr(self, method, None)
97                         # Use the same error message to hide private method names
98                         if method is None or not getattr(method, "exposed", False):
99                                 raise AssertionError("method does not exist")
100
101                         session.expunge_all()
102                         # Call the method, convert it into a 1-element tuple
103                         # as expected by dumps                                     
104                         response = method(*params)
105
106                         session.flush()
107                         response = xmlrpclib.dumps((response,), methodresponse=1, allow_none=1)
108                 except xmlrpclib.Fault, fault:
109                         # Can't marshal the result
110                         response = xmlrpclib.dumps(fault, allow_none=1)
111                 except:
112                         # Some other error; send back some error info
113                         response = xmlrpclib.dumps(
114                                 xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value))
115                                 )
116
117                 cherrypy.response.headers["Content-Type"] = "text/xml"
118                 return response
119
120         # User-defined functions must use cherrypy.expose; turbogears.expose
121         #       does additional checking of the response type that we don't want.
122         @cherrypy.expose
123         @export_to_docbook(roles=['tech', 'user', 'pi', 'admin'],
124                            accepts=[],
125                                            returns=Parameter(bool, 'True if successful'))
126         def upAndRunning(self,noop=None):
127                 """ This call can indicate to a script whether the server is up
128                 and running before trying any more sophisticated operations. """
129                 return True
130
131         # BOOTMAN SEQUENCE ------------------------------------------------------------
132
133         @cherrypy.expose
134         @export_to_docbook(roles=['admin'],
135                            accepts=[Parameter(dict, "Auth struct"), 
136                                                         Parameter(str, "The bootman sequence returned by MyOps"), 
137                                                         Parameter(str, "The action string that identifies what to do when this sequence occurs")],
138                                            returns=Parameter(bool, 'True on success.'))
139         def setBootmanSequence(self, auth, sequence, action):
140                 """ Using this call, you can set a new sequence to identify an Unknown
141                 Error sqeuence returned by MyOps and associate it with a pre-defined
142                 action, (i.e. reboot, reinstall, or others).  Please see the
143                 documentation for automated actions to see a list of supported
144                 actions. """
145                 api = plc.getAuthAPI()
146                 api.auth = auth
147                 if api.AuthCheck():
148                         bms = BootmanSequenceRecord.get_by(sequence=sequence)
149                         if not bms:
150                                 bms = BootmanSequenceRecord(sequence=sequence, action=action)
151                         else:
152                                 bms.action = action 
153
154                         bms.flush()
155                         return True
156                 else:
157                         return False
158
159         @cherrypy.expose
160         @export_to_docbook(roles=['admin'],
161                            accepts=[Parameter(dict, "Auth struct")], 
162                                            returns=Parameter(list, 'Array of bootman sequences'))
163         def getBootmanSequences(self, auth):
164                 """ Using this call, you can learn all currently defined bootman
165                 sequences and their associated actions. """
166                 api = plc.getAuthAPI()
167                 api.auth = auth
168                 if api.AuthCheck():
169                         ret_list = []
170                         bms = BootmanSequenceRecord.query.all()
171                         for q in bms:
172                                 d = q.to_dict()
173                                 d = convert_datetime(d, ['date_created'])
174                                 ret_list.append(d)
175                         return ret_list
176                 else:
177                         return []
178
179         # SITES ------------------------------------------------------------
180
181         @cherrypy.expose
182         @export_to_docbook(roles=['tech', 'user', 'pi', 'admin'],
183                            accepts=[Parameter(dict, "Auth struct")],
184                                            returns=Parameter(list, 'array of SiteStatus records'))
185         def getSiteStatus(self, auth):
186                 """ This call returns a list that includes the status and observations 
187                 of all sites, including those blacklisted.  At this time, there is no
188                 indication which sites are blacklisted from this list. """
189                 ret_list = []
190                 sites = HistorySiteRecord.query.all()
191                 for q in sites:
192                         d = q.to_dict(exclude=['timestamp', 'version', ])
193                         d = convert_datetime(d, ['last_checked', 'last_changed', 'message_created'])
194                         ret_list.append(d)
195                 return ret_list
196
197         @cherrypy.expose
198         @export_to_docbook(roles=['admin'],
199                            accepts=[Parameter(dict, "Auth struct")],
200                                            returns=Parameter(bool, 'True on success.'))
201         def clearSitePenalty(self, auth, loginbase):
202                 """ Rather than waiting for monitor to run automatically, this call
203                         will manually clear a site's penalties. """
204                 sitehist = SiteInterface.get_or_make(loginbase=loginbase)
205                 sitehist.clearPenalty()
206                 #sitehist.applyPenalty()
207                 #sitehist.sendMessage('clear_penalty')
208                 sitehist.closeTicket()
209                 return True
210
211         @cherrypy.expose
212         @export_to_docbook(roles=['admin'],
213                            accepts=[Parameter(dict, "Auth struct")],
214                                            returns=Parameter(bool, 'True on success.'))
215         def increaseSitePenalty(self, auth, loginbase):
216                 """ Rather than waiting for monitor to run automatically, this call
217                         will manually increase a site's penalties."""
218                 sitehist = SiteInterface.get_or_make(loginbase=loginbase)
219                 sitehist.increasePenalty()
220                 #sitehist.applyPenalty()
221                 #sitehist.sendMessage('increase_penalty')
222                 return True
223
224         # NODES ------------------------------------------------------------
225
226         @cherrypy.expose
227         @export_to_docbook(roles=['tech', 'user', 'pi', 'admin'],
228                            accepts=[Parameter(dict, "Auth struct")],
229                                            returns=Parameter(list, 'array of NodeStatus records.'))
230         def getNodeStatus(self, auth):
231                 """ This call returns a list of all nodes, including those
232                         blacklisted.  The current observation and recorded status of each node
233                         is returned."""
234                 ret_list = []
235                 sites = HistoryNodeRecord.query.all()
236                 for q in sites:
237                         d = q.to_dict(exclude=['timestamp', 'version', ])
238                         d = convert_datetime(d, ['last_checked', 'last_changed',])
239                         ret_list.append(d)
240                 return ret_list
241
242         @cherrypy.expose
243         @export_to_docbook(roles=['tech', 'user', 'pi', 'admin'],
244                            accepts=[Parameter(dict, "Auth struct")],
245                                            returns=Parameter(bool, 'True on success.'))
246         def getRecentActions(self, auth, loginbase=None, hostname=None):
247                 """ Monitor takes various actions on sites (such as applying
248                         penalties) and nodes (such as repairing a node installation via
249                         BootManager).  As well, it makes a log of every email message sent
250                         out, or believed to be sent.  This call returns a list of all actions,
251                         filtered on site or for a specific node. """
252                 ret_list = []
253                 return ret_list
254
255         # BLACKLIST ------------------------------------------------------------
256
257         @cherrypy.expose
258         @export_to_docbook(roles=['tech', 'user', 'pi', 'admin'],
259                            accepts=[Parameter(dict, "Auth struct")],
260                                            returns=Parameter(bool, 'True on success.'))
261         def getBlacklist(self, auth):
262                 """ Return a list of all nodes and sites that are excluded from
263                 penalties.  Currently there is no way to exclude a node or site 
264                 from being monitored. """
265                 bl = BlacklistRecord.query.all()
266                 ret_list = []
267                 for q in bl:
268                         d = q.to_dict(exclude=['timestamp', 'version', 'id', ])
269                         d = convert_datetime(d, ['date_created'])
270                         ret_list.append(d)
271
272                 return ret_list
273         
274         @cherrypy.expose
275         @export_to_docbook(roles=['admin'],
276                            accepts=[Parameter(dict, "Auth struct"), 
277                                                                 Parameter(str, "hostname"), 
278                                                                 Parameter(int, "expires number of seconds from time.now()")],
279                                            returns=Parameter(bool, 'True on success.'))
280         def addHostToBlacklist(self, auth, hostname, expires=0):
281                 """ Add a host to the blacklist, with an optional expiration time"""
282                 bl = BlacklistRecord.findby_or_create(hostname=hostname, expires=expires)
283                 return True
284
285         @cherrypy.expose
286         @export_to_docbook(roles=['admin'],
287                            accepts=[Parameter(dict, "Auth struct"),
288                                                                 Parameter(str, "loginbase"), 
289                                                                 Parameter(int, "expires number of seconds from time.now()")],
290                                            returns=Parameter(bool, 'True on success.'))
291         def addSiteToBlacklist(self, auth, loginbase, expires=0):
292                 """ Add a site to the blacklist, with an optional expiration time"""
293                 bl = BlacklistRecord.findby_or_create(hostname=hostname, expires=expires)
294                 return True
295
296         @cherrypy.expose
297         @export_to_docbook(roles=['admin'],
298                            accepts=[Parameter(dict, "Auth struct"),
299                                                                 Parameter(str, "loginbase"), 
300                                                                 Parameter(str, "hostname"),],
301                                            returns=Parameter(bool, 'True on success.'))
302         def deleteFromBlacklist(self, auth, loginbase=None, hostname=None):
303                 """ Remove a host or site from the blacklist """
304                 if (loginbase==None and hostname == None) or (loginbase != None and hostname != None):
305                         raise Exception("Please specify a single record to delete: either hostname or loginbase")
306                 elif loginbase != None:
307                         bl = BlacklistRecord.get_by(loginbase=loginbase)
308                         bl.delete()
309                 elif hostname != None:
310                         bl = BlacklistRecord.get_by(hostname=hostname)
311                         bl.delete()
312                 return True