1# Copyright (C) 2008-2011 Dejan Muhamedagic <dmuhamedagic@suse.de>
2# See COPYING for license information.
3
4from . import constants
5from . import clidisplay
6from . import utils
7from . import xmlutil
8
9
10#
11# CLI format generation utilities (from XML)
12#
13def cli_format(pl, break_lines=True, xml=False):
14    if break_lines and xml:
15        return ' \\\n'.join(pl)
16    elif break_lines:
17        return ' \\\n\t'.join(pl)
18    else:
19        return ' '.join(pl)
20
21
22def head_id_format(nodeid):
23    "Special format for property list / node id"
24    if utils.noquotes(nodeid):
25        return "%s:" % (clidisplay.ident(nodeid))
26    return '%s="%s"' % (clidisplay.ident('$id'),
27                        clidisplay.attr_value(nodeid))
28
29
30def quote_wrap(v):
31    if utils.noquotes(v):
32        return v
33    elif '"' in v:
34        return '"%s"' % v.replace('"', '\\"')
35    else:
36        return '"%s"' % v
37
38
39def nvpair_format(n, v):
40    if v is None:
41        return clidisplay.attr_name(n)
42    else:
43        return '='.join((clidisplay.attr_name(n),
44                         clidisplay.attr_value(quote_wrap(v))))
45
46
47def cli_nvpair(nvp):
48    'Converts an nvpair tag or a (name, value) pair to CLI syntax'
49    from .cibconfig import cib_factory
50    from .utils import obscured
51    nodeid = nvp.get('id')
52    idref = nvp.get('id-ref')
53    name = nvp.get('name')
54    value = nvp.get('value')
55    value = obscured(name, value)
56    if idref is not None:
57        if name is not None:
58            return '@%s:%s' % (idref, name)
59        return '@%s' % (idref)
60    elif nodeid is not None and cib_factory.is_id_refd(nvp.tag, nodeid):
61        return '$%s:%s' % (nodeid, nvpair_format(name, value))
62    return nvpair_format(name, value)
63
64
65def cli_nvpairs(nvplist):
66    'Return a string of name="value" pairs (passed in a list of nvpairs).'
67    return ' '.join([cli_nvpair(nvp) for nvp in nvplist])
68
69
70def nvpairs2list(node, add_id=False):
71    '''
72    Convert an attribute node to a list of nvpairs.
73    Also converts an id-ref or id into plain nvpairs.
74    The id attribute is normally skipped, since they tend to be
75    long and therefore obscure the relevant content. For some
76    elements, however, they are included (e.g. properties).
77    '''
78    ret = []
79    if 'id-ref' in node:
80        ret.append(xmlutil.nvpair('$id-ref', node.get('id-ref')))
81    nvpairs = node.xpath('./nvpair | ./attributes/nvpair')
82    if 'id' in node and (add_id or len(nvpairs) == 0):
83        ret.append(xmlutil.nvpair('$id', node.get('id')))
84    ret.extend(nvpairs)
85    return ret
86
87
88def date_exp2cli(node):
89    kwmap = {'in_range': 'in', 'date_spec': 'spec'}
90    l = []
91    operation = node.get("operation")
92    l.append(clidisplay.keyword("date"))
93    l.append(clidisplay.keyword(kwmap.get(operation, operation)))
94    if operation in utils.olist(constants.simple_date_ops):
95        value = node.get(utils.keyword_cmp(operation, 'lt') and "end" or "start")
96        l.append(clidisplay.attr_value(quote_wrap(value)))
97    else:
98        if operation == 'in_range':
99            for name in constants.in_range_attrs:
100                if name in node.attrib:
101                    l.append(nvpair_format(name, node.attrib[name]))
102        for c in node.iterchildren():
103            if c.tag in ("duration", "date_spec"):
104                l.extend([nvpair_format(name, c.get(name))
105                          for name in list(c.keys()) if name != 'id'])
106    return ' '.join(l)
107
108
109def binary_op_format(op):
110    l = op.split(':')
111    if len(l) == 2:
112        return "%s:%s" % (l[0], clidisplay.keyword(l[1]))
113    else:
114        return clidisplay.keyword(op)
115
116
117def exp2cli(node):
118    operation = node.get("operation")
119    typ = node.get("type")
120    if typ:
121        operation = "%s:%s" % (typ, operation)
122    attribute = node.get("attribute")
123    value = node.get("value")
124    if not value:
125        return "%s %s" % (binary_op_format(operation), attribute)
126    else:
127        value_source = node.get("value-source")
128        if not value_source or value_source == "literal":
129            return "%s %s %s" % (attribute, binary_op_format(operation), value)
130        else:
131            return "%s %s %s{%s}" % (attribute, binary_op_format(operation), value_source, value)
132
133
134def abs_pos_score(score):
135    return score in ("inf", "+inf", "Mandatory")
136
137
138def get_kind(node):
139    kind = node.get("kind")
140    if not kind:
141        kind = ""
142    return kind
143
144
145def get_score(node):
146    score = node.get("score")
147    if not score:
148        score = node.get("score-attribute")
149    else:
150        if score.find("INFINITY") >= 0:
151            score = score.replace("INFINITY", "inf")
152    if not score:
153        score = ""
154    return score
155
156
157def cli_rule_score(node):
158    score = node.get("score")
159    if score == "INFINITY":
160        return None
161    return get_score(node)
162
163
164def cli_exprs(node):
165    bool_op = node.get("boolean-op")
166    if not bool_op:
167        bool_op = "and"
168    exp = []
169    for c in node.iterchildren():
170        if c.tag == "date_expression":
171            exp.append(date_exp2cli(c))
172        elif c.tag == "expression":
173            exp.append(exp2cli(c))
174    return (" %s " % clidisplay.keyword(bool_op)).join(exp)
175
176
177def cli_rule(node):
178    from .cibconfig import cib_factory
179    s = []
180    node_id = node.get("id")
181    if node_id and cib_factory.is_id_refd(node.tag, node_id):
182        s.append(nvpair_format('$id', node_id))
183    else:
184        idref = node.get("id-ref")
185        if idref:
186            return nvpair_format('$id-ref', idref)
187    rsc_role = node.get("role")
188    if rsc_role:
189        s.append(nvpair_format('$role', rsc_role))
190    score = cli_rule_score(node)
191    if score:
192        s.append("%s:" % (clidisplay.score(score)))
193    s.append(cli_exprs(node))
194    return ' '.join(s)
195
196
197def mkrscrole(node, n):
198    rsc = clidisplay.rscref(node.get(n))
199    rsc_role = node.get(n + "-role")
200    rsc_instance = node.get(n + "-instance")
201    if rsc_role:
202        return "%s:%s" % (rsc, rsc_role)
203    elif rsc_instance:
204        return "%s:%s" % (rsc, rsc_instance)
205    else:
206        return rsc
207
208
209def mkrscaction(node, n):
210    rsc = clidisplay.rscref(node.get(n))
211    rsc_action = node.get(n + "-action")
212    rsc_instance = node.get(n + "-instance")
213    if rsc_action:
214        return "%s:%s" % (rsc, rsc_action)
215    elif rsc_instance:
216        return "%s:%s" % (rsc, rsc_instance)
217    else:
218        return rsc
219
220
221def cli_path(p):
222    return clidisplay.attr_value(quote_wrap(p))
223
224
225def boolean_maybe(v):
226    "returns True/False or None"
227    if v is None:
228        return None
229    return utils.get_boolean(v)
230
231
232def rsc_set_constraint(node, obj_type):
233    col = []
234    cnt = 0
235    for n in node.findall("resource_set"):
236        sequential = boolean_maybe(n.get("sequential"))
237        require_all = boolean_maybe(n.get("require-all"))
238        if require_all is False:
239            col.append("[")
240        elif sequential is False:
241            col.append("(")
242        role = n.get("role")
243        action = n.get("action")
244        for r in n.findall("resource_ref"):
245            rsc = clidisplay.rscref(r.get("id"))
246            q = (obj_type == "order") and action or role
247            col.append(q and "%s:%s" % (rsc, q) or rsc)
248            cnt += 1
249        if require_all is False:
250            if sequential in (None, True):
251                col.append(nvpair_format('sequential', 'true'))
252            col.append("]")
253        elif sequential is False:
254            if require_all is False:
255                col.append(nvpair_format('require-all', 'false'))
256            col.append(")")
257    is_ticket = obj_type == 'rsc_ticket'
258    is_location = obj_type == 'location'
259    is_seq_all = sequential in (None, True) and require_all in (None, True)
260    if not is_location and ((is_seq_all and not is_ticket and cnt <= 2) or
261                            (is_ticket and cnt <= 1)):  # a degenerate thingie
262        col.insert(0, "_rsc_set_")
263    return col
264
265
266def simple_rsc_constraint(node, obj_type):
267    col = []
268    if obj_type == "colocation":
269        col.append(mkrscrole(node, "rsc"))
270        col.append(mkrscrole(node, "with-rsc"))
271    elif obj_type == "order":
272        col.append(mkrscaction(node, "first"))
273        col.append(mkrscaction(node, "then"))
274    else:  # rsc_ticket
275        col.append(mkrscrole(node, "rsc"))
276    return col
277
278
279# this pre (or post)-processing is oversimplified
280# but it will do for now
281# (a shortcut with more than one placeholder in a single expansion
282# cannot have more than one expansion)
283# ("...'@@'...'@@'...","...") <- that won't work
284def build_exp_re(exp_l):
285    return [x.replace(r'@@', r'([a-zA-Z_][a-zA-Z0-9_.-]*)') for x in exp_l]
286
287
288def match_acl_shortcut(xpath, re_l):
289    import re
290    for i in range(len(re_l)):
291        s = ''.join(re_l[0:i+1])
292        r = re.match(s + r"$", xpath)
293        if r:
294            return (True, r.groups()[0:i+1])
295    return (False, None)
296
297
298def find_acl_shortcut(xpath):
299    for shortcut in constants.acl_shortcuts:
300        l = build_exp_re(constants.acl_shortcuts[shortcut])
301        (ec, spec_l) = match_acl_shortcut(xpath, l)
302        if ec:
303            return (shortcut, spec_l)
304    return (None, None)
305
306
307def acl_spec_format(xml_spec, v):
308    key_f = clidisplay.keyword(constants.acl_spec_map[xml_spec])
309    if xml_spec == "xpath":
310        (shortcut, spec_l) = find_acl_shortcut(v)
311        if shortcut:
312            key_f = clidisplay.keyword(shortcut)
313            v_f = ':'.join([clidisplay.attr_value(x) for x in spec_l])
314        else:
315            v_f = clidisplay.attr_value(quote_wrap(v))
316    elif xml_spec == "ref":
317        v_f = '%s' % clidisplay.attr_value(v)
318    else:  # tag and attribute
319        v_f = '%s' % clidisplay.attr_value(v)
320    return v_f and '%s:%s' % (key_f, v_f) or key_f
321
322
323def cli_acl_rule(node, format_mode=1):
324    l = []
325    acl_rule_name = node.tag
326    l.append(clidisplay.keyword(acl_rule_name))
327    for xml_spec in constants.acl_spec_map:
328        v = node.get(xml_spec)
329        if v:
330            l.append(acl_spec_format(xml_spec, v))
331    return ' '.join(l)
332
333
334def cli_acl_roleref(node, format_mode=1):
335    return "%s:%s" % (clidisplay.keyword("role"),
336                      clidisplay.attr_value(node.get("id")))
337
338
339def cli_acl_role(node):
340    return clidisplay.attr_value(node.get("id"))
341
342
343def cli_acl_spec2_format(xml_spec, v):
344    key_f = clidisplay.keyword(xml_spec)
345    if xml_spec == "xpath":
346        (shortcut, spec_l) = find_acl_shortcut(v)
347        if shortcut:
348            key_f = clidisplay.keyword(shortcut)
349            v_f = ':'.join([clidisplay.attr_value(x) for x in spec_l])
350        else:
351            v_f = clidisplay.attr_value(quote_wrap(v))
352    else:  # ref, type and attr
353        v_f = clidisplay.attr_value(v)
354    return v_f and '%s:%s' % (key_f, v_f) or key_f
355
356
357def cli_acl_permission(node):
358    s = [clidisplay.keyword(node.get('kind'))]
359    # if node.get('id'):
360    #     s.append(head_id_format(node.get('id')))
361    if node.get('description'):
362        s.append(nvpair_format('description', node.get('description')))
363    for attrname, cliname in constants.acl_spec_map_2_rev:
364        if attrname in node.attrib:
365            s.append(cli_acl_spec2_format(cliname, node.get(attrname)))
366    return ' '.join(s)
367
368#
369################################################################
370
371# vim:ts=4:sw=4:et:
372