-
Notifications
You must be signed in to change notification settings - Fork 0
/
nodemanager.py
executable file
·315 lines (272 loc) · 12 KB
/
nodemanager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#!/usr/bin/python3
#
# Useful information can be found at https://svn.planet-lab.org/wiki/NodeManager
#
# Faiyaz Ahmed <faiyaza at cs dot princeton dot edu>
# Copyright (C) 2008 The Trustees of Princeton University
"""
Node Manager
"""
import time
import os
import sys
import glob
import pickle
import random
from argparse import ArgumentParser
import logger
import tools
from config import Config
from plcapi import PLCAPI
class NodeManager:
PLUGIN_PATH = "/usr/share/NodeManager/plugins"
DB_FILE = "/var/lib/nodemanager/getslivers.pickle"
# the modules in this directory that need to be run
# NOTE: modules listed here will also be loaded in this order
# once loaded, they get re-ordered after their priority (lower comes first)
# for determining the runtime order
core_modules = ['net', 'conf_files', 'slivermanager', 'bwmon']
default_period = 600
default_random = 301
default_priority = 100
def __init__ (self):
parser = ArgumentParser()
parser.add_argument(
'-d', '--daemon', action='store_true', dest='daemon',
default=False,
help='run daemonized')
parser.add_argument(
'-f', '--config', action='store', dest='config',
default='/etc/planetlab/plc_config',
help='PLC configuration file')
parser.add_argument(
'-k', '--session', action='store', dest='session',
default='/etc/planetlab/session',
help='API session key (or file)')
parser.add_argument(
'-p', '--period', action='store', dest='period',
default=NodeManager.default_period,
help='Polling interval (sec) - default {}'
.format(NodeManager.default_period))
parser.add_argument(
'-r', '--random', action='store', dest='random',
default=NodeManager.default_random,
help='Range for additional random polling interval (sec) -- default {}'
.format(NodeManager.default_random))
parser.add_argument(
'-v', '--verbose', action='store_true', dest='verbose',
default=False,
help='more verbose log')
parser.add_argument(
'-P', '--path', action='store', dest='path',
default=NodeManager.PLUGIN_PATH,
help='Path to plugins directory')
parser.add_argument(
'-m', '--module', action='store', dest='user_module',
default='',
help='run a single module')
self.options = parser.parse_args()
# determine the modules to be run
self.modules = NodeManager.core_modules
# Deal with plugins directory
if os.path.exists(self.options.path):
sys.path.append(self.options.path)
plugins = [
os.path.split(os.path.splitext(x)[0])[1]
for x in glob.glob( os.path.join(self.options.path, '*.py') )
if not x.endswith("/__init__.py")
]
self.modules += plugins
if self.options.user_module:
assert self.options.user_module in self.modules
self.modules = [self.options.user_module]
logger.verbose('nodemanager: Running single module {}'.format(self.options.user_module))
def GetSlivers(self, config, plc):
"""
Retrieve GetSlivers at PLC and trigger callbacks defined in modules/plugins
"""
try:
logger.log("nodemanager: Syncing w/ PLC")
# retrieve GetSlivers from PLC
data = plc.GetSlivers()
# use the magic 'default' slice to retrieve system-wide defaults
self.getPLCDefaults(data, config)
# tweak the 'vref' attribute from GetSliceFamily
self.setSliversVref(data)
# dump it too, so it can be retrieved later in case of comm. failure
self.dumpSlivers(data)
# log it for debug purposes, no matter what verbose is
logger.log_slivers(data)
logger.verbose("nodemanager: Sync w/ PLC done")
last_data = data
except:
logger.log_exc("nodemanager: failed in GetSlivers")
# XXX So some modules can at least boostrap.
logger.log("nodemanager: Can't contact PLC to GetSlivers(). Continuing.")
data = {}
# for modules that request it though the 'persistent_data' property
last_data = self.loadSlivers()
# Invoke GetSlivers() functions from the callback modules
for module in self.loaded_modules:
logger.verbose('nodemanager: triggering {}.GetSlivers'.format(module.__name__))
try:
callback = getattr(module, 'GetSlivers')
module_data = data
if getattr(module, 'persistent_data', False):
module_data = last_data
callback(data, config, plc)
except SystemExit as e:
sys.exit(e)
except:
logger.log_exc("nodemanager: GetSlivers failed to run callback for module {}"
.format(module))
def getPLCDefaults(self, data, config):
"""
Get PLC wide defaults from _default system slice. Adds them to config class.
"""
for slice in data.get('slivers'):
if slice['name'] == config.PLC_SLICE_PREFIX + "_default":
attr_dict = {}
for attr in slice.get('attributes'):
attr_dict[attr['tagname']] = attr['value']
if attr_dict:
logger.verbose("nodemanager: Found default slice overrides.\n {}"
.format(attr_dict))
config.OVERRIDES = attr_dict
return
# NOTE: if an _default slice existed, it would have been found above and
# the routine would return. Thus, if we've gotten here, then no default
# slice is bound to this node.
if 'OVERRIDES' in dir(config):
del config.OVERRIDES
def setSliversVref (self, data):
"""
Tweak the 'vref' attribute in all slivers based on the 'GetSliceFamily' key
"""
# GetSlivers exposes the result of GetSliceFamily() as an separate key in data
# It is safe to override the attributes with this, as this method has the right logic
for sliver in data.get('slivers'):
try:
slicefamily = sliver.get('GetSliceFamily')
for att in sliver['attributes']:
if att['tagname'] == 'vref':
att['value'] = slicefamily
continue
sliver['attributes'].append(
{'tagname': 'vref', 'value': slicefamily})
except Exception:
logger.log_exc(
"nodemanager: Could not overwrite 'vref' attribute from 'GetSliceFamily'",
name=sliver['name'])
def dumpSlivers (self, slivers):
with open(NodeManager.DB_FILE, "wb") as feed:
logger.log ("nodemanager: saving successfully fetched GetSlivers in {}"
.format(NodeManager.DB_FILE))
pickle.dump(slivers, feed)
def loadSlivers (self):
try:
with open(NodeManager.DB_FILE, "rb+") as feed:
logger.log("nodemanager: restoring latest known GetSlivers from {}"
.format(NodeManager.DB_FILE))
slivers = pickle.load(feed)
return slivers
except:
logger.log("Could not restore GetSlivers from {}"
.format(NodeManager.DB_FILE))
return {}
def run(self):
# make sure to create /etc/planetlab/virt so others can read that
# used e.g. in vsys-scripts's sliceip
tools.get_node_virt()
try:
if self.options.daemon:
tools.daemon()
# set log level
if self.options.verbose:
logger.set_level(logger.LOG_VERBOSE)
tools.init_signals()
# Load /etc/planetlab/plc_config
config = Config(self.options.config)
try:
other_pid = tools.pid_file()
if other_pid is not None:
print("""There might be another instance of the node manager running as pid {}.
If this is not the case, please remove the pid file {}. -- exiting"""
.format(other_pid, tools.PID_FILE))
return
except OSError as err:
print("Warning while writing PID file:", err)
# load modules
self.loaded_modules = []
for module in self.modules:
try:
m = __import__(module)
logger.verbose("nodemanager: triggering {}.start".format(m.__name__))
try:
m.start()
except Exception:
logger.log("WARNING: module {} did not start".format(m.__name__))
self.loaded_modules.append(m)
except Exception:
if module not in NodeManager.core_modules:
logger.log_exc("ERROR while loading module {} - skipped"
.format(module))
else:
logger.log("FATAL : failed to start core module {}".format(module))
sys.exit(1)
# sort on priority (lower first)
def module_priority(module):
return getattr(module, 'priority', NodeManager.default_priority)
self.loaded_modules.sort(key=module_priority)
logger.log('ordered modules:')
for module in self.loaded_modules:
logger.log('{}: {}'
.format(getattr(module, 'priority',
NodeManager.default_priority),
module.__name__))
# Load /etc/planetlab/session
if os.path.exists(self.options.session):
with open(self.options.session) as feed:
session = feed.read().strip()
else:
session = None
# get random periods
iperiod = int(self.options.period)
irandom = int(self.options.random)
# Initialize XML-RPC client
plc = PLCAPI(config.plc_api_uri, config.cacert, session, timeout=iperiod/2)
#check auth
logger.log("nodemanager: Checking Auth.")
while not plc.check_authentication():
try:
plc.update_session()
logger.log("nodemanager: Authentication Failure. Retrying")
except Exception as exc:
logger.log("nodemanager: Retry Failed. ({}); Waiting.."
.format(exc))
time.sleep(iperiod)
logger.log("nodemanager: Authentication Succeeded!")
while True:
# Main nodemanager Loop
work_beg = time.time()
logger.log('nodemanager: mainloop - calling GetSlivers - period={} random={}'
.format(iperiod, irandom))
self.GetSlivers(config, plc)
delay = iperiod + random.randrange(0, irandom)
work_end = time.time()
work_duration = int(work_end-work_beg)
logger.log('nodemanager: mainloop has worked for {} s - sleeping for {} s'
.format(work_duration, delay))
time.sleep(delay)
except SystemExit:
pass
except:
logger.log_exc("nodemanager: failed in run")
def run():
logger.log("======================================== Entering nodemanager.py")
NodeManager().run()
if __name__ == '__main__':
run()
else:
# This is for debugging purposes. Open a copy of Python and import nodemanager
tools.as_daemon_thread(run)