Tagging module NodeManager - NodeManager-1.7-42
[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 import net
28
29 id="$Id$"
30 savedargv = sys.argv[:]
31
32 known_modules=['conf_files', 'sm', 'bwmon', 'vsys', 'codemux']
33
34 parser = optparse.OptionParser()
35 parser.add_option('-d', '--daemon', action='store_true', dest='daemon', default=False, help='run daemonized')
36 parser.add_option('-s', '--startup', action='store_true', dest='startup', default=False, help='run all sliver startup scripts')
37 parser.add_option('-f', '--config', action='store', dest='config', default='/etc/planetlab/plc_config', help='PLC configuration file')
38 parser.add_option('-k', '--session', action='store', dest='session', default='/etc/planetlab/session', help='API session key (or file)')
39 parser.add_option('-p', '--period', action='store', dest='period', default=600, help='Polling interval (sec)')
40 parser.add_option('-r', '--random', action='store', dest='random', default=301, help='Range for additional random polling interval (sec)')
41 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='more verbose log')
42 parser.add_option('-P', '--path', action='store', dest='path', default='/usr/share/NodeManager/plugins', help='Path to plugins directory')
43 parser.add_option('-m', '--module', action='store', dest='module', default='', help='run a single module among '+' '.join(known_modules))
44 (options, args) = parser.parse_args()
45
46 # Deal with plugins directory
47 if os.path.exists(options.path):
48     sys.path.append(options.path)
49     known_modules += [i[:-3] for i in os.listdir(options.path) if i.endswith(".py") and (i[:-3] not in known_modules)]
50
51 modules = []
52
53 def GetSlivers(plc, config):
54     '''Run call backs defined in modules'''
55     try: 
56         logger.log("Syncing w/ PLC")
57         data = plc.GetSlivers()
58         getPLCDefaults(data, config)
59     except: 
60         logger.log_exc()
61         #  XXX So some modules can at least boostrap.
62         data = {}
63     if (options.verbose):
64         logger.log_slivers(data)
65     # Set i2 ip list for nodes in I2 nodegroup.
66     try: net.GetSlivers(plc, data, config) # TODO - num of args needs to be unified across mods.
67     except: logger.log_exc()
68     #  All other callback modules
69     for module in modules:
70         try:        
71             callback = getattr(module, 'GetSlivers')
72             callback(data)
73         except: logger.log_exc()
74
75
76 def getPLCDefaults(data, config):
77     '''
78     Get PLC wide defaults from _default system slice.  Adds them to config class.
79     '''
80     for slice in data.get('slivers'): 
81         if slice['name'] == config.PLC_SLICE_PREFIX+"_default":
82             attr_dict = {}
83             for attr in slice.get('attributes'): attr_dict[attr['name']] = attr['value'] 
84             if len(attr_dict):
85                 logger.verbose("Found default slice overrides.\n %s" % attr_dict)
86                 config.OVERRIDES = attr_dict
87             return 
88     if 'OVERRIDES' in dir(config): del config.OVERRIDES
89
90
91 def run():
92     try:
93         if options.daemon: tools.daemon()
94
95         # set log level
96         if (options.verbose):
97             logger.set_level(logger.LOG_VERBOSE)
98
99         # Load /etc/planetlab/plc_config
100         config = Config(options.config)
101
102         try:
103             other_pid = tools.pid_file()
104             if other_pid != None:
105                 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)
106                 return
107         except OSError, err:
108             print "Warning while writing PID file:", err
109
110         # Load and start modules
111         if options.module:
112             assert options.module in known_modules
113             running_modules=[options.module]
114             logger.verbose('Running single module %s'%options.module)
115         else:
116             running_modules=known_modules
117         for module in running_modules:
118             try:
119                 m = __import__(module)
120                 m.start(options, config)
121                 modules.append(m)
122             except ImportError, err:
123                 print "Warning while loading module %s:" % module, err
124
125         # Load /etc/planetlab/session
126         if os.path.exists(options.session):
127             session = file(options.session).read().strip()
128         else:
129             session = options.session
130
131         # Initialize XML-RPC client
132         iperiod=int(options.period)
133         irandom=int(options.random)
134         plc = PLCAPI(config.plc_api_uri, config.cacert, session, timeout=iperiod/2)
135
136         while True:
137         # Main NM Loop
138             logger.verbose('mainloop - nm:getSlivers - period=%d random=%d'%(iperiod,irandom))
139             GetSlivers(plc, config)
140             delay=iperiod + random.randrange(0,irandom)
141             logger.verbose('mainloop - sleeping for %d s'%delay)
142             time.sleep(delay)
143     except: logger.log_exc()
144
145
146 if __name__ == '__main__':
147     logger.log("Entering nm.py "+id)
148     stacklim = 512*1024  # 0.5 MiB
149     curlim = resource.getrlimit(resource.RLIMIT_STACK)[0]  # soft limit
150     if curlim > stacklim:
151         resource.setrlimit(resource.RLIMIT_STACK, (stacklim, stacklim))
152         # for some reason, doesn't take effect properly without the exec()
153         python = '/usr/bin/python'
154         os.execv(python, [python] + savedargv)
155     run()
156 else:
157     # This is for debugging purposes.  Open a copy of Python and import nm
158     tools.as_daemon_thread(run)