Projet

Général

Profil

lxml_parser.py

Joël Cuissinat, 03/01/2012 16:29

Télécharger (14,3 ko)

 
1
# -*- coding: UTF-8 -*-
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
#from creole.error import *
9
from creole.error import ConfigError
10
from creole.utils import string_to_bool #, get_text_node
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
        #document = xml.dom.minidom.parse(filename)
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
    # FIXME !!!
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.find('variables'))
49

    
50
    # balise <files> (données sur le maître)
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
    # balise <containers> (données dans les conteneurs)
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
    # gestion des contraintes
69
    constraint_node = root_node.findall('constraints')
70
    if constraint_node != []:
71
        constraints = parse_constraints(constraint_node[0])
72
    else:
73
        constraints = {'checks' : {},
74
                       'fills' : {},
75
                       'conditions' : {},
76
                       'auto' : {}
77
                      }
78

    
79
    # gestion des groupes de variables
80
    group_node = root_node.findall('constraints')
81
    if group_node != []:
82
        groups = parse_groups(group_node[0])
83
    else:
84
        groups = {}
85

    
86
    # gestion de l'aide
87
    help_node = root_node.findall('help')
88
    if help_node != []:
89
        helps = parse_help(help_node[0])
90
    else:
91
        helps = {'variables':{},
92
                 'families':{}
93
                }
94

    
95
    # gestion des séparateurs
96
    sep_node = root_node.findall('separators')
97
    if sep_node != []:
98
        separators = parse_separators(sep_node[0])
99
    else:
100
        separators = {}
101
    return softwares, variables, families, order, constraints, groups, helps, separators
102

    
103

    
104
def _get_boolean_attr(node, attr_name):
105
    """
106
    Gestion spécifique pour les attributs booléens
107
    Ils sont à False par défaut
108
    """
109
    val = node.get(attr_name)
110
    return str(val).lower() == 'true'
111

    
112
def _get_optionnal(node, attr_name):
113
    """
114
    Valeur d'un attribut optionnel
115
    renvoie une chaine vide au lieu de None
116
    """
117
    val = node.get(attr_name)
118
    if val is None:
119
        val = ''
120
    return val
121

    
122
def _parse_value(varnode, attr='value'):
123
    """
124
    récupération des valeurs d'une variable
125
    """
126
    return [val.text for val in varnode.findall(attr)]
127

    
128

    
129
def parse_variables(var_node):
130
    """
131
    traitement des variables
132
    @param var_node: noeud <variables>
133
    """
134
    result = {}
135
    for var in var_node.getiterator('variable'):
136
        hidden = _get_boolean_attr(var, 'hidden')
137
        multi = _get_boolean_attr(var, 'multi')
138
        redefine = _get_boolean_attr(var, 'redefine')
139
        exists = _get_boolean_attr(var, 'exists')
140
        mode = var.get('mode')
141
        if mode != 'expert': mode = 'normal'
142
        name = var.attrib['name']
143
        value = _parse_value(var)
144
        typ = _get_optionnal(var, 'type')
145
        desc = _get_optionnal(var, 'description')
146
        result[name] = dict(value=value,
147
                            type=typ,
148
                            description=desc,
149
                            hidden=hidden,
150
                            multi=multi,
151
                            auto='',
152
                            redefine=redefine,
153
                            exists=exists,
154
                            mode=mode
155
                            )
156
    return result
157

    
158

    
159
def _get_vars_name(family_node, family_name, family_parse):
160
    """
161
    récupération de la liste des variables associées à une famille
162
    """
163
    varsname = []
164
    for var in family_node.getiterator('variable'):
165
        # FIXME : code qui ne sert pas ?
166
        #try:
167
        #    redefine = string_to_bool(var.getAttribute('redefine'))
168
        #except:
169
        #    redefine = False
170
        varsname.append(var.attrib['name'])
171
    return varsname
172

    
173

    
174
def parse_families(var_node):
175
    """
176
    traitement des familles
177
    @param var_node: noeud <variables>
178
    """
179
    result = {}
180
    order = []
181
    for family in var_node.getiterator('family'):
182
        family_name = family.attrib['name']
183
        order.append(family_name)
184
        hidden = _get_boolean_attr(family, 'hidden')
185
        mode = family.get('mode')
186
        if mode != 'expert': mode = 'normal'
187
        result[family_name] = {'hidden':hidden, 'mode':mode,
188
                               'vars':_get_vars_name(family,
189
                                                 family_name,
190
                                                 result)}
191
    return result, order
192

    
193

    
194
def parse_files(file_node):
195
    """
196
    Parsing des balises <file>
197
    @file_node : noeud xml
198
    """
199
    global file_level
200
    result = {}
201
    for fic in file_node.getiterator('file'):
202
        file_level += 1
203

    
204
        result[fic.attrib['name']] = {'hidden':False,
205
                                      'source': _get_optionnal(fic,'source'),
206
                                      'mode': _get_optionnal(fic, 'mode'),
207
                                      'owner': _get_optionnal(fic, 'owner'),
208
                                      'group': _get_optionnal(fic, 'group'),
209
                                      'mkdir': _get_boolean_attr(fic, 'mkdir'),
210
                                      'filelist': _get_optionnal(fic, 'filelist'),
211
                                      'rm': _get_boolean_attr(fic, 'rm'),
212
                                      'del_comment': _get_optionnal(fic, 'del_comment'),
213
                                      'container_only': _get_boolean_attr(fic, 'container_only'),
214
                                      'level': file_level
215
                                      }
216
    return result
217

    
218

    
219
def parse_packages(package_node):
220
    """
221
    Traitement de balises <package>
222
    """
223
    return _parse_value(package_node, 'package')
224

    
225

    
226
def parse_services(service_node):
227
    """
228
    Traitement des balises <service>
229
    """
230
    global service_level
231
    result = {}
232
    for serv in service_node.getiterator('service'):
233
        service_level += 1
234
        method = serv.get('method')
235
        if not method:
236
            method = 'service'
237
        # FIXME : cas inverse de d'habitude !
238
        try:
239
            pty = string_to_bool(serv.get('pty'))
240
        except (TypeError, ValueError):
241
            pty = True
242
        # FIXME : cas encore différent !
243
        try:
244
            #True : seulement en mode conteneur
245
            #False : seulement en mode non conteneur
246
            #None : dans les deux modes
247
            in_container = string_to_bool(serv.get('in_container'))
248
        except (TypeError, ValueError):
249
            in_container = None
250
        #result[str(get_text_node(serv))] = {'servicelist':serv.getAttribute('servicelist'),
251
        result[serv.text] = {'servicelist': _get_optionnal(serv, 'servicelist'),
252
                             'startlevel': _get_optionnal(serv, 'startlevel'),
253
                             'stoplevel': _get_optionnal(serv, 'stoplevel'),
254
                             'method': method,
255
                             'pty': pty,
256
                             'level': service_level,
257
                             'in_container': in_container}
258
    return result
259

    
260

    
261
def parse_disknod(disknod_node):
262
    """
263
    Traitement de balises <disknod>
264
    """
265
    return _parse_value(disknod_node, 'disknod')
266

    
267

    
268
def parse_interfaces(interface_node):
269
    """
270
    Traitement des balises <interface>
271
    """
272
    result = {}
273
    for interface in interface_node.getiterator('interface'):
274
        method = interface.get('method')
275
        if not method:
276
            method = 'macvlan'
277
        if method not in ['macvlan', 'bridge']:
278
            raise Exception('method pour une interface doit être macvlan ou bridge et pas {0}'.format(method))
279
        result[interface.text] = {'interfacelist': _get_optionnal(interface, 'interfacelist'),
280
                                  'linkto': _get_optionnal(interface, 'linkto'),
281
                                  'ip': _get_optionnal(interface, 'ip'),
282
                                  'mask': _get_optionnal(interface, 'mask'),
283
                                  'bcast': _get_optionnal(interface, 'bcast'),
284
                                  'method': method
285
                                 }
286
    return result
287

    
288

    
289
def parse_containers(containers_node):
290
    result = {}
291
    for container_node in containers_node.getiterator('container'):
292
        name = container_node.attrib['name']
293
        containerid = _get_optionnal(container_node, 'id')
294
        groupid = _get_optionnal(container_node, 'group')
295
        if name == VIRTMASTER:
296
            raise Exception("Le nom '%s' est interdit pour une balise <container>" % VIRTMASTER)
297
        if name in result:
298
            raise Exception("Le nom %s doit etre unique par dictionnaire" % name)
299

    
300
        file_nodes = parse_files(container_node)
301
        packages = parse_packages(container_node)
302
        services = parse_services(container_node)
303
        interfaces = parse_interfaces(container_node)
304
        disknods = parse_disknod(container_node)
305
        result[name] = [file_nodes, packages, services, interfaces, containerid, groupid, disknods]
306
    return result
307

    
308

    
309
def parse_constraints(node):
310
    """
311
    @param node: node des contraintes
312
    """
313
    constraints = {'checks' : parse_funcs(node,'check'),
314
                   'fills' : parse_funcs(node,'fill'),
315
                   'auto' : parse_funcs(node,'auto'),
316
                   'conditions' : parse_conditions(node)
317
                  }
318
    return constraints
319

    
320

    
321
def _parse_param(param_node):
322
    return {'name'  : _get_optionnal(param_node, 'name'),
323
            'type'  : _get_optionnal(param_node, 'type'),
324
            'value' : param_node.text,
325
            'optional' : _get_optionnal(param_node, 'optional'),
326
            'hidden' : _get_optionnal(param_node, 'hidden'),
327
            }
328

    
329

    
330
def parse_funcs(node, func_type):
331
    """
332
    @param node: node des fonctions
333
    @param func_type: TagName of the functions to find
334
    """
335
    # fonctions de vérification
336
    funcs = {}
337
    for func in node.getiterator(func_type):
338
        # lecture des paramètres
339
        # XXX (adim): est-ce que le fait que params ne soit pas réinitialisé
340
        #             à chaque tour de boucle "for target in targets" est un bug ?
341
        params = []
342
        targets = _parse_value(func, 'target') # [ get_text_node(t) for t in func.getElementsByTagName('target') ]
343
        if not targets:
344
            targets = [_get_optionnal(func, 'target')]
345
        for target in targets:
346
            if target is not None:
347
                for param in func.getiterator('param'):
348
                    params.append(_parse_param(param))
349
                funcs.setdefault(target, []).append([func.attrib['name'],
350
                                                     params])
351
    return funcs
352

    
353

    
354
def parse_conditions(node):
355
    """
356
    @param node: node des fonctions
357
    """
358
    # fonctions de vérification
359
    funcs = {}
360
    for func in node.getiterator('condition'):
361
        # lecture des paramètres
362
        targets = []
363
        family_targets = []
364
        file_targets = []
365
        filelist_targets = []
366
        servicelist_targets = []
367
        interfacelist_targets = []
368
        # paramètres de la fonction
369
        params = [_parse_param(param)
370
                  for param in func.getiterator('param')]
371
        # cibles de la dépendance
372
        for target in func.getiterator('target'):
373
            ttype = target.get('type')
374
            if ttype == 'family':
375
                family_targets.append(target.text)
376
            elif ttype == 'file':
377
                file_targets.append(target.text)
378
            elif ttype == 'filelist':
379
                filelist_targets.append(target.text)
380
            elif ttype == 'servicelist':
381
                servicelist_targets.append(target.text)
382
            elif ttype == 'interfacelist':
383
                interfacelist_targets.append(target.text)
384
            else:
385
                # sinon, c'est une variable EOLE
386
                targets.append(target.text)
387
        funcdef = [func.attrib['name'], family_targets, targets,
388
                   file_targets, filelist_targets, servicelist_targets, interfacelist_targets, params]
389
        funcs.setdefault(_get_optionnal(func, 'source'), []).append(funcdef)
390
    return funcs
391

    
392

    
393
def parse_groups(node):
394
    """
395
    Traitement des groupes de variables
396
    """
397
    result = {}
398
    for group in node.getiterator('group'):
399
        slaves = _parse_value(group, 'slave')
400
        result[group.attrib['master']] = slaves
401
    return result
402

    
403

    
404
def parse_help(node):
405
    """
406
    Traitement de l'aide
407
    """
408
    var_help = dict( (var.attrib['name'], var.text.strip())
409
                     for var in node.getiterator('variable') )
410
    fam_help = dict( (var.attrib['name'], var.text.strip())
411
                     for var in node.getiterator('family') )
412
    return {'variables':var_help, 'families': fam_help}
413

    
414

    
415
def parse_separators(node):
416
    """dictionnaire des séparateurs, format {'variable':'text'}
417
    variable : nom de la première variable après le sépateur"""
418
    var_sep = {}
419
    for var in node.getiterator('separator'):
420
        var_sep[var.attrib['name']] = (var.text.strip(), _get_boolean_attr(var, 'never_hidden'))
421
    return var_sep
422

    
423