04dcfeef447c74019c7637a15d01faeb2de71e31
[monitor.git] / RunlevelAgent.py
1 #!/usr/bin/python
2 #
3 # RunlevelAgent - acts as a heartbeat back to myplc reporting that the node is
4 #       online and whether it is in boot or pre-boot run-level.
5 #   This is useful to identify nodes that are behind a firewall, as well as to
6 #   have the machine report run-time status both in safeboot and boot modes,
7 #   so that it is immediately visible at myplc (gui or api).
8
9
10 import xml, xmlrpclib
11 import logging
12 import time
13 import traceback
14 import sys
15 import os
16 import string
17
18 CONFIG_FILE="/tmp/source/configuration"
19 SESSION_FILE="/etc/planetlab/session"
20
21 def read_config_file(filename):
22     ## NOTE: text copied from BootManager.py 
23     # TODO: unify this code to make it common. i.e. use ConfigParser module
24     vars = {}
25     vars_file= file(filename,'r')
26     validConfFile = True
27     for line in vars_file:
28         # if its a comment or a whitespace line, ignore
29         if line[:1] == "#" or string.strip(line) == "":
30             continue
31
32         parts= string.split(line,"=")
33         if len(parts) != 2:
34             print "Invalid line in vars file: %s" % line
35             validConfFile = False
36             break
37
38         name= string.strip(parts[0])
39         value= string.strip(parts[1])
40         vars[name]= value
41
42     vars_file.close()
43     if not validConfFile:
44         print "Unable to read configuration vars."
45
46     return vars
47
48 try:
49     sys.path = ['/etc/planetlab'] + sys.path
50     import plc_config
51     api_server_url = "https://" + plc_config.PLC_API_HOST + plc_config.PLC_API_PATH
52 except:
53     filename=CONFIG_FILE
54     vars = read_config_file(filename)
55     api_server_url = vars['BOOT_API_SERVER']
56
57
58 class Auth:
59     def __init__(self, username=None, password=None, **kwargs):
60         if 'session' in kwargs:
61             self.auth= { 'AuthMethod' : 'session',
62                     'session' : kwargs['session'] }
63         else:
64             if username==None and password==None:
65                 self.auth = {'AuthMethod': "anonymous"}
66             else:
67                 self.auth = {'Username' : username,
68                             'AuthMethod' : 'password',
69                             'AuthString' : password}
70 class PLC:
71     def __init__(self, auth, url):
72         self.auth = auth
73         self.url = url
74         self.api = xmlrpclib.Server(self.url, verbose=False, allow_none=True)
75
76     def __getattr__(self, name):
77         method = getattr(self.api, name)
78         if method is None:
79             raise AssertionError("method does not exist")
80
81         return lambda *params : method(self.auth.auth, *params)
82
83     def __repr__(self):
84         return self.api.__repr__()
85
86 def extract_from(filename, pattern):
87     f = os.popen("grep -E %s %s" % (pattern, filename))
88     val = f.read().strip()
89     return val
90
91 def check_running(commandname):
92     f = os.popen("ps ax | grep -E %s | grep -v grep" % (commandname))
93     val = f.read().strip()
94     return val
95     
96
97 def main():
98
99     f=open(SESSION_FILE,'r')
100     session_str=f.read().strip()
101     api = PLC(Auth(session=session_str), api_server_url)
102     # NOTE: What should we do if this call fails?
103         # TODO: handle dns failure here.
104     api.AuthCheck()
105
106     try:
107         env = 'production'
108         if len(sys.argv) > 1:
109             env = sys.argv[1]
110     except:
111         traceback.print_exc()
112
113     while True:
114         try:
115             # NOTE: here we are inferring the runlevel by environmental
116             #         observations.  We know how this process was started by the
117             #         given command line argument.  Then in bootmanager
118             #         runlevle, the bm.log gives information about the current
119             #         activity.
120             # other options:
121             #   call plc for current boot state?
122             #   how long have we been running?
123             if env == "bootmanager":
124                 bs_val = extract_from('/tmp/bm.log', 'Current boot state:')
125                 if len(bs_val) > 0: bs_val = bs_val.split()[-1]
126                 ex_val = extract_from('/tmp/bm.log', 'Exception')
127                 fs_val = extract_from('/tmp/bm.log', 'mke2fs')
128                 bm_val = check_running("BootManager.py")
129
130                 if bs_val in ['diag', 'diagnose', 'safeboot', 'disabled', 'disable']:
131                     api.ReportRunlevel({'run_level' : 'safeboot'})
132
133                 elif len(ex_val) > len("Exception"):
134                     api.ReportRunlevel({'run_level' : 'failboot'})
135
136                 elif len(fs_val) > 0 and len(bm_val) > 0:
137                     api.ReportRunlevel({'run_level' : 'reinstall'})
138
139                 else:
140                     api.ReportRunlevel({'run_level' : 'failboot'})
141
142             elif env == "production":
143                 api.ReportRunlevel({'run_level' : 'boot'})
144             else:
145                 api.ReportRunlevel({'run_level' : 'failboot'})
146                 
147         except:
148             print "reporting error: ", os.popen("uptime").read().strip()
149             traceback.print_exc()
150
151         sys.stdout.flush()
152         # TODO: change to a configurable value
153         time.sleep(60*15)
154
155 if __name__ == "__main__":
156     main()