1
|
|
2
|
"""
|
3
|
Parseur LXML des fichiers XML de collecte des variables EOLE
|
4
|
"""
|
5
|
|
6
|
import xml.dom.minidom
|
7
|
from lxml import etree
|
8
|
|
9
|
from creole.error import ConfigError
|
10
|
from creole.utils import string_to_bool
|
11
|
from creole.config import VIRTMASTER
|
12
|
|
13
|
service_level = 0
|
14
|
file_level = 0
|
15
|
|
16
|
|
17
|
def parse_xml_file(filename):
|
18
|
"""
|
19
|
@param filename: nom du fichier xml source
|
20
|
@return: structure de données permettant de créer les objets Eole
|
21
|
"""
|
22
|
try:
|
23
|
document = etree.iterparse(filename, events=('end',), tag='creole')
|
24
|
|
25
|
return _parse_root_node(document)
|
26
|
except Exception, err:
|
27
|
import traceback
|
28
|
traceback.print_exc()
|
29
|
raise ConfigError, "Erreur lors du parsing du fichier %s : %s" % (filename, str(err))
|
30
|
|
31
|
def parse_string(string):
|
32
|
|
33
|
try:
|
34
|
document = xml.dom.minidom.parseString(string)
|
35
|
return _parse_root_node(document)
|
36
|
except Exception, err:
|
37
|
raise ConfigError, "Erreur lors du parsing : %s" % str(err)
|
38
|
|
39
|
|
40
|
def _parse_root_node(document):
|
41
|
"""
|
42
|
@param document: le document DOM contenant l'arborescence xml
|
43
|
"""
|
44
|
for _, first_node in document:
|
45
|
root_node = first_node
|
46
|
|
47
|
variables = parse_variables(root_node.find('variables'))
|
48
|
families, order = parse_families(root_node)
|
49
|
|
50
|
|
51
|
file_node = root_node.findall('files')
|
52
|
softwares = {}
|
53
|
if file_node != []:
|
54
|
if len(file_node) != 1:
|
55
|
raise Exception("Erreur plusieurs balises <files> par dictionnaire")
|
56
|
file_root_node = file_node[0]
|
57
|
files = parse_files(file_root_node)
|
58
|
file_packages = parse_packages(file_root_node)
|
59
|
file_services = parse_services(file_root_node)
|
60
|
softwares[VIRTMASTER] = [files, file_packages, file_services, {}, '1', '', '']
|
61
|
|
62
|
|
63
|
containers_node = root_node.findall('containers')
|
64
|
if containers_node != []:
|
65
|
for container in containers_node:
|
66
|
softwares.update(parse_containers(container))
|
67
|
|
68
|
|
69
|
constraints = parse_constraints(root_node)
|
70
|
|
71
|
|
72
|
groups = parse_groups(root_node)
|
73
|
|
74
|
|
75
|
helps = parse_help(root_node)
|
76
|
|
77
|
|
78
|
separators = parse_separators(root_node)
|
79
|
|
80
|
return softwares, variables, families, order, constraints, groups, helps, separators
|
81
|
|
82
|
|
83
|
def _get_boolean_attr(node, attr_name, default=False):
|
84
|
"""
|
85
|
Gestion spécifique pour les attributs booléens
|
86
|
Ils sont à False par défaut
|
87
|
"""
|
88
|
val = node.get(attr_name)
|
89
|
if default:
|
90
|
return str(val).lower() != 'false'
|
91
|
else:
|
92
|
return str(val).lower() == 'true'
|
93
|
|
94
|
def _get_optionnal(node, attr_name):
|
95
|
"""
|
96
|
Valeur d'un attribut optionnel
|
97
|
renvoie une chaine vide au lieu de None
|
98
|
"""
|
99
|
val = node.get(attr_name)
|
100
|
if val is None:
|
101
|
val = ''
|
102
|
return val
|
103
|
|
104
|
def _parse_value(varnode, attr='value'):
|
105
|
"""
|
106
|
récupération des valeurs d'une variable
|
107
|
"""
|
108
|
return [val.text for val in varnode.findall(attr)]
|
109
|
|
110
|
|
111
|
def parse_variables(var_node):
|
112
|
"""
|
113
|
traitement des variables
|
114
|
@param var_node: noeud <variables>
|
115
|
"""
|
116
|
result = {}
|
117
|
for var in var_node.getiterator('variable'):
|
118
|
hidden = _get_boolean_attr(var, 'hidden')
|
119
|
multi = _get_boolean_attr(var, 'multi')
|
120
|
redefine = _get_boolean_attr(var, 'redefine')
|
121
|
exists = _get_boolean_attr(var, 'exists', default=True)
|
122
|
|
123
|
mode = _get_optionnal(var, 'mode')
|
124
|
name = var.attrib['name']
|
125
|
value = _parse_value(var)
|
126
|
typ = _get_optionnal(var, 'type')
|
127
|
desc = _get_optionnal(var, 'description')
|
128
|
result[name] = dict(value=value,
|
129
|
type=typ,
|
130
|
description=desc,
|
131
|
hidden=hidden,
|
132
|
multi=multi,
|
133
|
auto='',
|
134
|
redefine=redefine,
|
135
|
exists=exists,
|
136
|
mode=mode
|
137
|
)
|
138
|
return result
|
139
|
|
140
|
|
141
|
def _get_vars_name(family_node, family_name, family_parse):
|
142
|
"""
|
143
|
récupération de la liste des variables associées à une famille
|
144
|
"""
|
145
|
varsname = []
|
146
|
for var in family_node.getiterator('variable'):
|
147
|
varsname.append(var.attrib['name'])
|
148
|
return varsname
|
149
|
|
150
|
|
151
|
def parse_families(var_node):
|
152
|
"""
|
153
|
traitement des familles
|
154
|
@param var_node: noeud <variables>
|
155
|
"""
|
156
|
result = {}
|
157
|
order = []
|
158
|
for family in var_node.findall('variables/family'):
|
159
|
family_name = family.attrib['name']
|
160
|
order.append(family_name)
|
161
|
hidden = _get_boolean_attr(family, 'hidden')
|
162
|
|
163
|
mode = _get_optionnal(family, 'mode')
|
164
|
result[family_name] = {'hidden':hidden, 'mode':mode,
|
165
|
'vars':_get_vars_name(family,
|
166
|
family_name,
|
167
|
result)}
|
168
|
return result, order
|
169
|
|
170
|
|
171
|
def parse_files(file_node):
|
172
|
"""
|
173
|
Parsing des balises <file>
|
174
|
@file_node : noeud xml
|
175
|
"""
|
176
|
global file_level
|
177
|
result = {}
|
178
|
for fic in file_node.getiterator('file'):
|
179
|
file_level += 1
|
180
|
|
181
|
result[fic.attrib['name']] = {'hidden':False,
|
182
|
'source': _get_optionnal(fic,'source'),
|
183
|
'mode': _get_optionnal(fic, 'mode'),
|
184
|
'owner': _get_optionnal(fic, 'owner'),
|
185
|
'group': _get_optionnal(fic, 'group'),
|
186
|
'mkdir': _get_boolean_attr(fic, 'mkdir'),
|
187
|
'filelist': _get_optionnal(fic, 'filelist'),
|
188
|
'rm': _get_boolean_attr(fic, 'rm'),
|
189
|
'del_comment': _get_optionnal(fic, 'del_comment'),
|
190
|
'container_only': _get_boolean_attr(fic, 'container_only'),
|
191
|
'level': file_level
|
192
|
}
|
193
|
return result
|
194
|
|
195
|
|
196
|
def parse_packages(package_node):
|
197
|
"""
|
198
|
Traitement de balises <package>
|
199
|
"""
|
200
|
return _parse_value(package_node, 'package')
|
201
|
|
202
|
|
203
|
def parse_services(service_node):
|
204
|
"""
|
205
|
Traitement des balises <service>
|
206
|
"""
|
207
|
global service_level
|
208
|
result = {}
|
209
|
for serv in service_node.getiterator('service'):
|
210
|
service_level += 1
|
211
|
method = serv.get('method')
|
212
|
if not method:
|
213
|
method = 'service'
|
214
|
|
215
|
try:
|
216
|
pty = string_to_bool(serv.get('pty'))
|
217
|
except (TypeError, ValueError):
|
218
|
pty = True
|
219
|
|
220
|
try:
|
221
|
|
222
|
|
223
|
|
224
|
in_container = string_to_bool(serv.get('in_container'))
|
225
|
except (TypeError, ValueError):
|
226
|
in_container = None
|
227
|
|
228
|
result[serv.text] = {'servicelist': _get_optionnal(serv, 'servicelist'),
|
229
|
'startlevel': _get_optionnal(serv, 'startlevel'),
|
230
|
'stoplevel': _get_optionnal(serv, 'stoplevel'),
|
231
|
'method': method,
|
232
|
'pty': pty,
|
233
|
'level': service_level,
|
234
|
'in_container': in_container}
|
235
|
return result
|
236
|
|
237
|
|
238
|
def parse_disknod(disknod_node):
|
239
|
"""
|
240
|
Traitement de balises <disknod>
|
241
|
"""
|
242
|
return _parse_value(disknod_node, 'disknod')
|
243
|
|
244
|
|
245
|
def parse_interfaces(interface_node):
|
246
|
"""
|
247
|
Traitement des balises <interface>
|
248
|
"""
|
249
|
result = {}
|
250
|
for interface in interface_node.getiterator('interface'):
|
251
|
method = interface.get('method')
|
252
|
if not method:
|
253
|
method = 'macvlan'
|
254
|
if method not in ['macvlan', 'bridge']:
|
255
|
raise Exception('method pour une interface doit être macvlan ou bridge et pas {0}'.format(method))
|
256
|
result[interface.text] = {'interfacelist': _get_optionnal(interface, 'interfacelist'),
|
257
|
'linkto': _get_optionnal(interface, 'linkto'),
|
258
|
'ip': _get_optionnal(interface, 'ip'),
|
259
|
'mask': _get_optionnal(interface, 'mask'),
|
260
|
'bcast': _get_optionnal(interface, 'bcast'),
|
261
|
'method': method
|
262
|
}
|
263
|
return result
|
264
|
|
265
|
|
266
|
def parse_containers(containers_node):
|
267
|
result = {}
|
268
|
for container_node in containers_node.getiterator('container'):
|
269
|
name = container_node.attrib['name']
|
270
|
containerid = _get_optionnal(container_node, 'id')
|
271
|
groupid = _get_optionnal(container_node, 'group')
|
272
|
if name == VIRTMASTER:
|
273
|
raise Exception("Le nom '%s' est interdit pour une balise <container>" % VIRTMASTER)
|
274
|
if name in result:
|
275
|
raise Exception("Le nom %s doit etre unique par dictionnaire" % name)
|
276
|
|
277
|
file_nodes = parse_files(container_node)
|
278
|
packages = parse_packages(container_node)
|
279
|
services = parse_services(container_node)
|
280
|
interfaces = parse_interfaces(container_node)
|
281
|
disknods = parse_disknod(container_node)
|
282
|
result[name] = [file_nodes, packages, services, interfaces, containerid, groupid, disknods]
|
283
|
return result
|
284
|
|
285
|
|
286
|
def parse_constraints(node):
|
287
|
"""
|
288
|
@param node: node des contraintes
|
289
|
"""
|
290
|
constraints = {'checks' : parse_funcs(node,'check'),
|
291
|
'fills' : parse_funcs(node,'fill'),
|
292
|
'auto' : parse_funcs(node,'auto'),
|
293
|
'conditions' : parse_conditions(node)
|
294
|
}
|
295
|
return constraints
|
296
|
|
297
|
|
298
|
def _parse_param(param_node):
|
299
|
return {'name' : _get_optionnal(param_node, 'name'),
|
300
|
'type' : _get_optionnal(param_node, 'type'),
|
301
|
'value' : param_node.text,
|
302
|
'optional' : _get_optionnal(param_node, 'optional'),
|
303
|
'hidden' : _get_optionnal(param_node, 'hidden'),
|
304
|
}
|
305
|
|
306
|
|
307
|
def parse_funcs(node, func_type):
|
308
|
"""
|
309
|
@param node: node des fonctions
|
310
|
@param func_type: TagName of the functions to find
|
311
|
"""
|
312
|
|
313
|
funcs = {}
|
314
|
|
315
|
for func in node.findall('constraints/%s' % func_type):
|
316
|
|
317
|
|
318
|
|
319
|
params = []
|
320
|
targets = _parse_value(func, 'target')
|
321
|
if not targets:
|
322
|
targets = [_get_optionnal(func, 'target')]
|
323
|
for target in targets:
|
324
|
if target is not None:
|
325
|
for param in func.getiterator('param'):
|
326
|
params.append(_parse_param(param))
|
327
|
funcs.setdefault(target, []).append([func.attrib['name'],
|
328
|
params])
|
329
|
return funcs
|
330
|
|
331
|
|
332
|
def parse_conditions(node):
|
333
|
"""
|
334
|
@param node: node des fonctions
|
335
|
"""
|
336
|
|
337
|
funcs = {}
|
338
|
for func in node.getiterator('condition'):
|
339
|
|
340
|
targets = []
|
341
|
family_targets = []
|
342
|
file_targets = []
|
343
|
filelist_targets = []
|
344
|
servicelist_targets = []
|
345
|
interfacelist_targets = []
|
346
|
|
347
|
params = [_parse_param(param)
|
348
|
for param in func.getiterator('param')]
|
349
|
|
350
|
for target in func.getiterator('target'):
|
351
|
ttype = target.get('type')
|
352
|
if ttype == 'family':
|
353
|
family_targets.append(target.text)
|
354
|
elif ttype == 'file':
|
355
|
file_targets.append(target.text)
|
356
|
elif ttype == 'filelist':
|
357
|
filelist_targets.append(target.text)
|
358
|
elif ttype == 'servicelist':
|
359
|
servicelist_targets.append(target.text)
|
360
|
elif ttype == 'interfacelist':
|
361
|
interfacelist_targets.append(target.text)
|
362
|
else:
|
363
|
|
364
|
targets.append(target.text)
|
365
|
funcdef = [func.attrib['name'], family_targets, targets,
|
366
|
file_targets, filelist_targets, servicelist_targets, interfacelist_targets, params]
|
367
|
funcs.setdefault(_get_optionnal(func, 'source'), []).append(funcdef)
|
368
|
return funcs
|
369
|
|
370
|
|
371
|
def parse_groups(node):
|
372
|
"""
|
373
|
Traitement des groupes de variables
|
374
|
"""
|
375
|
result = {}
|
376
|
for group in node.findall('constraints/group'):
|
377
|
slaves = _parse_value(group, 'slave')
|
378
|
result[group.attrib['master']] = slaves
|
379
|
return result
|
380
|
|
381
|
|
382
|
def parse_help(node):
|
383
|
"""
|
384
|
Traitement de l'aide
|
385
|
"""
|
386
|
var_help = dict( (var.attrib['name'], var.text.strip())
|
387
|
for var in node.findall('help/variable') )
|
388
|
fam_help = dict( (var.attrib['name'], var.text.strip())
|
389
|
for var in node.findall('help/family') )
|
390
|
return {'variables':var_help, 'families': fam_help}
|
391
|
|
392
|
|
393
|
def parse_separators(node):
|
394
|
"""dictionnaire des séparateurs, format {'variable':'text'}
|
395
|
variable : nom de la première variable après le sépateur"""
|
396
|
var_sep = {}
|
397
|
for var in node.findall('separators/separator'):
|
398
|
var_sep[var.attrib['name']] = (var.text.strip(), _get_boolean_attr(var, 'never_hidden'))
|
399
|
return var_sep
|
400
|
|