34
34
Parse the resource type.
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):
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):
47
47
Parse attributes in the 'p=v' form.
49
49
if s and '=' in s[0]:
50
n,v = s[0].split('=',1)
50
n, v = s[0].split('=', 1)
54
cli_parse_attr_strict(s[1:],pl)
55
def cli_parse_attr(s,pl):
54
cli_parse_attr_strict(s[1:], pl)
55
def cli_parse_attr(s, pl):
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.
61
attr_lists_keyw = olist(["params","meta","utilization","operations","op","attributes"])
61
attr_lists_keyw = olist(["params", "meta", "utilization", "operations", "op", "attributes"])
63
63
if s[0] in attr_lists_keyw:
66
n,v = s[0].split('=',1)
66
n, v = s[0].split('=', 1)
72
cli_parse_attr(s[1:],pl)
73
def is_only_id(pl,keyw):
72
cli_parse_attr(s[1:], pl)
73
def is_only_id(pl, keyw):
75
75
common_err("%s: only single $id or $id-ref attribute is allowed" % keyw)
99
head.append(["id",s[1]])
91
head.append(["id", s[1]])
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:]])
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)
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
127
cli_list.append([el_type,head])
119
cli_list.append([el_type, head])
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"):
141
pl.append(["name",s[i+1]])
133
pl.append(["name", s[i+1]])
143
135
syntax_err(s[i:], context = el_type)
145
137
if keyword_cmp(keyw, "op"):
147
cli_parse_attr(s[i+2:],pl)
148
if not check_operation(pl):
139
cli_parse_attr(s[i+2:], pl)
151
cli_parse_attr(s[i+1:],pl)
141
cli_parse_attr(s[i+1:], pl)
153
143
syntax_err(s[i:], context = el_type)
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):
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])
163
153
syntax_err(s[i:], context = el_type)
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):
176
if not cli_parse_op_times(s[2],head_pl):
166
if not cli_parse_op_times(s[2], head_pl):
178
168
# rename rsc-role to role
179
169
for i in range(len(head_pl)):
181
171
head_pl[i][0] = "role"
183
173
# add the operation name
184
head_pl.append(["name",s[0]])
174
head_pl.append(["name", s[0]])
187
def cli_parse_ticket(ticket,pl):
177
def cli_parse_ticket(ticket, pl):
188
178
if ticket.endswith(':'):
189
179
ticket = ticket.rstrip(':')
191
181
syntax_err(ticket, context = 'rsc_ticket')
193
pl.append(["ticket",ticket])
183
pl.append(["ticket", ticket])
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(':')
200
190
syntax_err(score, context = 'score')
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'))])
224
214
return l[0] in olist(vars.binary_ops)
227
def cli_parse_binary_op(s,pl):
217
def cli_parse_binary_op(s, pl):
230
pl.append(["type",l[0]])
231
pl.append(["operation",l[1]])
220
pl.append(["type", l[0]])
221
pl.append(["operation", l[1]])
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]])
245
def cli_parse_dateexpr(s,pl):
235
def cli_parse_dateexpr(s, pl):
248
238
if s[1] not in olist(rng_attr_values_l('date_expression', 'operation')):
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]])
254
cli_parse_attr_strict(s[2:],pl)
244
cli_parse_attr_strict(s[2:], pl)
256
246
def parse_rule(s):
257
247
if not keyword_cmp(s[0], "rule"):
258
syntax_err(s,context = "rule")
248
syntax_err(s, context = "rule")
262
rule_list.append([s[0].lower(),head_pl])
252
rule_list.append([s[0].lower(), head_pl])
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"):
268
if not cli_parse_score(s[i],head_pl):
258
if not cli_parse_score(s[i], head_pl):
272
262
while len(s) > i+1:
278
268
fun = cli_parse_expression
279
269
elem = "expression"
280
if not fun(s[i:],pl):
281
syntax_err(s[i:],context = "rule")
283
rule_list.append([elem,pl])
270
if not fun(s[i:], pl):
271
syntax_err(s[i:], context = "rule")
273
rule_list.append([elem, 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))
294
284
bool_op = s[i].lower()
296
286
if len(s) > i and keyword_cmp(s[i], "rule"):
298
288
if bool_op and not keyword_cmp(bool_op, 'and'):
299
head_pl.append(["boolean-op",bool_op])
289
head_pl.append(["boolean-op", bool_op])
301
291
def parse_location(s):
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):
310
head_pl.append(["node",s[4]])
300
head_pl.append(["node", s[4]])
313
303
while i < len(s):
314
numtoks,l = parse_rule(s[i:])
304
numtoks, l = parse_rule(s[i:])
320
syntax_err(s[i:],context = "location")
310
syntax_err(s[i:], context = "location")
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)
334
def cli_parse_rsc_role(s,pl,attr_pfx = ''):
324
def cli_parse_rsc_role(s, pl, attr_pfx=''):
336
pl.append([attr_pfx+"rsc",l[0]])
326
pl.append([attr_pfx+"rsc", l[0]])
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]])
343
bad_def_err("resource role/instance",s)
333
bad_def_err("resource role/instance", s)
346
bad_def_err("resource role/instance",s)
336
bad_def_err("resource role/instance", s)
349
def cli_parse_op_times(s,pl):
339
def cli_parse_op_times(s, pl):
351
pl.append(["interval",l[0]])
341
pl.append(["interval", l[0]])
353
pl.append(["timeout",l[1]])
343
pl.append(["timeout", l[1]])
355
bad_def_err("op times",s)
345
bad_def_err("op times", s)
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.
407
397
if not self.set_pl:
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])
417
407
def parseattr(self, p):
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)
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)
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)
459
449
self.sequential = True
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)
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
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))
476
466
if not self.curr_attr:
477
467
self.curr_attr = self.q_attr
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)
484
474
if self.set_pl: # save the final set
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])
496
syntax_err(s,context = "colocation")
486
syntax_err(s, context = "colocation")
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):
501
491
# save node-attribute for later (if it exists)
503
493
if is_attribute(s[len(s)-1],"node-attribute"):
504
494
node_attr = s.pop()
506
if not cli_parse_rsc_role(s[3],head_pl):
496
if not cli_parse_rsc_role(s[3], head_pl):
508
if not cli_parse_rsc_role(s[4],head_pl,'with-'):
498
if not cli_parse_rsc_role(s[4], head_pl,'with-'):
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():
514
504
if not cli_opt_attribute(type, node_attr, head_pl, "node-attribute"):
517
def cli_parse_rsc_action(s,pl,rsc_pos):
507
def cli_parse_rsc_action(s, pl, rsc_pos):
519
pl.append([rsc_pos,l[0]])
509
pl.append([rsc_pos, l[0]])
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]])
526
bad_def_err("resource action/instance",s)
516
bad_def_err("resource action/instance", s)
529
bad_def_err("resource action/instance",s)
519
bad_def_err("resource action/instance", s)
537
cli_list.append([s[0],head_pl])
527
cli_list.append([s[0], head_pl])
539
syntax_err(s,context = "order")
529
syntax_err(s, context = "order")
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):
544
534
# save symmetrical for later (if it exists)
546
536
if is_attribute(s[len(s)-1],"symmetrical"):
549
if not cli_parse_rsc_action(s[3],head_pl,'first'):
539
if not cli_parse_rsc_action(s[3], head_pl,'first'):
551
if not cli_parse_rsc_action(s[4],head_pl,'then'):
541
if not cli_parse_rsc_action(s[4], head_pl,'then'):
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():
557
547
if not cli_opt_attribute(type, symm, head_pl, "symmetrical"):
564
554
type = "rsc_ticket"
565
cli_list.append([s[0],head_pl])
555
cli_list.append([s[0], head_pl])
567
syntax_err(s,context = "rsc_ticket")
557
syntax_err(s, context = "rsc_ticket")
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):
572
562
# save loss-policy for later (if it exists)
620
cli_parse_attr_strict(s[i:],opt_id_l)
610
cli_parse_attr_strict(s[i:], opt_id_l)
622
612
id = find_value(opt_id_l,"$id")
625
cli_parse_uname(s[i],head)
615
cli_parse_uname(s[i], head)
626
616
uname = find_value(head,"uname")
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])
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
644
634
while len(s) > i+1:
646
636
syntax_err(s[i:], context = 'node')
649
cli_parse_attr(s[i+1:],pl)
639
cli_parse_attr(s[i+1:], pl)
651
641
syntax_err(s[i:], context = 'node')
653
cli_list.append([s[i],pl])
643
cli_list.append([s[i], pl])
656
646
syntax_err(s[i:], context = 'node')
662
652
type = "fencing_topology"
663
cli_list.append([type,head_pl])
653
cli_list.append([type, head_pl])
665
655
for tok in s[1:]:
666
656
if tok.endswith(':'):
667
657
target = tok.rstrip(':')
669
659
head_pl.append(["fencing-level",
670
[["target",target], ["devices",tok]]])
660
[["target", target], ["devices", tok]]])
673
663
def parse_xml(s):
681
671
# strip spaces between elements
682
672
# they produce text elements
683
xml_s = re.sub(r">\s+<", "><", xml_s)
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)
691
elnode = doc.childNodes[0]
693
common_err("no elements in %s" % xml_s)
696
el_type = vars.cib_cli_map[elnode.tagName]
698
common_err("element %s not recognized" % elnode.tagName)
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]
682
common_err("element %s not recognized" % e.tag)
685
head.append(["id", id])
686
cli_list.append([el_type, head])
687
cli_list.append(["raw", xml_s])
706
690
def expand_acl_shortcuts(l):
778
762
syntax_err(s, context = obj_type)
781
cli_list.append([rule_name,l])
765
cli_list.append([rule_name, l])
783
767
def parse_acl(s):
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)
795
cli_list.append(["role_ref",["id",a[1]]])
779
cli_list.append(["role_ref", ["id", a[1]]])
797
return cli_parse_acl_rules(s[2:],obj_type,cli_list)
781
return cli_parse_acl_rules(s[2:], obj_type, cli_list)
806
790
class CliParser(object):
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),
830
814
def __init__(self):
831
815
self.comments = []
834
818
Input: a list of tokens (or a CLI format string).
835
819
Return: a list of items; each item is a tuple