~ubuntu-branches/ubuntu/vivid/crmsh/vivid-proposed

« back to all changes in this revision

Viewing changes to modules/parse.py

  • Committer: Package Import Robot
  • Author(s): Martin Loschwitz
  • Date: 2013-08-02 07:37:50 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130802073750-wq5kjrh5w61eu0xf
Tags: 1.2.5+hg1006-1
New upstream checkout for Pacemaker 1.1.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import shlex
19
19
import re
20
 
import xml.dom.minidom
 
20
from lxml import etree
21
21
from utils import *
22
22
from vars import Vars
23
23
from msg import *
33
33
    '''
34
34
    Parse the resource type.
35
35
    '''
36
 
    ra_class,provider,rsc_type = disambiguate_ra_type(s)
37
 
    if not ra_type_validate(s,ra_class,provider,rsc_type):
 
36
    ra_class, provider, rsc_type = disambiguate_ra_type(s)
 
37
    if not ra_type_validate(s, ra_class, provider, rsc_type):
38
38
        return None
39
 
    pl.append(["class",ra_class])
 
39
    pl.append(["class", ra_class])
40
40
    if ra_class == "ocf":
41
 
        pl.append(["provider",provider])
42
 
    pl.append(["type",rsc_type])
43
 
def is_attribute(p,a):
 
41
        pl.append(["provider", provider])
 
42
    pl.append(["type", rsc_type])
 
43
def is_attribute(p, a):
44
44
    return p.startswith(a + '=')
45
 
def cli_parse_attr_strict(s,pl):
 
45
def cli_parse_attr_strict(s, pl):
46
46
    '''
47
47
    Parse attributes in the 'p=v' form.
48
48
    '''
49
49
    if s and '=' in s[0]:
50
 
        n,v = s[0].split('=',1)
 
50
        n, v = s[0].split('=', 1)
51
51
        if not n:
52
52
            return
53
 
        pl.append([n,v])
54
 
        cli_parse_attr_strict(s[1:],pl)
55
 
def cli_parse_attr(s,pl):
 
53
        pl.append([n, v])
 
54
        cli_parse_attr_strict(s[1:], pl)
 
55
def cli_parse_attr(s, pl):
56
56
    '''
57
57
    Parse attributes in the 'p=v' form.
58
58
    Allow also the 'p' form (no value) unless p is one of the
59
59
    attr_list_keyw words.
60
60
    '''
61
 
    attr_lists_keyw = olist(["params","meta","utilization","operations","op","attributes"])
 
61
    attr_lists_keyw = olist(["params", "meta", "utilization", "operations", "op", "attributes"])
62
62
    if s:
63
63
        if s[0] in attr_lists_keyw:
64
64
            return
65
65
        if '=' in s[0]:
66
 
            n,v = s[0].split('=',1)
 
66
            n, v = s[0].split('=', 1)
67
67
        else:
68
68
            n = s[0]; v = None
69
69
        if not n:
70
70
            return
71
 
        pl.append([n,v])
72
 
        cli_parse_attr(s[1:],pl)
73
 
def is_only_id(pl,keyw):
 
71
        pl.append([n, v])
 
72
        cli_parse_attr(s[1:], pl)
 
73
def is_only_id(pl, keyw):
74
74
    if len(pl) > 1:
75
75
        common_err("%s: only single $id or $id-ref attribute is allowed" % keyw)
76
76
        return False
78
78
        common_err("%s: only single $id or $id-ref attribute is allowed" % keyw)
79
79
        return False
80
80
    return True
81
 
def check_operation(pl):
82
 
    op_name = find_value(pl,"name")
83
 
    if not op_name in olist(vars.op_cli_names):
84
 
        common_warn("%s: operation not recognized" % op_name)
85
 
    if op_name == "monitor" and not find_value(pl,"interval"):
86
 
        common_err("monitor requires interval")
87
 
        return False
88
 
    return True
89
81
def parse_resource(s):
90
82
    el_type = s[0].lower()
91
83
    if el_type == "master": # ugly kludge :(
96
88
    cli_list = []
97
89
    # the head
98
90
    head = []
99
 
    head.append(["id",s[1]])
 
91
    head.append(["id", s[1]])
100
92
    i = 3
101
93
    if el_type in ("primitive", "rsc_template"):
102
94
        if el_type == "primitive" and s[2].startswith('@'):
103
 
            head.append(["template",s[2][1:]])
 
95
            head.append(["template", s[2][1:]])
104
96
        else:
105
 
            cli_parse_rsctype(s[2],head)
 
97
            cli_parse_rsctype(s[2], head)
106
98
            if not find_value(head,"type"):
107
99
                syntax_err(s[2:], context = el_type)
108
100
                return False
118
110
                else:
119
111
                    cl.append(s[i])
120
112
                    i += 1 # skip to the next token
121
 
        head.append(["$children",cl])
 
113
        head.append(["$children", cl])
122
114
    try:  # s[i] may be out of range
123
115
        if is_attribute(s[i],"description"):
124
 
            cli_parse_attr(s[i:i+1],head)
 
116
            cli_parse_attr(s[i:i+1], head)
125
117
            i += 1 # skip to the next token
126
118
    except: pass
127
 
    cli_list.append([el_type,head])
 
119
    cli_list.append([el_type, head])
128
120
    # the rest
129
121
    state = 0 # 1: reading operations; 2: operations read
130
122
    while len(s) > i+1:
138
130
        elif el_type in ("primitive", "rsc_template") and state <= 1 and keyword_cmp(keyw, "op"):
139
131
            if state == 0:
140
132
                state = 1
141
 
            pl.append(["name",s[i+1]])
 
133
            pl.append(["name", s[i+1]])
142
134
        else:
143
135
            syntax_err(s[i:], context = el_type)
144
136
            return False
145
137
        if keyword_cmp(keyw, "op"):
146
138
            if len(s) > i+2:
147
 
                cli_parse_attr(s[i+2:],pl)
148
 
            if not check_operation(pl):
149
 
                return False
 
139
                cli_parse_attr(s[i+2:], pl)
150
140
        else:
151
 
            cli_parse_attr(s[i+1:],pl)
 
141
            cli_parse_attr(s[i+1:], pl)
152
142
            if len(pl) == 0:
153
143
                syntax_err(s[i:], context = el_type)
154
144
                return False
155
 
        if keyword_cmp(keyw, "operations") and not is_only_id(pl,keyw):
 
145
        if keyword_cmp(keyw, "operations") and not is_only_id(pl, keyw):
156
146
            return False
157
147
        i += len(pl)+1
158
148
        # interval is obligatory for ops, supply 0 if not there
159
149
        if keyword_cmp(keyw, "op") and not find_value(pl,"interval"):
160
150
            pl.append(["interval","0"])
161
 
        cli_list.append([keyw,pl])
 
151
        cli_list.append([keyw, pl])
162
152
    if len(s) > i:
163
153
        syntax_err(s[i:], context = el_type)
164
154
        return False
170
160
    cli_list = []
171
161
    head_pl = []
172
162
    # this is an op
173
 
    cli_list.append(["op",head_pl])
174
 
    if not cli_parse_rsc_role(s[1],head_pl):
 
163
    cli_list.append(["op", head_pl])
 
164
    if not cli_parse_rsc_role(s[1], head_pl):
175
165
        return False
176
 
    if not cli_parse_op_times(s[2],head_pl):
 
166
    if not cli_parse_op_times(s[2], head_pl):
177
167
        return False
178
168
    # rename rsc-role to role
179
169
    for i in range(len(head_pl)):
181
171
            head_pl[i][0] = "role"
182
172
            break
183
173
    # add the operation name
184
 
    head_pl.append(["name",s[0]])
 
174
    head_pl.append(["name", s[0]])
185
175
    return cli_list
186
176
 
187
 
def cli_parse_ticket(ticket,pl):
 
177
def cli_parse_ticket(ticket, pl):
188
178
    if ticket.endswith(':'):
189
179
        ticket = ticket.rstrip(':')
190
180
    else:
191
181
        syntax_err(ticket, context = 'rsc_ticket')
192
182
        return False
193
 
    pl.append(["ticket",ticket])
 
183
    pl.append(["ticket", ticket])
194
184
    return True
195
185
 
196
 
def cli_parse_score(score,pl,noattr = False):
 
186
def cli_parse_score(score, pl, noattr=False):
197
187
    if score.endswith(':'):
198
188
        score = score.rstrip(':')
199
189
    else:
200
190
        syntax_err(score, context = 'score')
201
191
        return False
202
192
    if score in vars.score_types:
203
 
        pl.append(["score",vars.score_types[score]])
204
 
    elif re.match("^[+-]?(inf(inity)?|INF(INITY)?|[0-9]+)$",score):
 
193
        pl.append(["score", vars.score_types[score]])
 
194
    elif re.match("^[+-]?(inf(inity)?|INF(INITY)?|[0-9]+)$", score):
205
195
        score = re.sub("inf(inity)?|INF(INITY)?", "INFINITY", score)
206
 
        pl.append(["score",score])
 
196
        pl.append(["score", score])
207
197
    # orders have the special kind attribute
208
198
    elif noattr and can_cannonize(score, rng_attr_values('rsc_order', 'kind')):
209
199
        pl.append(["kind", cannonize(score, rng_attr_values('rsc_order', 'kind'))])
212
202
            common_err("attribute not allowed for score in orders")
213
203
            return False
214
204
        else:
215
 
            pl.append(["score-attribute",score])
 
205
            pl.append(["score-attribute", score])
216
206
    return True
217
207
 
218
208
def is_binary_op(s):
224
214
        return l[0] in olist(vars.binary_ops)
225
215
    else:
226
216
        return False
227
 
def cli_parse_binary_op(s,pl):
 
217
def cli_parse_binary_op(s, pl):
228
218
    l = s.split(':')
229
219
    if len(l) == 2:
230
 
        pl.append(["type",l[0]])
231
 
        pl.append(["operation",l[1]])
 
220
        pl.append(["type", l[0]])
 
221
        pl.append(["operation", l[1]])
232
222
    else:
233
 
        pl.append(["operation",l[0]])
234
 
def cli_parse_expression(s,pl):
 
223
        pl.append(["operation", l[0]])
 
224
def cli_parse_expression(s, pl):
235
225
    if len(s) > 1 and s[0] in olist(vars.unary_ops):
236
 
        pl.append(["operation",s[0]])
237
 
        pl.append(["attribute",s[1]])
 
226
        pl.append(["operation", s[0]])
 
227
        pl.append(["attribute", s[1]])
238
228
    elif len(s) > 2 and is_binary_op(s[1]):
239
 
        pl.append(["attribute",s[0]])
240
 
        cli_parse_binary_op(s[1],pl)
241
 
        pl.append(["value",s[2]])
 
229
        pl.append(["attribute", s[0]])
 
230
        cli_parse_binary_op(s[1], pl)
 
231
        pl.append(["value", s[2]])
242
232
    else:
243
233
        return False
244
234
    return True
245
 
def cli_parse_dateexpr(s,pl):
 
235
def cli_parse_dateexpr(s, pl):
246
236
    if len(s) < 3:
247
237
        return False
248
238
    if s[1] not in olist(rng_attr_values_l('date_expression', 'operation')):
249
239
        return False
250
 
    pl.append(["operation",s[1]])
 
240
    pl.append(["operation", s[1]])
251
241
    if s[1] in olist(vars.simple_date_ops):
252
 
        pl.append([keyword_cmp(s[1], 'lt') and "end" or "start",s[2]])
 
242
        pl.append([keyword_cmp(s[1], 'lt') and "end" or "start", s[2]])
253
243
        return True
254
 
    cli_parse_attr_strict(s[2:],pl)
 
244
    cli_parse_attr_strict(s[2:], pl)
255
245
    return True
256
246
def parse_rule(s):
257
247
    if not keyword_cmp(s[0], "rule"):
258
 
        syntax_err(s,context = "rule")
259
 
        return 0,None
 
248
        syntax_err(s, context = "rule")
 
249
        return 0, None
260
250
    rule_list = []
261
251
    head_pl = []
262
 
    rule_list.append([s[0].lower(),head_pl])
 
252
    rule_list.append([s[0].lower(), head_pl])
263
253
    i = 1
264
 
    cli_parse_attr_strict(s[i:],head_pl)
 
254
    cli_parse_attr_strict(s[i:], head_pl)
265
255
    i += len(head_pl)
266
256
    if find_value(head_pl,"$id-ref"):
267
 
        return i,rule_list
268
 
    if not cli_parse_score(s[i],head_pl):
269
 
        return i,None
 
257
        return i, rule_list
 
258
    if not cli_parse_score(s[i], head_pl):
 
259
        return i, None
270
260
    i += 1
271
261
    bool_op = ''
272
262
    while len(s) > i+1:
277
267
        else:
278
268
            fun = cli_parse_expression
279
269
            elem = "expression"
280
 
        if not fun(s[i:],pl):
281
 
            syntax_err(s[i:],context = "rule")
282
 
            return i,None
283
 
        rule_list.append([elem,pl])
 
270
        if not fun(s[i:], pl):
 
271
            syntax_err(s[i:], context = "rule")
 
272
            return i, None
 
273
        rule_list.append([elem, pl])
284
274
        i += len(pl)
285
275
        if find_value(pl, "type"):
286
276
            i -= 1 # reduce no of tokens by one if there was "type:op"
289
279
        if len(s) > i and s[i] in olist(vars.boolean_ops):
290
280
            if bool_op and not keyword_cmp(bool_op, s[i]):
291
281
                common_err("rule contains different bool operations: %s" % ' '.join(s))
292
 
                return i,None
 
282
                return i, None
293
283
            else:
294
284
                bool_op = s[i].lower()
295
285
                i += 1
296
286
        if len(s) > i and keyword_cmp(s[i], "rule"):
297
287
            break
298
288
    if bool_op and not keyword_cmp(bool_op, 'and'):
299
 
        head_pl.append(["boolean-op",bool_op])
300
 
    return i,rule_list
 
289
        head_pl.append(["boolean-op", bool_op])
 
290
    return i, rule_list
301
291
def parse_location(s):
302
292
    cli_list = []
303
293
    head_pl = []
304
 
    head_pl.append(["id",s[1]])
305
 
    head_pl.append(["rsc",s[2]])
306
 
    cli_list.append([s[0].lower(),head_pl])
 
294
    head_pl.append(["id", s[1]])
 
295
    head_pl.append(["rsc", s[2]])
 
296
    cli_list.append([s[0].lower(), head_pl])
307
297
    if len(s) == 5 and not keyword_cmp(s[3], "rule"): # the short node preference form
308
 
        if not cli_parse_score(s[3],head_pl):
 
298
        if not cli_parse_score(s[3], head_pl):
309
299
            return False
310
 
        head_pl.append(["node",s[4]])
 
300
        head_pl.append(["node", s[4]])
311
301
        return cli_list
312
302
    i = 3
313
303
    while i < len(s):
314
 
        numtoks,l = parse_rule(s[i:])
 
304
        numtoks, l = parse_rule(s[i:])
315
305
        if not l:
316
306
            return False
317
307
        cli_list += l
318
308
        i += numtoks
319
309
    if len(s) < i:
320
 
        syntax_err(s[i:],context = "location")
 
310
        syntax_err(s[i:], context = "location")
321
311
        return False
322
312
    return cli_list
323
313
 
325
315
    if not p:
326
316
        return True
327
317
    pl1 = []
328
 
    cli_parse_attr([p],pl1)
 
318
    cli_parse_attr([p], pl1)
329
319
    if len(pl1) != 1 or not find_value(pl1, attr):
330
 
        syntax_err(p,context = type)
 
320
        syntax_err(p, context = type)
331
321
        return False
332
322
    pl += pl1
333
323
    return True
334
 
def cli_parse_rsc_role(s,pl,attr_pfx = ''):
 
324
def cli_parse_rsc_role(s, pl, attr_pfx=''):
335
325
    l = s.split(':')
336
 
    pl.append([attr_pfx+"rsc",l[0]])
 
326
    pl.append([attr_pfx+"rsc", l[0]])
337
327
    if len(l) == 2:
338
328
        if l[1] in rng_attr_values('resource_set', 'role'):
339
 
            pl.append([attr_pfx+"rsc-role",l[1]])
 
329
            pl.append([attr_pfx+"rsc-role", l[1]])
340
330
        elif l[1].isdigit():
341
 
            pl.append([attr_pfx+"rsc-instance",l[1]])
 
331
            pl.append([attr_pfx+"rsc-instance", l[1]])
342
332
        else:
343
 
            bad_def_err("resource role/instance",s)
 
333
            bad_def_err("resource role/instance", s)
344
334
            return False
345
335
    elif len(l) > 2:
346
 
        bad_def_err("resource role/instance",s)
 
336
        bad_def_err("resource role/instance", s)
347
337
        return False
348
338
    return True
349
 
def cli_parse_op_times(s,pl):
 
339
def cli_parse_op_times(s, pl):
350
340
    l = s.split(':')
351
 
    pl.append(["interval",l[0]])
 
341
    pl.append(["interval", l[0]])
352
342
    if len(l) == 2:
353
 
        pl.append(["timeout",l[1]])
 
343
        pl.append(["timeout", l[1]])
354
344
    elif len(l) > 2:
355
 
        bad_def_err("op times",s)
 
345
        bad_def_err("op times", s)
356
346
        return False
357
347
    return True
358
348
 
362
352
    a ( b c:start ) d:Master e ...
363
353
    Appends one or more lists to cli_list.
364
354
    Lists are in form:
365
 
        list :: ["resource_set",set_pl]
366
 
        set_pl :: [["sequential","false"], ["action"|"role",action|role],
367
 
            ["resource_ref",["id",rsc]], ...]
 
355
        list :: ["resource_set", set_pl]
 
356
        set_pl :: [["sequential","false"], ["action"|"role", action|role],
 
357
            ["resource_ref", ["id", rsc]], ...]
368
358
        (the first two elements of set_pl are optional)
369
359
    Action/role change makes a new resource set.
370
360
    '''
374
364
        '[': ']',
375
365
        '(': ')',
376
366
    }
377
 
    def __init__(self,type,s,cli_list):
 
367
    def __init__(self, type, s, cli_list):
378
368
        self.type = type
379
369
        self.valid_q = (type == "order") and \
380
370
            rng_attr_values('resource_set', 'action') or \
407
397
        if not self.set_pl:
408
398
            return
409
399
        if self.curr_attr:
410
 
            self.set_pl.insert(0,[self.curr_attr,self.prev_q])
 
400
            self.set_pl.insert(0, [self.curr_attr, self.prev_q])
411
401
        if not self.sequential:
412
 
            self.set_pl.insert(0,["sequential","false"])
 
402
            self.set_pl.insert(0, ["sequential","false"])
413
403
        if not self.require_all:
414
 
            self.set_pl.insert(0,["require-all","false"])
415
 
        self.cli_list.append(["resource_set",self.set_pl])
 
404
            self.set_pl.insert(0, ["require-all","false"])
 
405
        self.cli_list.append(["resource_set", self.set_pl])
416
406
        self.reset_set()
417
407
    def parseattr(self, p):
418
408
        l = p.split('=')
425
415
        else:
426
416
            self.require_all = get_boolean(l[1])
427
417
        return True
428
 
    def splitrsc(self,p):
 
418
    def splitrsc(self, p):
429
419
        l = p.split(':')
430
420
        return (len(l) == 1) and [p,''] or l
431
421
    def parse(self):
438
428
                if self.set_pl: # save the set before
439
429
                    self.save_set()
440
430
                if self.opened:
441
 
                    syntax_err(self.tokens[tokpos:],context = self.type)
 
431
                    syntax_err(self.tokens[tokpos:], context = self.type)
442
432
                    return False
443
433
                self.sequential = False
444
434
                if p == '[':
447
437
                continue
448
438
            if p in self.close_set:
449
439
                if not self.opened:
450
 
                    syntax_err(self.tokens[tokpos:],context = self.type)
 
440
                    syntax_err(self.tokens[tokpos:], context = self.type)
451
441
                    return False
452
442
                if p != self.matching[self.opened]:
453
 
                    syntax_err(self.tokens[tokpos:],context = self.type)
 
443
                    syntax_err(self.tokens[tokpos:], context = self.type)
454
444
                    return False
455
445
                if not self.set_pl:  # empty sets not allowed
456
 
                    syntax_err(self.tokens[tokpos:],context = self.type)
 
446
                    syntax_err(self.tokens[tokpos:], context = self.type)
457
447
                    return False
458
448
                self.save_set()
459
449
                self.sequential = True
462
452
                continue
463
453
            if '=' in p:
464
454
                if not self.parseattr(p):
465
 
                    syntax_err(self.tokens[tokpos:],context = self.type)
 
455
                    syntax_err(self.tokens[tokpos:], context = self.type)
466
456
                    return False
467
457
                continue
468
 
            rsc,q = self.splitrsc(p)
 
458
            rsc, q = self.splitrsc(p)
469
459
            if q != self.prev_q: # one set can't have different roles/actions
470
460
                self.save_set()
471
461
                self.prev_q = q
472
462
            if q:
473
463
                if q not in self.valid_q:
474
 
                    common_err("%s: invalid %s in %s" % (q,self.q_attr,self.type))
 
464
                    common_err("%s: invalid %s in %s" % (q, self.q_attr, self.type))
475
465
                    return False
476
466
                if not self.curr_attr:
477
467
                    self.curr_attr = self.q_attr
478
468
            else:
479
469
                self.curr_attr = ''
480
 
            self.set_pl.append(["resource_ref",["id",rsc]])
 
470
            self.set_pl.append(["resource_ref", ["id", rsc]])
481
471
        if self.opened: # no close
482
 
            syntax_err(self.tokens[tokpos:],context = self.type)
 
472
            syntax_err(self.tokens[tokpos:], context = self.type)
483
473
            return False
484
474
        if self.set_pl: # save the final set
485
475
            self.save_set()
491
481
    type = s[0]
492
482
    if type == "collocation": # another ugly :(
493
483
        type = "colocation"
494
 
    cli_list.append([type,head_pl])
 
484
    cli_list.append([type, head_pl])
495
485
    if len(s) < 5:
496
 
        syntax_err(s,context = "colocation")
 
486
        syntax_err(s, context = "colocation")
497
487
        return False
498
 
    head_pl.append(["id",s[1]])
499
 
    if not cli_parse_score(s[2],head_pl):
 
488
    head_pl.append(["id", s[1]])
 
489
    if not cli_parse_score(s[2], head_pl):
500
490
        return False
501
491
    # save node-attribute for later (if it exists)
502
492
    node_attr = ""
503
493
    if is_attribute(s[len(s)-1],"node-attribute"):
504
494
        node_attr = s.pop()
505
495
    if len(s) == 5:
506
 
        if not cli_parse_rsc_role(s[3],head_pl):
 
496
        if not cli_parse_rsc_role(s[3], head_pl):
507
497
            return False
508
 
        if not cli_parse_rsc_role(s[4],head_pl,'with-'):
 
498
        if not cli_parse_rsc_role(s[4], head_pl,'with-'):
509
499
            return False
510
500
    else:
511
 
        resource_set_obj = ResourceSet(type,s[3:],cli_list)
 
501
        resource_set_obj = ResourceSet(type, s[3:], cli_list)
512
502
        if not resource_set_obj.parse():
513
503
            return False
514
504
    if not cli_opt_attribute(type, node_attr, head_pl, "node-attribute"):
515
505
        return False
516
506
    return cli_list
517
 
def cli_parse_rsc_action(s,pl,rsc_pos):
 
507
def cli_parse_rsc_action(s, pl, rsc_pos):
518
508
    l = s.split(':')
519
 
    pl.append([rsc_pos,l[0]])
 
509
    pl.append([rsc_pos, l[0]])
520
510
    if len(l) == 2:
521
511
        if l[1] in rng_attr_values('resource_set', 'action'):
522
 
            pl.append([rsc_pos+"-action",l[1]])
 
512
            pl.append([rsc_pos+"-action", l[1]])
523
513
        elif l[1].isdigit():
524
 
            pl.append([rsc_pos+"-instance",l[1]])
 
514
            pl.append([rsc_pos+"-instance", l[1]])
525
515
        else:
526
 
            bad_def_err("resource action/instance",s)
 
516
            bad_def_err("resource action/instance", s)
527
517
            return False
528
518
    elif len(l) > 1:
529
 
        bad_def_err("resource action/instance",s)
 
519
        bad_def_err("resource action/instance", s)
530
520
        return False
531
521
    return True
532
522
 
534
524
    cli_list = []
535
525
    head_pl = []
536
526
    type = "order"
537
 
    cli_list.append([s[0],head_pl])
 
527
    cli_list.append([s[0], head_pl])
538
528
    if len(s) < 5:
539
 
        syntax_err(s,context = "order")
 
529
        syntax_err(s, context = "order")
540
530
        return False
541
 
    head_pl.append(["id",s[1]])
542
 
    if not cli_parse_score(s[2],head_pl,noattr = True):
 
531
    head_pl.append(["id", s[1]])
 
532
    if not cli_parse_score(s[2], head_pl, noattr = True):
543
533
        return False
544
534
    # save symmetrical for later (if it exists)
545
535
    symm = ""
546
536
    if is_attribute(s[len(s)-1],"symmetrical"):
547
537
        symm = s.pop()
548
538
    if len(s) == 5:
549
 
        if not cli_parse_rsc_action(s[3],head_pl,'first'):
 
539
        if not cli_parse_rsc_action(s[3], head_pl,'first'):
550
540
            return False
551
 
        if not cli_parse_rsc_action(s[4],head_pl,'then'):
 
541
        if not cli_parse_rsc_action(s[4], head_pl,'then'):
552
542
            return False
553
543
    else:
554
 
        resource_set_obj = ResourceSet(type,s[3:],cli_list)
 
544
        resource_set_obj = ResourceSet(type, s[3:], cli_list)
555
545
        if not resource_set_obj.parse():
556
546
            return False
557
547
    if not cli_opt_attribute(type, symm, head_pl, "symmetrical"):
562
552
    cli_list = []
563
553
    head_pl = []
564
554
    type = "rsc_ticket"
565
 
    cli_list.append([s[0],head_pl])
 
555
    cli_list.append([s[0], head_pl])
566
556
    if len(s) < 4:
567
 
        syntax_err(s,context = "rsc_ticket")
 
557
        syntax_err(s, context = "rsc_ticket")
568
558
        return False
569
 
    head_pl.append(["id",s[1]])
570
 
    if not cli_parse_ticket(s[2],head_pl):
 
559
    head_pl.append(["id", s[1]])
 
560
    if not cli_parse_ticket(s[2], head_pl):
571
561
        return False
572
562
    # save loss-policy for later (if it exists)
573
563
    loss_policy = ""
596
586
def parse_property(s):
597
587
    cli_list = []
598
588
    head_pl = []
599
 
    cli_list.append([s[0],head_pl])
600
 
    cli_parse_attr_strict(s[1:],head_pl)
 
589
    cli_list.append([s[0], head_pl])
 
590
    cli_parse_attr_strict(s[1:], head_pl)
601
591
    if len(head_pl) < 0 or len(s) > len(head_pl)+1:
602
592
        syntax_err(s, context = s[0])
603
593
        return False
606
596
    l = s.split(':')
607
597
    if not l or len(l) > 2:
608
598
        return None
609
 
    pl.append(["uname",l[0]])
 
599
    pl.append(["uname", l[0]])
610
600
    if len(l) == 2:
611
 
        pl.append(["type",l[1]])
 
601
        pl.append(["type", l[1]])
612
602
def parse_node(s):
613
603
    cli_list = []
614
604
    # the head
617
607
    id = ''
618
608
    opt_id_l = []
619
609
    i = 1
620
 
    cli_parse_attr_strict(s[i:],opt_id_l)
 
610
    cli_parse_attr_strict(s[i:], opt_id_l)
621
611
    if opt_id_l:
622
612
        id = find_value(opt_id_l,"$id")
623
613
        i += 1
624
614
    # uname[:type]
625
 
    cli_parse_uname(s[i],head)
 
615
    cli_parse_uname(s[i], head)
626
616
    uname = find_value(head,"uname")
627
617
    if not uname:
628
618
        return False
629
 
    head.append(["id",id and id or uname])
 
619
    head.append(["id", id and id or uname])
630
620
    # drop type if default
631
621
    type = find_value(head,"type")
632
622
    if type == vars.node_default_type:
633
 
        head.remove(["type",type])
634
 
    cli_list.append([s[0],head])
 
623
        head.remove(["type", type])
 
624
    cli_list.append([s[0], head])
635
625
    if len(s) == i:
636
626
        return cli_list
637
627
    # the rest
638
628
    i += 1
639
629
    try:  # s[i] may be out of range
640
630
        if is_attribute(s[i],"description"):
641
 
            cli_parse_attr(s[i:i+1],head)
 
631
            cli_parse_attr(s[i:i+1], head)
642
632
            i += 1 # skip to the next token
643
633
    except: pass
644
634
    while len(s) > i+1:
646
636
            syntax_err(s[i:], context = 'node')
647
637
            return False
648
638
        pl = []
649
 
        cli_parse_attr(s[i+1:],pl)
 
639
        cli_parse_attr(s[i+1:], pl)
650
640
        if len(pl) == 0:
651
641
            syntax_err(s[i:], context = 'node')
652
642
            return False
653
 
        cli_list.append([s[i],pl])
 
643
        cli_list.append([s[i], pl])
654
644
        i += len(pl)+1
655
645
    if len(s) > i:
656
646
        syntax_err(s[i:], context = 'node')
660
650
    cli_list = []
661
651
    head_pl = []
662
652
    type = "fencing_topology"
663
 
    cli_list.append([type,head_pl])
 
653
    cli_list.append([type, head_pl])
664
654
    target = "@@"
665
655
    for tok in s[1:]:
666
656
        if tok.endswith(':'):
667
657
            target = tok.rstrip(':')
668
658
        else:
669
659
            head_pl.append(["fencing-level",
670
 
                [["target",target], ["devices",tok]]])
 
660
                [["target", target], ["devices", tok]]])
671
661
    return cli_list
672
662
 
673
663
def parse_xml(s):
680
670
        return False
681
671
    # strip spaces between elements
682
672
    # they produce text elements
683
 
    xml_s = re.sub(r">\s+<", "><", xml_s)
684
673
    try:
685
 
        doc = xml.dom.minidom.parseString(xml_s)
686
 
    except xml.parsers.expat.ExpatError, msg:
 
674
        e = etree.fromstring(xml_s)
 
675
    except Exception, msg:
687
676
        common_err("cannot parse xml chunk: %s" % xml_s)
688
677
        common_err(msg)
689
678
        return False
690
679
    try:
691
 
        elnode = doc.childNodes[0]
692
 
    except:
693
 
        common_err("no elements in %s" % xml_s)
694
 
        return False
695
 
    try:
696
 
        el_type = vars.cib_cli_map[elnode.tagName]
697
 
    except:
698
 
        common_err("element %s not recognized" % elnode.tagName)
699
 
        return False
700
 
    id = elnode.getAttribute("id")
701
 
    head.append(["id",id])
702
 
    cli_list.append([el_type,head])
703
 
    cli_list.append(["raw",xml_s])
 
680
        el_type = vars.cib_cli_map[e.tag]
 
681
    except:
 
682
        common_err("element %s not recognized" % e.tag)
 
683
        return False
 
684
    id = e.get("id")
 
685
    head.append(["id", id])
 
686
    cli_list.append([el_type, head])
 
687
    cli_list.append(["raw", xml_s])
704
688
    return cli_list
705
689
 
706
690
def expand_acl_shortcuts(l):
731
715
            if '@@' not in expansion[exp_i]:
732
716
                xpath += expansion[exp_i]
733
717
                exp_i += 1
734
 
            xpath += expansion[exp_i].replace('@@',tok)
 
718
            xpath += expansion[exp_i].replace('@@', tok)
735
719
            exp_i += 1
736
720
        except:
737
721
            return []
746
730
    l = []
747
731
    eligible_specs = vars.acl_spec_map.values()
748
732
    for spec in s:
749
 
        a = spec.split(':',1)
 
733
        a = spec.split(':', 1)
750
734
        a = expand_acl_shortcuts(a)
751
735
        if len(a) != 2 or a[0] not in eligible_specs:
752
736
            return l
753
 
        l.append([a[0],a[1]])
 
737
        l.append([a[0], a[1]])
754
738
        eligible_specs.remove(a[0])
755
739
        if a[0] == "xpath":
756
740
            eligible_specs.remove("ref")
762
746
        else:
763
747
            break # nothing after "attribute"
764
748
    return l
765
 
def cli_parse_acl_rules(s,obj_type,cli_list):
 
749
def cli_parse_acl_rules(s, obj_type, cli_list):
766
750
    i = 0
767
751
    while i < len(s):
768
752
        if not is_acl_rule_name(s[i]):
778
762
            syntax_err(s, context = obj_type)
779
763
            return False
780
764
        i += len(l)
781
 
        cli_list.append([rule_name,l])
 
765
        cli_list.append([rule_name, l])
782
766
    return cli_list
783
767
def parse_acl(s):
784
768
    cli_list = []
785
769
    head_pl = []
786
770
    obj_type = s[0]
787
 
    cli_list.append([obj_type,head_pl])
788
 
    head_pl.append(["id",s[1]])
 
771
    cli_list.append([obj_type, head_pl])
 
772
    head_pl.append(["id", s[1]])
789
773
    if keyword_cmp(obj_type, "user") and s[2].startswith("role:"):
790
 
        for i in range(2,len(s)):
791
 
            a = s[i].split(':',1)
 
774
        for i in range(2, len(s)):
 
775
            a = s[i].split(':', 1)
792
776
            if len(a) != 2 or a[0] != "role":
793
777
                syntax_err(s, context = obj_type)
794
778
                return False
795
 
            cli_list.append(["role_ref",["id",a[1]]])
 
779
            cli_list.append(["role_ref", ["id", a[1]]])
796
780
        return cli_list
797
 
    return cli_parse_acl_rules(s[2:],obj_type,cli_list)
 
781
    return cli_parse_acl_rules(s[2:], obj_type, cli_list)
798
782
 
799
783
def xml_lex(s):
800
784
    l = lines2cli(s)
806
790
class CliParser(object):
807
791
    parsers = {
808
792
        # elem_name: (minimum number of tokens, parse function)
809
 
        "primitive": (3,parse_resource),
810
 
        "group": (3,parse_resource),
811
 
        "clone": (3,parse_resource),
812
 
        "ms": (3,parse_resource),
813
 
        "master": (3,parse_resource),
814
 
        "rsc_template": (3,parse_resource),
815
 
        "location": (3,parse_constraint),
816
 
        "colocation": (3,parse_constraint),
817
 
        "collocation": (3,parse_constraint),
818
 
        "order": (3,parse_constraint),
819
 
        "rsc_ticket": (3,parse_constraint),
820
 
        "monitor": (3,parse_op),
821
 
        "node": (2,parse_node),
822
 
        "property": (2,parse_property),
823
 
        "rsc_defaults": (2,parse_property),
824
 
        "op_defaults": (2,parse_property),
825
 
        "fencing_topology": (3,parse_fencing_order),
826
 
        "role": (3,parse_acl),
827
 
        "user": (3,parse_acl),
828
 
        "xml": (3,parse_xml),
 
793
        "primitive": (3, parse_resource),
 
794
        "group": (3, parse_resource),
 
795
        "clone": (3, parse_resource),
 
796
        "ms": (3, parse_resource),
 
797
        "master": (3, parse_resource),
 
798
        "rsc_template": (3, parse_resource),
 
799
        "location": (3, parse_constraint),
 
800
        "colocation": (3, parse_constraint),
 
801
        "collocation": (3, parse_constraint),
 
802
        "order": (3, parse_constraint),
 
803
        "rsc_ticket": (3, parse_constraint),
 
804
        "monitor": (3, parse_op),
 
805
        "node": (2, parse_node),
 
806
        "property": (2, parse_property),
 
807
        "rsc_defaults": (2, parse_property),
 
808
        "op_defaults": (2, parse_property),
 
809
        "fencing_topology": (2, parse_fencing_order),
 
810
        "role": (3, parse_acl),
 
811
        "user": (3, parse_acl),
 
812
        "xml": (3, parse_xml),
829
813
    }
830
814
    def __init__(self):
831
815
        self.comments = []
832
 
    def parse(self,s):
 
816
    def parse(self, s):
833
817
        '''
834
818
        Input: a list of tokens (or a CLI format string).
835
819
        Return: a list of items; each item is a tuple
862
846
        if s[0] not in self.parsers.keys():
863
847
            syntax_err(s)
864
848
            return False
865
 
        mintoks,parser_fn = self.parsers[s[0]]
 
849
        mintoks, parser_fn = self.parsers[s[0]]
866
850
        if len(s) < mintoks:
867
851
            syntax_err(s)
868
852
            return False
870
854
        if not cli_list:
871
855
            return False
872
856
        if self.comments:
873
 
            cli_list.append(["comments",self.comments])
 
857
            cli_list.append(["comments", self.comments])
874
858
            self.comments = []
875
859
        return cli_list
876
860