Merge database from trunk. Runs ensure created on every account type.
[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             elif 'OVERRIDES' in dir(config): del config.OVERRIDES
88
89
90 def run():
91     try:
92         if options.daemon: tools.daemon()
93
94         # set log level
95         if (options.verbose):
96             logger.set_level(logger.LOG_VERBOSE)
97
98         # Load /etc/planetlab/plc_config
99         config = Config(options.config)
100
101         try:
102             other_pid = tools.pid_file()
103             if other_pid != None:
104                 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)
105                 return
106         except OSError, err:
107             print "Warning while writing PID file:", err
108
109         # Load and start modules
110         if options.module:
111             assert options.module in known_modules
112             running_modules=[options.module]
113             logger.verbose('Running single module %s'%options.module)
114         else:
115             running_modules=known_modules
116         for module in running_modules:
117             try:
118                 m = __import__(module)
119                 m.start(options, config)
120                 modules.append(m)
121             except ImportError, err:
122                 print "Warning while loading module %s:" % module, err
123
124         # Load /etc/planetlab/session
125         if os.path.exists(options.session):
126             session = file(options.session).read().strip()
127         else:
128             session = options.session
129
130         # Initialize XML-RPC client
131         iperiod=int(options.period)
132         irandom=int(options.random)
133         plc = PLCAPI(config.plc_api_uri, config.cacert, session, timeout=iperiod/2)
134
135         while True:
136         # Main NM Loop
137             logger.verbose('mainloop - nm:getSlivers - period=%d random=%d'%(iperiod,irandom))
138             GetSlivers(plc, config)
139             delay=iperiod + random.randrange(0,irandom)
140             logger.verbose('mainloop - sleeping for %d s'%delay)
141             time.sleep(delay)
142     except: logger.log_exc()
143
144
145 if __name__ == '__main__':
146     logger.log("Entering nm.py "+id)
147     stacklim = 512*1024  # 0.5 MiB
148     curlim = resource.getrlimit(resource.RLIMIT_STACK)[0]  # soft limit
149     if curlim > stacklim:
150         resource.setrlimit(resource.RLIMIT_STACK, (stacklim, stacklim))
151         # for some reason, doesn't take effect properly without the exec()
152         python = '/usr/bin/python'
153         os.execv(python, [python] + savedargv)
154     run()
155 else:
156     # This is for debugging purposes.  Open a copy of Python and import nm
157     tools.as_daemon_thread(run)