1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
"""
|
10
|
Services Twisted de collection et de publication de données.
|
11
|
"""
|
12
|
|
13
|
import locale, gettext, os, pwd, shutil, random
|
14
|
from pathlib2 import Path
|
15
|
from glob import glob
|
16
|
import cjson
|
17
|
import traceback
|
18
|
|
19
|
|
20
|
from zephir.monitor.agentmanager import ZEPHIRAGENTS_DATADIR
|
21
|
APP = 'zephir-agents'
|
22
|
DIR = os.path.join(ZEPHIRAGENTS_DATADIR, 'i18n')
|
23
|
gettext.install(APP, DIR, unicode=False)
|
24
|
|
25
|
|
26
|
from twisted.application import internet, service
|
27
|
from twisted.internet import utils, reactor
|
28
|
from twisted.web import resource, server, static, util, xmlrpc
|
29
|
from twisted.python import syslog
|
30
|
|
31
|
from zephir.monitor.agentmanager import config as cfg
|
32
|
from zephir.monitor.agentmanager.util import ensure_dirs, md5file, get_md5files, log
|
33
|
from zephir.monitor.agentmanager.web_resources import ZephirServerResource
|
34
|
from zephir.monitor.agentmanager.clientmanager import ClientManager
|
35
|
|
36
|
try:
|
37
|
import zephir.zephir_conf.zephir_conf as conf_zeph
|
38
|
from zephir.lib_zephir import zephir_proxy, convert, zephir_dir, update_sudoers, charset
|
39
|
from zephir.lib_zephir import log as zeph_log
|
40
|
registered = 1
|
41
|
except:
|
42
|
|
43
|
registered = 0
|
44
|
|
45
|
class ZephirService(service.MultiService):
|
46
|
"""Main Twisted service for Zephir apps"""
|
47
|
|
48
|
def __init__(self, config, root_resource=None, serve_static=False):
|
49
|
"""config will be completed by default values"""
|
50
|
service.MultiService.__init__(self)
|
51
|
self.config = cfg.DEFAULT_CONFIG.copy()
|
52
|
self.config.update(config)
|
53
|
self.updater = self.publisher = None
|
54
|
|
55
|
if registered:
|
56
|
update_sudoers()
|
57
|
|
58
|
if root_resource is None:
|
59
|
self.root_resource = resource.Resource()
|
60
|
webserver = internet.TCPServer(self.config['webserver_port'],
|
61
|
server.Site(self.root_resource))
|
62
|
webserver.setServiceParent(service.IServiceCollection(self))
|
63
|
else:
|
64
|
self.root_resource = root_resource
|
65
|
|
66
|
if serve_static:
|
67
|
self.root_resource.putChild('static',
|
68
|
static.File(self.config['static_web_dir']))
|
69
|
|
70
|
|
71
|
|
72
|
|
73
|
def with_updater(self):
|
74
|
assert self.updater is None
|
75
|
self.updater = UpdaterService(self.config, self, self.root_resource)
|
76
|
return self
|
77
|
|
78
|
def with_publisher(self):
|
79
|
assert self.publisher is None
|
80
|
self.publisher = PublisherService(self.config, self, self.root_resource)
|
81
|
return self
|
82
|
|
83
|
def with_updater_and_publisher(self):
|
84
|
assert self.updater is None
|
85
|
assert self.publisher is None
|
86
|
self.updater = UpdaterService(self.config, self, self.root_resource)
|
87
|
self.publisher = PublisherService(self.config, self, self.root_resource,
|
88
|
show_clients_page = False,
|
89
|
live_agents={self.config['host_ref']: self.updater.agents})
|
90
|
return self
|
91
|
|
92
|
|
93
|
|
94
|
|
95
|
class UpdaterService(service.MultiService, xmlrpc.XMLRPC):
|
96
|
"""Schedules measures, data serialisation and upload."""
|
97
|
|
98
|
def __init__(self, config, parent, root_resource):
|
99
|
"""config should be complete"""
|
100
|
service.MultiService.__init__(self)
|
101
|
xmlrpc.XMLRPC.__init__(self)
|
102
|
self.old_obs = None
|
103
|
self.config = config
|
104
|
|
105
|
self.update_static_data()
|
106
|
|
107
|
loc, enc = locale.getdefaultlocale()
|
108
|
log.msg(_('default locale: %s encoding: %s') % (loc, enc))
|
109
|
if enc == 'utf':
|
110
|
log.msg(_('Warning: locale encoding %s broken in RRD graphs, set e.g: LC_ALL=fr_FR') % enc)
|
111
|
self.agents = self.load_agents()
|
112
|
|
113
|
self.setServiceParent(service.IServiceCollection(parent))
|
114
|
root_resource.putChild('xmlrpc', self)
|
115
|
|
116
|
def startService(self):
|
117
|
"""initialize zephir services"""
|
118
|
service.MultiService.startService(self)
|
119
|
reactor.callLater(2,self.schedule_all)
|
120
|
|
121
|
|
122
|
|
123
|
self.old_obs = None
|
124
|
if len(log.theLogPublisher.observers) >= 1:
|
125
|
self.old_obs = log.theLogPublisher.observers[0]
|
126
|
try:
|
127
|
from zephir.backend import config as conf_zeph
|
128
|
log_prefix = 'zephir_backend'
|
129
|
except:
|
130
|
log_prefix = 'zephiragents'
|
131
|
new_obs = syslog.SyslogObserver(log_prefix, options=syslog.DEFAULT_OPTIONS, facility=syslog.DEFAULT_FACILITY)
|
132
|
log.addObserver(new_obs.emit)
|
133
|
log.removeObserver(self.old_obs)
|
134
|
if registered != 0:
|
135
|
|
136
|
|
137
|
self.setup_uucp()
|
138
|
|
139
|
if os.path.isfile(os.path.join(zephir_dir,'reboot.lck')):
|
140
|
try:
|
141
|
zeph_log('REBOOT',0,'redémarrage du serveur terminé')
|
142
|
os.unlink(os.path.join(zephir_dir,'reboot.lck'))
|
143
|
except:
|
144
|
pass
|
145
|
|
146
|
def stopService(self):
|
147
|
"""stops zephir services"""
|
148
|
if self.old_obs:
|
149
|
log.removeObserver(log.theLogPublisher.observers[0])
|
150
|
log.addObserver(self.old_obs)
|
151
|
service.MultiService.stopService(self)
|
152
|
|
153
|
def load_agents(self):
|
154
|
"""Charge tous les agents du répertoire de configurations."""
|
155
|
log.msg(_("Loading agents from %s...") % self.config['config_dir'])
|
156
|
loaded_agents = {}
|
157
|
list_agents = glob(os.path.join(self.config['config_dir'], "*.agent"))
|
158
|
for f in list_agents:
|
159
|
log.msg(_(" from %s:") % os.path.basename(f))
|
160
|
h = { 'AGENTS': None }
|
161
|
execfile(f, globals(), h)
|
162
|
assert h.has_key('AGENTS')
|
163
|
for a in h['AGENTS']:
|
164
|
assert not loaded_agents.has_key(a.name)
|
165
|
|
166
|
a.init_data(os.path.join(self.config['state_dir'],
|
167
|
self.config['host_ref'],
|
168
|
a.name))
|
169
|
a.manager = self
|
170
|
a.archive()
|
171
|
loaded_agents[a.name] = a
|
172
|
log.msg(_(" %s, period %d") % (a.name, a.period))
|
173
|
log.msg(_("Loaded."))
|
174
|
return loaded_agents
|
175
|
|
176
|
|
177
|
|
178
|
|
179
|
def schedule(self, agent_name):
|
180
|
"""Planifie les mesures périodiques d'un agent."""
|
181
|
assert self.agents.has_key(agent_name)
|
182
|
if self.agents[agent_name].period > 0:
|
183
|
timer = internet.TimerService(self.agents[agent_name].period,
|
184
|
self.wakeup_for_measure, agent_name)
|
185
|
timer.setName(agent_name)
|
186
|
timer.setServiceParent(service.IServiceCollection(self))
|
187
|
|
188
|
|
189
|
def wakeup_for_measure(self, agent_name):
|
190
|
"""Callback pour les mesures planifiées."""
|
191
|
assert self.agents.has_key(agent_name)
|
192
|
|
193
|
self.agents[agent_name].scheduled_measure()
|
194
|
|
195
|
|
196
|
def schedule_all(self):
|
197
|
"""Planifie tous les agents chargés.
|
198
|
Démarre le cycle de mesures périodiques de chaque agent
|
199
|
chargé. La première mesure est prise immédiatement.
|
200
|
"""
|
201
|
for agent_name in self.agents.keys():
|
202
|
|
203
|
|
204
|
for action_dir in (os.path.join(self.config['action_dir'],'eole'), self.config['action_dir']):
|
205
|
f_actions = os.path.join(action_dir, "%s.actions" % agent_name)
|
206
|
if os.path.isfile(f_actions):
|
207
|
actions = {}
|
208
|
execfile(f_actions, globals(), actions)
|
209
|
for item in actions.keys():
|
210
|
if item.startswith('action_'):
|
211
|
setattr(self.agents[agent_name], item, actions[item])
|
212
|
|
213
|
self.schedule(agent_name)
|
214
|
|
215
|
|
216
|
def timer_for_agent_named(self, agent_name):
|
217
|
assert self.agents.has_key(agent_name)
|
218
|
return self.getServiceNamed(agent_name)
|
219
|
|
220
|
|
221
|
|
222
|
|
223
|
def setup_uucp(self):
|
224
|
ensure_dirs(self.config['uucp_dir'])
|
225
|
self.update_static_data()
|
226
|
|
227
|
try:
|
228
|
reload(conf_zeph)
|
229
|
|
230
|
|
231
|
|
232
|
|
233
|
if not os.path.isdir('/var/lib/zephir'):
|
234
|
for st_dir in os.listdir(self.config['state_dir']):
|
235
|
if st_dir != str(conf_zeph.id_serveur):
|
236
|
shutil.rmtree(os.path.join(self.config['state_dir'],st_dir))
|
237
|
|
238
|
period = convert(zephir_proxy.serveurs.get_timeout(conf_zeph.id_serveur)[1])
|
239
|
except:
|
240
|
period = 0
|
241
|
|
242
|
if period < 30:
|
243
|
period = self.config['upload_period']
|
244
|
log.msg(_('Using default period : %s seconds') % period)
|
245
|
|
246
|
|
247
|
delay = random.randrange(30,period)
|
248
|
reactor.callLater(delay,self.wakeup_for_upload)
|
249
|
|
250
|
def update_static_data(self):
|
251
|
original = os.path.join(self.config['config_dir'], 'site.cfg')
|
252
|
if os.path.isfile(original):
|
253
|
destination = cfg.client_data_dir(self.config, self.config['host_ref'])
|
254
|
ensure_dirs(destination)
|
255
|
need_copy = False
|
256
|
try:
|
257
|
org_mtime = os.path.getmtime(original)
|
258
|
dest_mtime = os.path.getmtime(os.path.join(destination, 'site.cfg'))
|
259
|
except OSError:
|
260
|
need_copy = True
|
261
|
if need_copy or (org_mtime > dest_mtime):
|
262
|
shutil.copy(original, destination)
|
263
|
|
264
|
def wakeup_for_upload(self, recall=True):
|
265
|
|
266
|
try:
|
267
|
reload(conf_zeph)
|
268
|
period = convert(zephir_proxy.serveurs.get_timeout(conf_zeph.id_serveur)[1])
|
269
|
except:
|
270
|
period = 0
|
271
|
|
272
|
if period < 30:
|
273
|
period = self.config['upload_period']
|
274
|
log.msg(_('Using default period : %s seconds') % period)
|
275
|
|
276
|
|
277
|
if recall:
|
278
|
reactor.callLater(period,self.wakeup_for_upload)
|
279
|
|
280
|
|
281
|
for agent in self.agents.values():
|
282
|
agent.archive()
|
283
|
|
284
|
self.update_static_data()
|
285
|
|
286
|
try:
|
287
|
assert conf_zeph.id_serveur != 0
|
288
|
client_dir = os.path.join(self.config['tmp_data_dir'],str(conf_zeph.id_serveur))
|
289
|
except:
|
290
|
client_dir = os.path.join(self.config['tmp_data_dir'],self.config['host_ref'])
|
291
|
try:
|
292
|
|
293
|
if os.path.isdir(client_dir):
|
294
|
shutil.rmtree(client_dir)
|
295
|
os.makedirs(client_dir)
|
296
|
except:
|
297
|
pass
|
298
|
args = ['-Rf',os.path.abspath(os.path.join(cfg.client_data_dir(self.config, self.config['host_ref']),'site.cfg'))]
|
299
|
ignore_file = os.path.abspath(os.path.join(self.config['state_dir'],'ignore_list'))
|
300
|
if os.path.exists(ignore_file):
|
301
|
args.append(ignore_file)
|
302
|
|
303
|
|
304
|
for agent_name in self.agents.keys():
|
305
|
args.append(os.path.abspath(cfg.agent_data_dir(self.config, self.config['host_ref'],agent_name)))
|
306
|
args.append(os.path.abspath(client_dir))
|
307
|
res = utils.getProcessOutput('/bin/cp', args = args)
|
308
|
res.addCallbacks(self._make_archive,
|
309
|
lambda x: log.msg(_("/!\ copy failed (%s)\n"
|
310
|
"data: %s")
|
311
|
% (x, self.config['state_dir'])))
|
312
|
|
313
|
def _check_md5(self):
|
314
|
def to_bytes(objet):
|
315
|
"""Transforme les objets unicode contenus dans un objet en bytes
|
316
|
"""
|
317
|
if isinstance(objet, tuple):
|
318
|
l = []
|
319
|
for item in objet:
|
320
|
l.append(to_bytes(item))
|
321
|
return '({})'.format(', '.join(l))
|
322
|
if isinstance(objet, list):
|
323
|
l = []
|
324
|
for item in objet:
|
325
|
l.append(to_bytes(item))
|
326
|
return '[{}]'.format(', '.join(l))
|
327
|
if isinstance(objet, dict):
|
328
|
dico={}
|
329
|
for cle in objet:
|
330
|
dico[to_bytes(cle)] = to_bytes(objet[cle])
|
331
|
return '{{{}}}'.format(', '.join(['{}: {}'.format(el[0], el[1]) for el in sorted(dico.items())]))
|
332
|
if isinstance(objet, unicode):
|
333
|
string = objet.encode(charset)
|
334
|
return "'{}'".format(string)
|
335
|
if isinstance(objet, int):
|
336
|
return str(objet)
|
337
|
if isinstance(objet, float):
|
338
|
return str(objet)
|
339
|
if objet == None:
|
340
|
return 'None'
|
341
|
return objet
|
342
|
|
343
|
|
344
|
rep_src = "/usr/share/eole/creole"
|
345
|
rep_conf = "/etc/eole"
|
346
|
data = []
|
347
|
try:
|
348
|
for src, dst, pattern in get_md5files(cfg.distrib_version):
|
349
|
if src == 'variables.eol':
|
350
|
|
351
|
orig_eol = os.path.join(rep_conf, 'config.eol')
|
352
|
if os.path.isfile(orig_eol):
|
353
|
var_eol = os.path.join(rep_src, 'variables.eol')
|
354
|
|
355
|
conf = cjson.decode(file(orig_eol).read(), all_unicode=True)
|
356
|
var_names = conf.keys()
|
357
|
var_names.sort()
|
358
|
f_var = file(var_eol, 'w')
|
359
|
for var_name in var_names:
|
360
|
if var_name not in ('mode_zephir', '___version___'):
|
361
|
|
362
|
|
363
|
|
364
|
if type(conf[var_name]) == dict and 'val' in conf[var_name]:
|
365
|
val = conf[var_name].get('val')
|
366
|
if isinstance(val, dict):
|
367
|
val = {str(k): str(v) for k, v in sorted(val.items(), key=lambda x: x[0])}
|
368
|
if isinstance(val, list) or isinstance(val, tuple):
|
369
|
val = to_bytes(val)
|
370
|
var_data = "{0}:{1}\n".format(var_name, val)
|
371
|
f_var.write(var_data)
|
372
|
f_var.close()
|
373
|
if os.path.isdir(os.path.join(rep_src,src)):
|
374
|
fics = os.listdir(os.path.join(rep_src,src))
|
375
|
fics = [(os.path.join(src,fic),os.path.join(dst,fic)) for fic in fics]
|
376
|
else:
|
377
|
fics = [(src,dst)]
|
378
|
for fic, fic_dst in fics:
|
379
|
if os.path.isfile(os.path.join(rep_src,fic)):
|
380
|
if (pattern is None) or fic.endswith(pattern):
|
381
|
md5res = md5file(os.path.join(rep_src,fic))
|
382
|
data.append("%s %s\n" % (md5res, fic_dst))
|
383
|
if Path('/usr/share/zephir/zephir_conf/fichiers_zephir').is_file():
|
384
|
with open('/usr/share/zephir/zephir_conf/fichiers_zephir', 'r') as fz_fh:
|
385
|
fichiers_zephir = []
|
386
|
for l in fz_fh.readlines():
|
387
|
if l.startswith('#'):
|
388
|
continue
|
389
|
if l.startswith('%%'):
|
390
|
break
|
391
|
fichier_serveur = Path(l.strip())
|
392
|
if fichier_serveur.is_file():
|
393
|
md5res = md5file(fichier_serveur.as_posix())
|
394
|
data.append("{} {}\n".format(md5res, Path('fichiers_zephir').joinpath(fichier_serveur.name)))
|
395
|
elif fichier_serveur.is_dir():
|
396
|
for step in os.walk(fichier_serveur.as_posix()):
|
397
|
for sub_fichier_serveur in step[2]:
|
398
|
sub_fichier_serveur_full_path = Path(step[0]).joinpath(sub_fichier_serveur)
|
399
|
md5res = md5file(sub_fichier_serveur_full_path.as_posix())
|
400
|
sub_fichier_serveur = sub_fichier_serveur_full_path.relative_to(fichier_serveur)
|
401
|
data.append("{} {}\n".format(md5res,
|
402
|
Path('fichiers_zephir').joinpath(fichier_serveur.name, sub_fichier_serveur)))
|
403
|
if Path('/usr/share/zephir/zephir_conf/fichiers_variante').is_file():
|
404
|
with open('/usr/share/zephir/zephir_conf/fichiers_variante', 'r') as fz_fh:
|
405
|
fichiers_zephir = []
|
406
|
for l in fz_fh.readlines():
|
407
|
if l.startswith('#'):
|
408
|
continue
|
409
|
if l.startswith('%%'):
|
410
|
break
|
411
|
fichier_serveur = Path(l.strip())
|
412
|
if fichier_serveur.is_file():
|
413
|
md5res = md5file(fichier_serveur.as_posix())
|
414
|
data.append("{} {}\n".format(md5res, Path('fichiers_zephir').joinpath('variante', fichier_serveur.name)))
|
415
|
except:
|
416
|
|
417
|
log.msg('!! Erreur rencontrée lors du calcul du md5 de config.eol !!')
|
418
|
traceback.print_exc()
|
419
|
try:
|
420
|
assert conf_zeph.id_serveur != 0
|
421
|
outf = file(os.path.join(self.config['tmp_data_dir'],"config%s.md5" % str(conf_zeph.id_serveur)), "w")
|
422
|
except:
|
423
|
outf = file(os.path.join(self.config['tmp_data_dir'],"config%s.md5" % self.config['host_ref']), "w")
|
424
|
outf.writelines(data)
|
425
|
outf.close()
|
426
|
|
427
|
def _get_packages(self, *args):
|
428
|
"""génère une liste des paquets installés
|
429
|
"""
|
430
|
try:
|
431
|
assert conf_zeph.id_serveur != 0
|
432
|
cmd_pkg = ("/usr/bin/dpkg-query -W >" + os.path.join(self.config['tmp_data_dir'],"packages%s.list" % str(conf_zeph.id_serveur)))
|
433
|
except:
|
434
|
cmd_pkg = ("/usr/bin/dpkg-query -W >" + os.path.join(self.config['tmp_data_dir'],"packages%s.list" % self.config['host_ref']))
|
435
|
os.system(cmd_pkg)
|
436
|
|
437
|
def _make_archive(self,*args):
|
438
|
self._check_md5()
|
439
|
self._get_packages()
|
440
|
|
441
|
try:
|
442
|
assert conf_zeph.id_serveur != 0
|
443
|
tarball = os.path.join(self.config['uucp_dir'],'site%s.tar' % str(conf_zeph.id_serveur))
|
444
|
except:
|
445
|
tarball = os.path.join(self.config['uucp_dir'],'site%s.tar' % self.config['host_ref'])
|
446
|
tar_cwd = os.path.dirname(os.path.abspath(self.config['tmp_data_dir']))
|
447
|
tar_dir = os.path.basename(os.path.abspath(self.config['tmp_data_dir']))
|
448
|
res = utils.getProcessOutput('/bin/tar',
|
449
|
args = ('czf', tarball,
|
450
|
'--exclude', 'private',
|
451
|
'-C', tar_cwd,
|
452
|
tar_dir))
|
453
|
res.addCallbacks(self._try_chown,
|
454
|
lambda x: log.msg(_("/!\ archiving failed (%s)\n"
|
455
|
"data: %s\narchive: %s")
|
456
|
% (str(x), self.config['state_dir'], tarball)),
|
457
|
callbackArgs = [tarball])
|
458
|
|
459
|
def _try_chown(self, tar_output, tarball):
|
460
|
try:
|
461
|
uucp_uid, uucp_gid = pwd.getpwnam('uucp')[2:4]
|
462
|
uid = os.getuid()
|
463
|
os.chown(tarball, uucp_uid, uucp_gid)
|
464
|
except OSError, e:
|
465
|
log.msg("/!\ chown error, check authorizations (%s)" % e)
|
466
|
|
467
|
|
468
|
try:
|
469
|
uucp_uid, uucp_gid = pwd.getpwnam('uucp')[2:4]
|
470
|
os.chown('/usr/share/zephir/deffered_logs', uucp_uid, uucp_gid)
|
471
|
except:
|
472
|
log.msg("/!\ chown error on deffered_logs")
|
473
|
os.system('/usr/share/zephir/scripts/zephir_client call &> /dev/null')
|
474
|
|
475
|
|
476
|
|
477
|
|
478
|
def xmlrpc_list_agents(self):
|
479
|
"""@return: Liste des agents chargés"""
|
480
|
return self.agents.keys()
|
481
|
xmlrpc_list_agents.signature = [['array']]
|
482
|
|
483
|
def xmlrpc_agents_menu(self):
|
484
|
"""@return: Liste des agents chargés et structure d'affichage"""
|
485
|
try:
|
486
|
menu = {}
|
487
|
for name, agent in self.agents.items():
|
488
|
if agent.section != None:
|
489
|
if not menu.has_key(agent.section):
|
490
|
menu[agent.section] = []
|
491
|
menu[agent.section].append((name, agent.description))
|
492
|
return menu
|
493
|
except Exception, e:
|
494
|
log.msg(e)
|
495
|
xmlrpc_agents_menu.signature = [['struct']]
|
496
|
|
497
|
def xmlrpc_status_for_agents(self, agent_name_list = []):
|
498
|
"""
|
499
|
@return: Les statuts des agents listés dans un dictionnaire
|
500
|
C{{nom:status}}. Le status est lui-même un dictionnaire avec
|
501
|
pour clés C{'level'} et C{'message'}. Seuls les noms d'agents
|
502
|
effectivement chargés apparaîtront parmi les clés du
|
503
|
dictionnaire.
|
504
|
"""
|
505
|
result = {}
|
506
|
if len(agent_name_list) == 0:
|
507
|
agent_name_list = self.agents.keys()
|
508
|
for agent_name in agent_name_list:
|
509
|
if self.agents.has_key(agent_name):
|
510
|
result[agent_name] = self.agents[agent_name].check_status().to_dict()
|
511
|
return result
|
512
|
xmlrpc_status_for_agents.signature = [['string', 'struct']]
|
513
|
|
514
|
def xmlrpc_reset_max_status_for_agents(self, agent_name_list=[]):
|
515
|
if len(agent_name_list) == 0:
|
516
|
agent_name_list = self.agents.keys()
|
517
|
for agent_name in agent_name_list:
|
518
|
if self.agents.has_key(agent_name):
|
519
|
self.agents[agent_name].reset_max_status()
|
520
|
return "ok"
|
521
|
|
522
|
def xmlrpc_archive_for_upload(self):
|
523
|
self.wakeup_for_upload(False)
|
524
|
return "ok"
|
525
|
|
526
|
|
527
|
class PublisherService(service.MultiService):
|
528
|
"""Serves the web interface for current agent data"""
|
529
|
|
530
|
def __init__(self, config, parent, root_resource,
|
531
|
live_agents=None,
|
532
|
show_clients_page=True):
|
533
|
"""config should be complete"""
|
534
|
service.MultiService.__init__(self)
|
535
|
self.config = config
|
536
|
self.show_clients_page = show_clients_page
|
537
|
self.manager = ClientManager(self.config, live_agents)
|
538
|
|
539
|
self.setServiceParent(service.IServiceCollection(parent))
|
540
|
|
541
|
rsrc = ZephirServerResource(self.config, self.manager)
|
542
|
root_resource.putChild('agents', rsrc)
|
543
|
default_page = './agents/'
|
544
|
if not self.show_clients_page:
|
545
|
default_page += self.config['host_ref'] + '/'
|
546
|
root_resource.putChild('', util.Redirect(default_page))
|
547
|
|
548
|
|
549
|
|
550
|
|
551
|
|
552
|
|
553
|
|