Setting tag nodemanager-1.8-39
[nodemanager.git] / nm.py
1 #!/usr/bin/python
2
3 #
4 # Useful information can be found at https://svn.planet-lab.org/wiki/NodeManager
5 #
6
7 # Faiyaz Ahmed <faiyaza at cs dot princeton dot edu>
8 # Copyright (C) 2008 The Trustees of Princeton University
9
10
11 """Node Manager"""
12
13 import optparse
14 import time
15 import xmlrpclib
16 import socket
17 import os
18 import sys
19 import resource
20
21 import logger
22 import tools
23
24 from config import Config
25 from plcapi import PLCAPI 
26 import random
27
28 id="$Id$"
29 savedargv = sys.argv[:]
30
31 # NOTE: modules listed here should also be loaded in this order
32 known_modules=['net','conf_files', 'sm', 'bwmon']
33
34 plugin_path = "/usr/share/NodeManager/plugins"
35
36 parser = optparse.OptionParser()
37 parser.add_option('-d', '--daemon', action='store_true', dest='daemon', default=False, help='run daemonized')
38 parser.add_option('-s', '--startup', action='store_true', dest='startup', default=False, help='run all sliver startup scripts')
39 parser.add_option('-f', '--config', action='store', dest='config', default='/etc/planetlab/plc_config', help='PLC configuration file')
40 parser.add_option('-k', '--session', action='store', dest='session', default='/etc/planetlab/session', help='API session key (or file)')
41 parser.add_option('-p', '--period', action='store', dest='period', default=600, help='Polling interval (sec)')
42 parser.add_option('-r', '--random', action='store', dest='random', default=301, help='Range for additional random polling interval (sec)')
43 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='more verbose log')
44 parser.add_option('-P', '--path', action='store', dest='path', default=plugin_path, help='Path to plugins directory')
45
46 # NOTE: BUG the 'help' for this parser.add_option() wont list plugins from the --path argument
47 parser.add_option('-m', '--module', action='store', dest='module', default='', help='run a single module among '+' '.join(known_modules))
48 (options, args) = parser.parse_args()
49
50 # Deal with plugins directory
51 if os.path.exists(options.path):
52     sys.path.append(options.path)
53     known_modules += [i[:-3] for i in os.listdir(options.path) if i.endswith(".py") and (i[:-3] not in known_modules)]
54
55 modules = []
56
57 def GetSlivers(config, plc):
58     '''Run call backs defined in modules'''
59     try: 
60         logger.log("Syncing w/ PLC")
61         data = plc.GetSlivers()
62         if (options.verbose): logger.log_slivers(data)
63         getPLCDefaults(data, config)
64     except: 
65         logger.log_exc()
66         #  XXX So some modules can at least boostrap.
67         logger.log("nm:  Can't contact PLC to GetSlivers().  Continuing.")
68         data = {}
69     #  Invoke GetSlivers() functions from the callback modules
70     for module in modules:
71         try:        
72             callback = getattr(module, 'GetSlivers')
73             callback(data, config, plc)
74         except: logger.log_exc()
75
76
77 def getPLCDefaults(data, config):
78     '''
79     Get PLC wide defaults from _default system slice.  Adds them to config class.
80     '''
81     for slice in data.get('slivers'): 
82         if slice['name'] == config.PLC_SLICE_PREFIX+"_default":
83             attr_dict = {}
84             for attr in slice.get('attributes'): attr_dict[attr['tagname']] = attr['value'] 
85             if len(attr_dict):
86                 logger.verbose("Found default slice overrides.\n %s" % attr_dict)
87                 config.OVERRIDES = attr_dict
88                 return
89     # NOTE: if an _default slice existed, it would have been found above and
90         #               the routine would return.  Thus, if we've gotten here, then no default
91         #               slice is bound to this node.
92     if 'OVERRIDES' in dir(config): del config.OVERRIDES
93
94
95 def run():
96     try:
97         if options.daemon: tools.daemon()
98
99         # set log level
100         if (options.verbose):
101             logger.set_level(logger.LOG_VERBOSE)
102
103         # Load /etc/planetlab/plc_config
104         config = Config(options.config)
105
106         try:
107             other_pid = tools.pid_file()
108             if other_pid != None:
109                 print """There might be another instance of the node manager running as pid %d.  If this is not the case, please remove the pid file %s""" % (other_pid, tools.PID_FILE)
110                 return
111         except OSError, err:
112             print "Warning while writing PID file:", err
113
114         # Load and start modules
115         if options.module:
116             assert options.module in known_modules
117             running_modules=[options.module]
118             logger.verbose('Running single module %s'%options.module)
119         else:
120             running_modules=known_modules
121         for module in running_modules:
122             try:
123                 m = __import__(module)
124                 m.start(options, config)
125                 modules.append(m)
126             except ImportError, err:
127                 print "Warning while loading module %s:" % module, err
128
129         # Load /etc/planetlab/session
130         if os.path.exists(options.session):
131             session = file(options.session).read().strip()
132         else:
133             session = None
134
135         # Initialize XML-RPC client
136         iperiod=int(options.period)
137         irandom=int(options.random)
138         plc = PLCAPI(config.plc_api_uri, config.cacert, session, timeout=iperiod/2)
139
140         #check auth
141         logger.log("Checking Auth.")
142         while plc.check_authentication() != True:
143             try:
144                 plc.update_session()
145                 logger.log("Authentication Failure.  Retrying")
146             except:
147                 logger.log("Retry Failed.  Waiting")
148             time.sleep(iperiod)
149         logger.log("Authentication Succeeded!")
150
151
152         while True:
153         # Main NM Loop
154             logger.verbose('mainloop - nm:getSlivers - period=%d random=%d'%(iperiod,irandom))
155             GetSlivers(config, plc)
156             delay=iperiod + random.randrange(0,irandom)
157             logger.verbose('mainloop - sleeping for %d s'%delay)
158             time.sleep(delay)
159     except: logger.log_exc()
160
161
162 if __name__ == '__main__':
163     logger.log("Entering nm.py "+id)
164     run()
165 else:
166     # This is for debugging purposes.  Open a copy of Python and import nm
167     tools.as_daemon_thread(run)