Skip to content

Commit 361a5ff

Browse files
committed
High: Shell: template support
1 parent 683b422 commit 361a5ff

File tree

9 files changed

+267
-116
lines changed

9 files changed

+267
-116
lines changed

doc/crm.8.txt

+50-1
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,14 @@ Commands for resources are:
13551355
- `clone`
13561356
- `ms`/`master` (master-slave)
13571357

1358+
In order to streamline large configurations, it is possible to
1359+
define a template which can later be referenced in primitives:
1360+
1361+
- `rsc_template`
1362+
1363+
In that case the primitive inherits all attributes defined in the
1364+
template.
1365+
13581366
There are three types of constraints:
13591367

13601368
- `location`
@@ -1429,9 +1437,13 @@ instance attributes of that operation. A typical example is
14291437

14301438
For multistate resources, roles are specified as `role=<role>`.
14311439

1440+
A template may be defined for resources which are of the same
1441+
type and which share most of the configuration. See
1442+
<<cmdhelp_configure_rsc_template,`rsc_template`>> for more information.
1443+
14321444
Usage:
14331445
...............
1434-
primitive <rsc> [<class>:[<provider>:]]<type>
1446+
primitive <rsc> {[<class>:[<provider>:]]<type>|@<template>}
14351447
[params attr_list]
14361448
[meta attr_list]
14371449
[utilization attr_list]
@@ -1462,6 +1474,9 @@ Example:
14621474
params drbd_resource=r0 \
14631475
op monitor role=Master interval=60s \
14641476
op monitor role=Slave interval=300s
1477+
1478+
primitive xen0 @vm_scheme1 \
1479+
params xmfile=/etc/xen/vm/xen0
14651480
...............
14661481

14671482
[[cmdhelp_configure_monitor,add monitor operation to a primitive]]
@@ -1565,6 +1580,40 @@ It is advisable to give meaningful names to attribute sets which
15651580
are going to be referenced.
15661581
****************************
15671582

1583+
[[cmdhelp_configure_rsc_template,define a resource template]]
1584+
==== `rsc_template`
1585+
1586+
The `rsc_template` command creates a resource template. It may be
1587+
referenced in primitives. It is used to reduce large
1588+
configurations with many similar resources.
1589+
1590+
Usage:
1591+
...............
1592+
rsc_template <name> [<class>:[<provider>:]]<type>
1593+
[params attr_list]
1594+
[meta attr_list]
1595+
[utilization attr_list]
1596+
[operations id_spec]
1597+
[op op_type [<attribute>=<value>...] ...]
1598+
1599+
attr_list :: [$id=<id>] <attr>=<val> [<attr>=<val>...] | $id-ref=<id>
1600+
id_spec :: $id=<id> | $id-ref=<id>
1601+
op_type :: start | stop | monitor
1602+
...............
1603+
Example:
1604+
...............
1605+
rsc_template public_vm ocf:heartbeat:Xen \
1606+
op start timeout=300s \
1607+
op stop timeout=300s \
1608+
op monitor interval=30s timeout=60s \
1609+
op migrate_from timeout=600s \
1610+
op migrate_to timeout=600s
1611+
primitive xen0 @public_vm \
1612+
params xmfile=/etc/xen/xen0
1613+
primitive xen1 @public_vm \
1614+
params xmfile=/etc/xen/xen1
1615+
...............
1616+
15681617
[[cmdhelp_configure_location,a location preference]]
15691618
==== `location`
15701619

modules/cibconfig.py

+71-42
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from clidisplay import CliDisplay
3434
from cibstatus import CibStatus
3535
from idmgmt import IdMgmt
36-
from ra import RAInfo, get_properties_list, get_pe_meta
36+
from ra import get_ra, get_properties_list, get_pe_meta
3737

3838
def show_unrecognized_elems(doc):
3939
try:
@@ -242,18 +242,18 @@ def process_primitive(prim, clash_dict):
242242
(ra_class, ra_provider, ra_type, name, value) -> [ resourcename ]
243243
if parameter "name" should be unique
244244
'''
245-
ra_class = prim.getAttribute("class")
246-
ra_provider = prim.getAttribute("provider")
247-
ra_type = prim.getAttribute("type")
248245
ra_id = prim.getAttribute("id")
249-
ra = RAInfo(ra_class, ra_type, ra_provider)
250-
if ra == None:
246+
r_node = reduce_primitive(prim)
247+
if not r_node:
248+
return # template not defined yet
249+
ra = get_ra(r_node)
250+
if not ra.mk_ra_node(): # no RA found?
251251
return
252252
ra_params = ra.params()
253-
for a in prim.getElementsByTagName("instance_attributes"):
253+
for a in r_node.getElementsByTagName("instance_attributes"):
254254
# params are in instance_attributes just below the parent
255255
# operations may have some as well, e.g. OCF_CHECK_LEVEL
256-
if a.parentNode != prim:
256+
if a.parentNode != r_node:
257257
continue
258258
for p in a.getElementsByTagName("nvpair"):
259259
name = p.getAttribute("name")
@@ -984,11 +984,22 @@ def cli_list2node(self,cli_list,oldnode):
984984
remove_id_used_attributes(get_topnode(cib_factory.doc,self.parent_type))
985985
return headnode
986986

987-
def get_ra(node):
988-
ra_type = node.getAttribute("type")
989-
ra_class = node.getAttribute("class")
990-
ra_provider = node.getAttribute("provider")
991-
return RAInfo(ra_class,ra_type,ra_provider)
987+
def reduce_primitive(node):
988+
'''
989+
A primitive may reference template. If so, put the two
990+
together.
991+
Returns:
992+
- if no template reference, node itself
993+
- if template reference, but no template found, None
994+
- return merged primitive node into template node
995+
'''
996+
template = node.getAttribute("template")
997+
if not template:
998+
return node
999+
template_obj = cib_factory.find_object(template)
1000+
if not template_obj:
1001+
return None
1002+
return merge_nodes_2(node, template_obj.node)
9921003

9931004
class CibPrimitive(CibObject):
9941005
'''
@@ -1002,17 +1013,25 @@ class CibPrimitive(CibObject):
10021013
def repr_cli_head(self,node):
10031014
obj_type = vars.cib_cli_map[node.tagName]
10041015
node_id = node.getAttribute("id")
1005-
ra_type = node.getAttribute("type")
1006-
ra_class = node.getAttribute("class")
1007-
ra_provider = node.getAttribute("provider")
1008-
s1 = s2 = ''
1009-
if ra_class:
1010-
s1 = "%s:"%ra_class
1011-
if ra_provider:
1012-
s2 = "%s:"%ra_provider
1016+
if obj_type == "primitive":
1017+
template_ref = node.getAttribute("template")
1018+
else:
1019+
template_ref = None
1020+
if template_ref:
1021+
rsc_spec = "@%s" % cli_display.idref(template_ref)
1022+
else:
1023+
ra_type = node.getAttribute("type")
1024+
ra_class = node.getAttribute("class")
1025+
ra_provider = node.getAttribute("provider")
1026+
s1 = s2 = ''
1027+
if ra_class:
1028+
s1 = "%s:"%ra_class
1029+
if ra_provider:
1030+
s2 = "%s:"%ra_provider
1031+
rsc_spec = ''.join((s1,s2,ra_type))
10131032
s = cli_display.keyword(obj_type)
10141033
id = cli_display.id(node_id)
1015-
return "%s %s %s" % (s, id, ''.join((s1,s2,ra_type)))
1034+
return "%s %s %s" % (s, id, rsc_spec)
10161035
def repr_cli_child(self,c,format):
10171036
if c.tagName in self.set_names:
10181037
return "%s %s" % \
@@ -1080,31 +1099,30 @@ def check_sanity(self):
10801099
common_err("%s: no xml (strange)" % self.obj_id)
10811100
return user_prefs.get_check_rc()
10821101
rc3 = sanity_check_meta(self.obj_id,self.node,vars.rsc_meta_attributes)
1083-
ra = get_ra(self.node)
1102+
if self.obj_type == "primitive":
1103+
r_node = reduce_primitive(self.node)
1104+
if not r_node:
1105+
# perhaps the template will be defined later
1106+
return rc3
1107+
else:
1108+
r_node = self.node
1109+
ra = get_ra(r_node)
10841110
if not ra.mk_ra_node(): # no RA found?
10851111
if cib_factory.is_asymm_cluster():
10861112
return rc3
10871113
ra.error("no such resource agent")
10881114
return user_prefs.get_check_rc()
1115+
actions = get_rsc_operations(r_node)
1116+
default_timeout = get_default_timeout()
1117+
rc2 = ra.sanity_check_ops(self.obj_id, actions, default_timeout)
10891118
params = []
1090-
for c in self.node.childNodes:
1119+
for c in r_node.childNodes:
10911120
if not is_element(c):
10921121
continue
10931122
if c.tagName == "instance_attributes":
10941123
params += nvpairs2list(c)
1095-
rc1 = ra.sanity_check_params(self.obj_id, params)
1096-
actions = {}
1097-
for c in self.node.childNodes:
1098-
if not is_element(c):
1099-
continue
1100-
if c.tagName == "operations":
1101-
for c2 in c.childNodes:
1102-
if is_element(c2) and c2.tagName == "op":
1103-
op,pl = op2list(c2)
1104-
if op:
1105-
actions[op] = pl
1106-
default_timeout = get_default_timeout()
1107-
rc2 = ra.sanity_check_ops(self.obj_id, actions, default_timeout)
1124+
rc1 = ra.sanity_check_params(self.obj_id, params,
1125+
existence_only = (self.obj_type != "primitive"))
11081126
return rc1 | rc2 | rc3
11091127

11101128
class CibContainer(CibObject):
@@ -1389,6 +1407,7 @@ def get_default_timeout():
13891407
"group": ( "group", CibContainer, "resources" ),
13901408
"clone": ( "clone", CibContainer, "resources" ),
13911409
"master": ( "ms", CibContainer, "resources" ),
1410+
"template": ( "rsc_template", CibPrimitive, "resources" ),
13921411
"rsc_location": ( "location", CibLocation, "constraints" ),
13931412
"rsc_colocation": ( "colocation", CibSimpleConstraint, "constraints" ),
13941413
"rsc_order": ( "order", CibSimpleConstraint, "constraints" ),
@@ -1755,6 +1774,10 @@ def f_prim_id_list(self):
17551774
"List of possible primitives ids (for group completion)."
17561775
return [x.obj_id for x in self.cib_objects \
17571776
if x.obj_type == "primitive" and not x.parent]
1777+
def rsc_template_list(self):
1778+
"List of templates."
1779+
return [x.obj_id for x in self.cib_objects \
1780+
if x.obj_type == "rsc_template"]
17581781
def f_children_id_list(self):
17591782
"List of possible child ids (for clone/master completion)."
17601783
return [x.obj_id for x in self.cib_objects \
@@ -1800,13 +1823,19 @@ def default_timeouts(self,*args):
18001823
common_warn("element %s is not a primitive" % obj_id)
18011824
rc = False
18021825
continue
1803-
ra = get_ra(obj.node)
1826+
r_node = reduce_primitive(obj.node)
1827+
if not r_node:
1828+
# cannot do anything without template defined
1829+
common_warn("template for %s not defined" % obj_id)
1830+
rc = False
1831+
continue
1832+
ra = get_ra(r_node)
18041833
if not ra.mk_ra_node(): # no RA found?
18051834
if not self.is_asymm_cluster():
18061835
ra.error("no resource agent found for %s" % obj_id)
18071836
continue
18081837
obj_modified = False
1809-
for c in obj.node.childNodes:
1838+
for c in r_node.childNodes:
18101839
if not is_element(c):
18111840
continue
18121841
if c.tagName == "operations":
@@ -1818,7 +1847,7 @@ def default_timeouts(self,*args):
18181847
continue
18191848
if op in implied_actions:
18201849
implied_actions.remove(op)
1821-
elif can_migrate(obj.node) and op in implied_migrate_actions:
1850+
elif can_migrate(r_node) and op in implied_migrate_actions:
18221851
implied_migrate_actions.remove(op)
18231852
elif is_ms(obj.node.parentNode) and op in implied_ms_actions:
18241853
implied_ms_actions.remove(op)
@@ -1829,7 +1858,7 @@ def default_timeouts(self,*args):
18291858
c2.setAttribute("timeout",adv_timeout)
18301859
obj_modified = True
18311860
l = implied_actions
1832-
if can_migrate(obj.node):
1861+
if can_migrate(r_node):
18331862
l += implied_migrate_actions
18341863
if is_ms(obj.node.parentNode):
18351864
l += implied_ms_actions
@@ -2123,7 +2152,7 @@ def merge_from_cli(self,obj,cli_list):
21232152
if not node:
21242153
return
21252154
if obj.obj_type in vars.nvset_cli_names:
2126-
rc = merge_nvpairs(obj.node, node)
2155+
rc = merge_attributes(obj.node, node, "nvpair")
21272156
else:
21282157
rc = merge_nodes(obj.node, node)
21292158
if rc:

modules/cliformat.py

-11
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,6 @@ def nvpairs2list(node, add_id = False):
8888
pl.append([name,value])
8989
return pl
9090

91-
def op2list(node):
92-
pl = []
93-
action = ""
94-
for name in node.attributes.keys():
95-
if name == "name":
96-
action = node.getAttribute(name)
97-
elif name != "id": # skip the id
98-
pl.append([name,node.getAttribute(name)])
99-
if not action:
100-
common_err("op is invalid (no name)")
101-
return action,pl
10291
def op_instattr(node):
10392
pl = []
10493
for c in node.childNodes:

modules/completion.py

+34-19
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ def node_id_list(idx,delimiter = False):
121121
if delimiter:
122122
return ' '
123123
return cib_factory.node_id_list()
124+
def rsc_template_list(idx,delimiter = False):
125+
if delimiter:
126+
return ' '
127+
return cib_factory.rsc_template_list()
124128
def node_attr_keyw_list(idx,delimiter = False):
125129
if delimiter:
126130
return ' '
@@ -182,12 +186,11 @@ def ticket_cmd_list(idx,delimiter = False):
182186
# completion for primitives including help for parameters
183187
# (help also available for properties)
184188
#
185-
def get_primitive_type(words):
186-
try:
187-
idx = words.index("primitive") + 2
188-
type_word = words[idx]
189-
except: type_word = ''
190-
return type_word
189+
def get_prim_token(words, n):
190+
for key in ("primitive", "rsc_template"):
191+
try: return words[words.index(key)+n-1]
192+
except: pass
193+
return ''
191194
def ra_type_list(toks,idx,delimiter):
192195
if idx == 2:
193196
if toks[0] == "ocf":
@@ -257,6 +260,12 @@ def get_lastkeyw(words,keyw):
257260
for w in revwords:
258261
if w in keyw:
259262
return w
263+
def ra_classes_or_tmpl(idx,delimiter = False):
264+
words = readline.get_line_buffer().split()
265+
if words[-1].startswith('@'):
266+
return rsc_template_list(idx,delimiter)
267+
else:
268+
return ra_classes_list(idx,delimiter)
260269
def primitive_complete_complex(idx,delimiter = False):
261270
'''
262271
This completer depends on the content of the line, i.e. on
@@ -269,19 +278,24 @@ def primitive_complete_complex(idx,delimiter = False):
269278
}
270279
# manage the resource type
271280
words = readline.get_line_buffer().split()
272-
type_word = get_primitive_type(words)
273-
toks = type_word.split(':')
274-
if toks[0] != "ocf":
275-
idx += 1
276-
if idx in (2,3):
277-
return ra_type_list(toks,idx,delimiter)
278-
# create an ra object
279-
ra = None
280-
ra_class,provider,rsc_type = disambiguate_ra_type(type_word)
281-
if ra_type_validate(type_word,ra_class,provider,rsc_type):
282-
ra = RAInfo(ra_class,rsc_type,provider)
281+
cmd = get_prim_token(words, 1)
282+
type_word = get_prim_token(words, 3)
283+
with_template = cmd == "primitive" and type_word.startswith('@')
284+
if with_template:
285+
# template reference
286+
ra = get_ra(cib_factory.find_object(type_word[1:]).node)
287+
else:
288+
toks = type_word.split(':')
289+
if toks[0] != "ocf":
290+
idx += 1
291+
if idx in (2,3):
292+
return ra_type_list(toks,idx,delimiter)
293+
ra = None
294+
ra_class,provider,rsc_type = disambiguate_ra_type(type_word)
295+
if ra_type_validate(type_word,ra_class,provider,rsc_type):
296+
ra = RAInfo(ra_class,rsc_type,provider)
283297
keywords = completers_set.keys()
284-
if idx == 4:
298+
if idx == 4 or (idx == 2 and with_template):
285299
if delimiter:
286300
return ' '
287301
return keywords
@@ -464,7 +478,8 @@ def setup_readline():
464478
"save" : None,
465479
"load" : None,
466480
"node" : (node_id_list,node_attr_keyw_list),
467-
"primitive" : (null_list,ra_classes_list,primitive_complete_complex,loop),
481+
"primitive" : (null_list,ra_classes_or_tmpl,primitive_complete_complex,loop),
482+
"rsc_template" : (null_list,ra_classes_list,primitive_complete_complex,loop),
468483
"group" : (null_list,f_prim_id_list,loop),
469484
"clone" : (null_list,f_children_id_list),
470485
"ms" : (null_list,f_children_id_list),

0 commit comments

Comments
 (0)