39
37
def __init__(self):
40
38
self.good = self.is_lrmd_accessible()
41
def lrmadmin(self, opts, xml = False):
39
def lrmadmin(self, opts, xml=False):
43
41
Get information directly from lrmd using lrmadmin.
45
l = stdout2list("%s %s" % (lrmadmin_prog,opts))
43
rc, l = stdout2list("%s %s" % (lrmadmin_prog, opts))
47
45
l = l[1:] # skip the first line
52
50
return subprocess.call(\
53
51
add_sudo(">/dev/null 2>&1 %s -C" % lrmadmin_prog), \
55
def meta(self, ra_class,ra_type,ra_provider):
56
return self.lrmadmin("-M %s %s %s"%(ra_class,ra_type,ra_provider),True)
57
def providers(self, ra_type,ra_class = "ocf"):
53
def meta(self, ra_class, ra_type, ra_provider):
54
return self.lrmadmin("-M %s %s %s"%(ra_class, ra_type, ra_provider), True)
55
def providers(self, ra_type, ra_class="ocf"):
58
56
'List of providers for a class:type.'
59
return self.lrmadmin("-P %s %s" % (ra_class,ra_type),True)
57
return self.lrmadmin("-P %s %s" % (ra_class, ra_type), True)
62
60
return self.lrmadmin("-C")
63
def types(self, ra_class = "ocf", ra_provider = ""):
61
def types(self, ra_class="ocf", ra_provider=""):
64
62
'List of types for a class.'
65
63
return self.lrmadmin("-T %s" % ra_class)
71
69
def __init__(self):
73
def meta(self, ra_class,ra_type,ra_provider):
71
def meta(self, ra_class, ra_type, ra_provider):
75
73
if ra_class == "ocf":
76
l = stdout2list("%s/resource.d/%s/%s meta-data" % \
77
(os.environ["OCF_ROOT"],ra_provider,ra_type))
74
rc, l = stdout2list("%s/resource.d/%s/%s meta-data" % \
75
(os.environ["OCF_ROOT"], ra_provider, ra_type))
78
76
elif ra_class == "stonith":
79
77
if ra_type.startswith("fence_") and os.path.exists("/usr/sbin/%s" % ra_type):
80
l = stdout2list("/usr/sbin/%s -o metadata" % ra_type)
78
rc, l = stdout2list("/usr/sbin/%s -o metadata" % ra_type)
82
l = stdout2list("stonith -m -t %s" % ra_type)
80
rc, l = stdout2list("stonith -m -t %s" % ra_type)
83
81
elif ra_class == "nagios":
84
l = stdout2list("%s/check_%s --metadata" % \
82
rc, l = stdout2list("%s/check_%s --metadata" % \
85
83
(vars.nagios_dir, ra_type))
87
def providers(self, ra_type,ra_class = "ocf"):
85
def providers(self, ra_type, ra_class="ocf"):
88
86
'List of providers for a class:type.'
90
88
if ra_class == "ocf":
91
for s in glob.glob("%s/resource.d/*/%s" % (os.environ["OCF_ROOT"],ra_type)):
89
for s in glob.glob("%s/resource.d/*/%s" % (os.environ["OCF_ROOT"], ra_type)):
98
96
return "heartbeat lsb nagios ocf stonith".split()
99
def types(self, ra_class = "ocf", ra_provider = ""):
97
def types(self, ra_class="ocf", ra_provider=""):
100
98
'List of types for a class.'
102
100
prov = ra_provider and ra_provider or "*"
103
101
if ra_class == "ocf":
104
l = os_types_list("%s/resource.d/%s/*" % (os.environ["OCF_ROOT"],prov))
102
l = os_types_list("%s/resource.d/%s/*" % (os.environ["OCF_ROOT"], prov))
105
103
elif ra_class == "lsb":
106
104
l = os_types_list("/etc/init.d/*")
107
105
elif ra_class == "stonith":
108
l = stdout2list("stonith -L")
106
rc, l = stdout2list("stonith -L")
108
# stonith(8) may not be installed
109
common_debug("stonith exited with code %d" % rc)
109
111
l.extend(os_types_list("/usr/sbin/fence_*"))
110
112
elif ra_class == "nagios":
111
113
l = os_types_list("%s/check_*" % vars.nagios_dir)
129
131
Get information from crm_resource.
131
return stdout2list("crm_resource %s" % opts, stderr_on=False)
132
def meta(self, ra_class,ra_type,ra_provider):
133
return self.crm_resource("--show-metadata %s:%s:%s"%(ra_class,ra_provider,ra_type))
134
def providers(self, ra_type,ra_class = "ocf"):
133
rc, s = stdout2list("crm_resource %s" % opts, stderr_on=False)
134
# not clear when/why crm_resource exits with non-zero
137
common_debug("crm_resource %s exited with code %d" % \
140
def meta(self, ra_class, ra_type, ra_provider):
141
return self.crm_resource("--show-metadata %s:%s:%s"%(ra_class, ra_provider, ra_type))
142
def providers(self, ra_type, ra_class="ocf"):
135
143
'List of providers for OCF:type.'
136
144
if ra_class != "ocf":
137
145
common_err("no providers for class %s" % ra_class)
141
149
'List of classes.'
142
150
l = self.crm_resource("--list-standards")
144
def types(self, ra_class = "ocf", ra_provider = ""):
152
def types(self, ra_class="ocf", ra_provider=""):
145
153
'List of types for a class.'
147
return self.crm_resource("--list-agents %s:%s" % (ra_class,ra_provider))
155
return self.crm_resource("--list-agents %s:%s" % (ra_class, ra_provider))
149
157
return self.crm_resource("--list-agents %s" % ra_class)
153
161
# after this glue release all users can get meta-data and
154
162
# similar from lrmd
155
163
minimum_glue = "1.0.10"
156
glue_ver = get_stdout("%s -v" % lrmadmin_prog, stderr_on = False)
164
rc, glue_ver = get_stdout("%s -v" % lrmadmin_prog, stderr_on = False)
157
165
if not glue_ver: #lrmadmin probably not found
159
167
v_min = version.LooseVersion(minimum_glue)
160
168
v_this = version.LooseVersion(glue_ver)
161
169
return v_this >= v_min or \
162
(getpwdent()[0] in ("root",vars.crm_daemon_user))
170
(getpwdent()[0] in ("root", vars.crm_daemon_user))
163
171
def crm_resource_support():
164
s = get_stdout("crm_resource --list-standards", stderr_on = False)
172
rc, s = get_stdout("crm_resource --list-standards", stderr_on = False)
182
190
return wcache.retrieve("ra_classes")
183
191
l = ra_if().classes()
185
return wcache.store("ra_classes",l)
186
def ra_providers(ra_type,ra_class = "ocf"):
193
return wcache.store("ra_classes", l)
194
def ra_providers(ra_type, ra_class="ocf"):
187
195
'List of providers for a class:type.'
188
id = "ra_providers-%s-%s" % (ra_class,ra_type)
196
id = "ra_providers-%s-%s" % (ra_class, ra_type)
189
197
if wcache.is_cached(id):
190
198
return wcache.retrieve(id)
191
l = ra_if().providers(ra_type,ra_class)
199
l = ra_if().providers(ra_type, ra_class)
193
return wcache.store(id,l)
194
def ra_providers_all(ra_class = "ocf"):
201
return wcache.store(id, l)
202
def ra_providers_all(ra_class="ocf"):
196
204
List of providers for a class.
201
209
dir = "%s/resource.d" % os.environ["OCF_ROOT"]
203
211
for s in os.listdir(dir):
204
if os.path.isdir("%s/%s" % (dir,s)):
212
if os.path.isdir("%s/%s" % (dir, s)):
207
return wcache.store(id,l)
208
def ra_types(ra_class = "ocf", ra_provider = ""):
215
return wcache.store(id, l)
216
def ra_types(ra_class="ocf", ra_provider=""):
210
218
List of RA type for a class.
214
id = "ra_types-%s-%s" % (ra_class,ra_provider)
222
id = "ra_types-%s-%s" % (ra_class, ra_provider)
215
223
if wcache.is_cached(id):
216
224
return wcache.retrieve(id)
218
226
for ra in ra_if().types(ra_class):
219
227
if (not ra_provider or \
220
ra_provider in ra_providers(ra,ra_class)) \
228
ra_provider in ra_providers(ra, ra_class)) \
221
229
and ra not in list:
224
return wcache.store(id,list)
232
return wcache.store(id, list)
226
234
def get_pe_meta():
227
235
if not vars.pe_metadata:
263
271
if is_program(prog):
264
l = stdout2list("%s metadata" % prog)
272
rc, l = stdout2list("%s metadata" % prog)
274
common_debug("%s metadata exited with code %d" % (prog, rc))
266
def get_nodes_text(n,tag):
268
node = n.getElementsByTagName(tag)[0]
269
for c in node.childNodes:
270
if c.nodeType == c.TEXT_NODE:
271
return c.data.strip()
277
def get_nodes_text(n, tag):
278
try: return n.findtext(tag).strip()
272
279
except: return ''
274
def mk_monitor_name(role,depth):
281
def mk_monitor_name(role, depth):
275
282
depth = depth != "0" and ("_%s" % depth) or ""
276
283
return role and role != "Started" and \
277
"monitor_%s%s" % (role,depth) or \
284
"monitor_%s%s" % (role, depth) or \
278
285
"monitor%s" % depth
279
286
def monitor_name_node(node):
280
depth = node.getAttribute("depth") or '0'
281
role = node.getAttribute("role")
282
return mk_monitor_name(role,depth)
287
depth = node.get("depth") or '0'
288
role = node.get("role")
289
return mk_monitor_name(role, depth)
283
290
def monitor_name_pl(pl):
284
291
depth = find_value(pl, "depth") or '0'
285
292
role = find_value(pl, "role")
286
return mk_monitor_name(role,depth)
293
return mk_monitor_name(role, depth)
288
295
class RAInfo(object):
293
300
required_ops = ("start", "stop")
294
301
skip_ops = ("meta-data", "validate-all")
295
302
skip_op_attr = ("name", "depth", "role")
296
def __init__(self,ra_class,ra_type,ra_provider = "heartbeat"):
303
def __init__(self, ra_class, ra_type, ra_provider="heartbeat"):
297
304
self.advanced_params = []
298
305
self.ra_class = ra_class
299
306
self.ra_type = ra_type
300
307
self.ra_provider = ra_provider
301
308
if not self.ra_provider:
302
309
self.ra_provider = "heartbeat"
304
311
def ra_string(self):
305
312
return self.ra_class == "ocf" and \
306
313
"%s:%s:%s" % (self.ra_class, self.ra_provider, self.ra_type) or \
316
323
def set_advanced_params(self, l):
317
324
self.advanced_params = l
318
325
def filter_crmd_attributes(self):
319
for n in self.ra_node.getElementsByTagName("parameter"):
320
if not n.getAttribute("name") in vars.crmd_user_attributes:
321
n.parentNode.removeChild(n)
322
def add_ra_params(self,ra):
326
for p in self.ra_elem.xpath("//parameters/parameter"):
327
if not p.get("name") in vars.crmd_user_attributes:
328
self.ra_elem.remove(p)
329
def add_ra_params(self, ra):
324
331
Add parameters from another RAInfo instance.
327
if not self.mk_ra_node() or not ra.mk_ra_node():
334
if self.mk_ra_node() is None or ra.mk_ra_node() is None:
332
params_node = self.doc.getElementsByTagName("parameters")[0]
339
params_node = self.ra_elem.findall("parameters")[0]
334
params_node = self.doc.createElement("parameters")
335
self.ra_node.appendChild(params_node)
336
for n in ra.ra_node.getElementsByTagName("parameter"):
337
params_node.appendChild(self.doc.importNode(n,1))
341
params_node = etree.SubElement(self.ra_elem, "parameters")
342
for n in ra.ra_elem.xpath("//parameters/parameter"):
343
params_node.append(copy.deepcopy(n))
338
344
def mk_ra_node(self):
340
346
Return the resource_agent node.
348
if self.ra_elem is not None:
344
350
meta = self.meta()
346
self.doc = xml.dom.minidom.parseString('\n'.join(meta))
348
self.error("could not parse meta-data: %s" % '\n'.join(meta))
352
self.ra_node = self.doc.getElementsByTagName("resource-agent")[0]
352
self.ra_elem = etree.fromstring('\n'.join(meta))
353
assert(self.ra_elem.tag == 'resource-agent')
354
except Exception, msg:
354
356
self.error("meta-data contains no resource-agent element")
357
359
if self.ra_class == "stonith":
358
360
self.add_ra_params(get_stonithd_meta())
360
def param_type_default(self,n):
362
def param_type_default(self, n):
362
content = n.getElementsByTagName("content")[0]
363
type = content.getAttribute("type")
364
default = content.getAttribute("default")
364
content = n.find("content")
365
type = content.get("type")
366
default = content.get("default")
368
370
def params(self):
370
372
Construct a dict of dicts: parameters are keys and
373
375
id = "ra_params-%s" % self.ra_string()
374
376
if wcache.is_cached(id):
375
377
return wcache.retrieve(id)
376
if not self.mk_ra_node():
378
if self.mk_ra_node() is None:
379
for pset in self.ra_node.getElementsByTagName("parameters"):
380
for c in pset.getElementsByTagName("parameter"):
381
name = c.getAttribute("name")
384
required = c.getAttribute("required")
385
unique = c.getAttribute("unique")
386
type,default = self.param_type_default(c)
388
"required": required,
393
return wcache.store(id,d)
381
for c in self.ra_elem.xpath("//parameters/parameter"):
385
required = c.get("required")
386
unique = c.get("unique")
387
type, default = self.param_type_default(c)
389
"required": required,
394
return wcache.store(id, d)
394
395
def completion_params(self):
396
397
Extra method for completion, for we want to filter some
397
398
(advanced) parameters out. And we want this to be fast.
399
if not self.mk_ra_node():
400
if self.mk_ra_node() is None:
401
return [c.getAttribute("name")
402
for c in self.ra_node.getElementsByTagName("parameter")
403
if c.getAttribute("name")
404
and c.getAttribute("name") not in self.advanced_params
402
return [c.get("name")
403
for c in self.ra_elem.xpath("//parameters/parameter")
405
and c.get("name") not in self.advanced_params
406
407
def actions(self):
411
412
id = "ra_actions-%s" % self.ra_string()
412
413
if wcache.is_cached(id):
413
414
return wcache.retrieve(id)
414
if not self.mk_ra_node():
415
if self.mk_ra_node() is None:
417
for pset in self.ra_node.getElementsByTagName("actions"):
418
for c in pset.getElementsByTagName("action"):
419
name = c.getAttribute("name")
420
if not name or name in self.skip_ops:
418
for c in self.ra_elem.xpath("//actions/action"):
420
if not name or name in self.skip_ops:
422
if name == "monitor":
423
name = monitor_name_node(c)
425
for a in c.attrib.keys():
426
if a in self.skip_op_attr:
422
if name == "monitor":
423
name = monitor_name_node(c)
425
for a in c.attributes.keys():
426
if a in self.skip_op_attr:
428
v = c.getAttribute(a)
431
431
# add monitor ops without role, if they don't already
466
466
if p in ("action", "port"):
469
def sanity_check_params(self, id, pl, existence_only = False):
469
def sanity_check_params(self, id, pl, existence_only=False):
471
pl is a list of (attribute,value) pairs.
471
pl is a list of (attribute, value) pairs.
472
472
- are all required parameters defined
473
473
- do all parameters exist
479
479
if not existence_only:
480
480
for p in self.reqd_params_list():
481
481
if self.unreq_param(p):
484
common_err("%s: required parameter %s not defined" % (id,p))
484
common_err("%s: required parameter %s not defined" % (id, p))
485
485
rc |= user_prefs.get_check_rc()
487
487
if p.startswith("$"):
488
488
# these are special, non-RA parameters
490
490
if p not in self.params():
491
common_err("%s: parameter %s does not exist" % (id,p))
491
common_err("%s: parameter %s does not exist" % (id, p))
492
492
rc |= user_prefs.get_check_rc()
494
def get_adv_timeout(self, op, node = None):
495
if node and op == "monitor":
494
def get_adv_timeout(self, op, node=None):
495
if node is not None and op == "monitor":
496
496
name = monitor_name_node(node)
524
524
if self.ra_class == "stonith" and op in ("start", "stop"):
526
526
if op not in self.actions():
527
common_warn("%s: action %s not advertised in meta-data, it may not be supported by the RA" % (id,op))
527
common_warn("%s: action %s not advertised in meta-data, it may not be supported by the RA" % (id, op))
529
529
if "interval" in n_ops[op]:
530
530
v = n_ops[op]["interval"]
531
531
v_msec = crm_msec(v)
532
532
if op in ("start", "stop") and v_msec != 0:
533
common_warn("%s: Specified interval for %s is %s, it must be 0" %(id,op,v))
533
common_warn("%s: Specified interval for %s is %s, it must be 0" %(id, op, v))
535
535
if op.startswith("monitor") and v_msec != 0:
536
536
if v_msec not in intervals:
550
550
timeout_string = "default timeout"
551
551
if crm_msec(v) < 0:
553
if crm_time_cmp(adv_timeout,v) > 0:
553
if crm_time_cmp(adv_timeout, v) > 0:
554
554
common_warn("%s: %s %s for %s is smaller than the advised %s" % \
555
(id,timeout_string,v,op,adv_timeout))
555
(id, timeout_string, v, op, adv_timeout))
565
565
if self.ra_class in vars.meta_progs:
566
566
l = prog_meta(self.ra_class)
568
l = ra_if().meta(self.ra_class,self.ra_type,self.ra_provider)
568
l = ra_if().meta(self.ra_class, self.ra_type, self.ra_provider)
569
569
self.debug("read and cached meta-data")
570
570
return wcache.store(id, l)
571
571
def meta_pretty(self):
573
573
Print the RA meta-data in a human readable form.
575
if not self.mk_ra_node():
575
if self.mk_ra_node() is None:
578
578
title = self.meta_title()
580
longdesc = get_nodes_text(self.ra_node,"longdesc")
580
longdesc = get_nodes_text(self.ra_elem,"longdesc")
582
582
l.append(longdesc)
583
583
if self.ra_class != "heartbeat":
589
589
l.append(actions)
590
590
return '\n\n'.join(l)
591
def get_shortdesc(self,n):
592
name = n.getAttribute("name")
591
def get_shortdesc(self, n):
593
593
shortdesc = get_nodes_text(n,"shortdesc")
594
594
longdesc = get_nodes_text(n,"longdesc")
595
if shortdesc and shortdesc not in (name,longdesc,self.ra_type):
595
if shortdesc and shortdesc not in (name, longdesc, self.ra_type):
598
598
def meta_title(self):
599
599
s = self.ra_string()
600
shortdesc = self.get_shortdesc(self.ra_node)
600
shortdesc = self.get_shortdesc(self.ra_elem)
602
s = "%s (%s)" % (shortdesc,s)
602
s = "%s (%s)" % (shortdesc, s)
604
def meta_param_head(self,n):
605
name = n.getAttribute("name")
604
def meta_param_head(self, n):
609
if n.getAttribute("required") == "1":
609
if n.get("required") == "1":
611
type,default = self.param_type_default(n)
611
type, default = self.param_type_default(n)
612
612
if type and default:
613
s = "%s (%s, [%s])" % (s,type,default)
613
s = "%s (%s, [%s])" % (s, type, default)
615
s = "%s (%s)" % (s,type)
615
s = "%s (%s)" % (s, type)
616
616
shortdesc = self.get_shortdesc(n)
617
s = "%s: %s" % (s,shortdesc)
617
s = "%s: %s" % (s, shortdesc)
619
def format_parameter(self,n):
619
def format_parameter(self, n):
621
621
head = self.meta_param_head(n)
626
626
longdesc = get_nodes_text(n,"longdesc")
628
longdesc = self.ra_tab + longdesc.replace("\n","\n"+self.ra_tab) + '\n'
628
longdesc = self.ra_tab + longdesc.replace("\n", "\n"+self.ra_tab) + '\n'
629
629
l.append(longdesc)
630
630
return '\n'.join(l)
631
def meta_parameter(self,param):
632
if not self.mk_ra_node():
631
def meta_parameter(self, param):
632
if self.mk_ra_node() is None:
635
for pset in self.ra_node.getElementsByTagName("parameters"):
636
for c in pset.getElementsByTagName("parameter"):
637
if c.getAttribute("name") == param:
638
return self.format_parameter(c)
634
for c in self.ra_elem.xpath("//parameters/parameter"):
635
if c.get("name") == param:
636
return self.format_parameter(c)
639
637
def meta_parameters(self):
640
if not self.mk_ra_node():
638
if self.mk_ra_node() is None:
643
for pset in self.ra_node.getElementsByTagName("parameters"):
644
for c in pset.getElementsByTagName("parameter"):
645
s = self.format_parameter(c)
641
for c in self.ra_elem.xpath("//parameters/parameter"):
642
s = self.format_parameter(c)
649
646
return "Parameters (* denotes required, [] the default):\n\n" + '\n'.join(l)
650
def meta_action_head(self,n):
651
name = n.getAttribute("name")
647
def meta_action_head(self, n):
654
651
if name in self.skip_ops:
656
653
if name == "monitor":
657
654
name = monitor_name_node(n)
658
655
s = "%-13s" % name
659
for a in n.attributes.keys():
656
for a in n.attrib.keys():
660
657
if a in self.skip_op_attr:
662
v = n.getAttribute(a)
664
s = "%s %s=%s" % (s,a,v)
661
s = "%s %s=%s" % (s, a, v)
666
663
def meta_actions(self):
668
for aset in self.ra_node.getElementsByTagName("actions"):
669
for c in aset.getElementsByTagName("action"):
670
s = self.meta_action_head(c)
672
l.append(self.ra_tab + s)
665
for c in self.ra_elem.xpath("//actions/action"):
666
s = self.meta_action_head(c)
668
l.append(self.ra_tab + s)
674
670
return "Operations' defaults (advisory minimum):\n\n" + '\n'.join(l)
677
ra_type = node.getAttribute("type")
678
ra_class = node.getAttribute("class")
679
ra_provider = node.getAttribute("provider")
680
return RAInfo(ra_class,ra_type,ra_provider)
673
ra_type = el.get("type")
674
ra_class = el.get("class")
675
ra_provider = el.get("provider")
676
return RAInfo(ra_class, ra_type, ra_provider)
683
679
# resource type definition
706
702
if not l or len(l) > 3:
710
706
elif len(l) == 2:
707
ra_class, ra_type = l
716
712
if ra_class == "ocf":
717
pl = ra_providers(ra_type,ra_class)
713
pl = ra_providers(ra_type, ra_class)
718
714
if pl and len(pl) == 1:
719
715
ra_provider = pl[0]
721
717
ra_provider = 'heartbeat'
722
return ra_class,ra_provider,ra_type
718
return ra_class, ra_provider, ra_type
724
720
wcache = WCache.getInstance()
725
721
vars = Vars.getInstance()