1# Copyright (C) 2008-2011 Dejan Muhamedagic <dmuhamedagic@suse.de> 2# See COPYING for license information. 3 4import os 5import subprocess 6import copy 7import re 8import glob 9from lxml import etree 10from . import cache 11from . import constants 12from . import config 13from . import options 14from . import userdir 15from . import utils 16from .utils import stdout2list, is_program, is_process, to_ascii 17from .utils import os_types_list, get_stdout, find_value 18from .utils import crm_msec, crm_time_cmp 19from .msg import common_debug, common_err, common_warn, common_info 20 21# 22# Resource Agents interface (meta-data, parameters, etc) 23# 24 25lrmadmin_prog = "lrmadmin" 26 27 28def lrmadmin(opts, xml=False): 29 """ 30 Get information directly from lrmd using lrmadmin. 31 """ 32 _rc, l = stdout2list("%s %s" % (lrmadmin_prog, opts)) 33 if l and not xml: 34 l = l[1:] # skip the first line 35 return l 36 37 38def crm_resource(opts): 39 ''' 40 Get information from crm_resource. 41 ''' 42 _rc, l = stdout2list("crm_resource %s" % opts, stderr_on=False) 43 return l 44 45 46@utils.memoize 47def can_use_lrmadmin(): 48 from distutils import version 49 # after this glue release all users can get meta-data and 50 # similar from lrmd 51 minimum_glue = "1.0.10" 52 _rc, glue_ver = get_stdout("%s -v" % lrmadmin_prog, stderr_on=False) 53 if not glue_ver: # lrmadmin probably not found 54 return False 55 v_min = version.LooseVersion(minimum_glue) 56 v_this = version.LooseVersion(glue_ver) 57 if v_this < v_min: 58 return False 59 if userdir.getuser() not in ("root", config.path.crm_daemon_user): 60 return False 61 if not (is_program(lrmadmin_prog) and is_process(pacemaker_execd())): 62 return False 63 return utils.ext_cmd(">/dev/null 2>&1 %s -C" % lrmadmin_prog) == 0 64 65 66@utils.memoize 67def can_use_crm_resource(): 68 _rc, s = get_stdout("crm_resource --list-standards", stderr_on=False) 69 return s != "" 70 71 72def ra_classes(): 73 ''' 74 List of RA classes. 75 ''' 76 if cache.is_cached("ra_classes"): 77 return cache.retrieve("ra_classes") 78 if can_use_crm_resource(): 79 l = crm_resource("--list-standards") 80 elif can_use_lrmadmin(): 81 l = lrmadmin("-C") 82 else: 83 l = ["heartbeat", "lsb", "nagios", "ocf", "stonith", "systemd"] 84 l.sort() 85 return cache.store("ra_classes", l) 86 87 88def ra_providers(ra_type, ra_class="ocf"): 89 'List of providers for a class:type.' 90 ident = "ra_providers-%s-%s" % (ra_class, ra_type) 91 if cache.is_cached(ident): 92 return cache.retrieve(ident) 93 if can_use_crm_resource(): 94 if ra_class != "ocf": 95 common_err("no providers for class %s" % ra_class) 96 return [] 97 l = crm_resource("--list-ocf-alternatives %s" % ra_type) 98 elif can_use_lrmadmin(): 99 l = lrmadmin("-P %s %s" % (ra_class, ra_type), True) 100 else: 101 l = [] 102 if ra_class == "ocf": 103 for s in glob.glob("%s/resource.d/*/%s" % (os.environ["OCF_ROOT"], ra_type)): 104 a = s.split("/") 105 if len(a) == 7: 106 l.append(a[5]) 107 l.sort() 108 return cache.store(ident, l) 109 110 111def ra_providers_all(ra_class="ocf"): 112 ''' 113 List of providers for a class. 114 ''' 115 if ra_class != "ocf": 116 return [] 117 ident = "ra_providers_all-%s" % ra_class 118 if cache.is_cached(ident): 119 return cache.retrieve(ident) 120 ocf = os.path.join(os.environ["OCF_ROOT"], "resource.d") 121 if os.path.isdir(ocf): 122 return cache.store(ident, sorted(s for s in os.listdir(ocf) 123 if os.path.isdir(os.path.join(ocf, s)))) 124 return [] 125 126 127def os_types(ra_class): 128 'List of types for a class.' 129 def stonith_types(): 130 rc, l = stdout2list("stonith -L") 131 if rc != 0: 132 # stonith(8) may not be installed 133 common_debug("stonith exited with code %d" % rc) 134 l = [] 135 for ra in os_types_list("/usr/sbin/fence_*"): 136 if ra not in ("fence_ack_manual", "fence_pcmk", "fence_legacy"): 137 l.append(ra) 138 return l 139 140 def systemd_types(): 141 l = [] 142 rc, lines = stdout2list("systemctl list-unit-files --full") 143 if rc != 0: 144 return l 145 t = re.compile(r'^(.+)\.service') 146 for line in lines: 147 m = t.search(line) 148 if m: 149 l.append(m.group(1)) 150 return l 151 152 l = [] 153 if ra_class == "ocf": 154 l = os_types_list("%s/resource.d/*/*" % (os.environ["OCF_ROOT"])) 155 elif ra_class == "lsb": 156 l = os_types_list("/etc/init.d/*") 157 elif ra_class == "stonith": 158 l = stonith_types() 159 elif ra_class == "nagios": 160 l = [x.replace("check_", "") 161 for x in os_types_list("%s/check_*" % config.path.nagios_plugins)] 162 elif ra_class == "systemd": 163 l = systemd_types() 164 l = list(set(l)) 165 l.sort() 166 return l 167 168 169def ra_types(ra_class="ocf", ra_provider=""): 170 ''' 171 List of RA type for a class. 172 ''' 173 174 def find_types(): 175 """ 176 Actually go out and ask for the types of a class. 177 """ 178 if can_use_crm_resource(): 179 l = crm_resource("--list-agents %s" % ra_class) 180 elif can_use_lrmadmin(): 181 l = lrmadmin("-T %s" % ra_class) 182 else: 183 l = os_types(ra_class) 184 return l 185 186 if not ra_class: 187 ra_class = "ocf" 188 ident = "ra_types-%s-%s" % (ra_class, ra_provider) 189 if cache.is_cached(ident): 190 return cache.retrieve(ident) 191 192 if not ra_provider: 193 def include(ra): 194 return True 195 else: 196 def include(ra): 197 return ra_provider in ra_providers(ra, ra_class) 198 return cache.store(ident, sorted(list(set(ra for ra in find_types() if include(ra))))) 199 200 201@utils.memoize 202def ra_meta(ra_class, ra_type, ra_provider): 203 """ 204 Return metadata for the given class/type/provider 205 """ 206 if can_use_crm_resource(): 207 if ra_provider: 208 return crm_resource("--show-metadata %s:%s:%s" % (ra_class, ra_provider, ra_type)) 209 return crm_resource("--show-metadata %s:%s" % (ra_class, ra_type)) 210 elif can_use_lrmadmin(): 211 return lrmadmin("-M %s %s %s" % (ra_class, ra_type, ra_provider), True) 212 else: 213 l = [] 214 if ra_class == "ocf": 215 _rc, l = stdout2list("%s/resource.d/%s/%s meta-data" % 216 (os.environ["OCF_ROOT"], ra_provider, ra_type)) 217 elif ra_class == "stonith": 218 if ra_type.startswith("fence_") and os.path.exists("/usr/sbin/%s" % ra_type): 219 _rc, l = stdout2list("/usr/sbin/%s -o metadata" % ra_type) 220 else: 221 _rc, l = stdout2list("stonith -m -t %s" % ra_type) 222 elif ra_class == "nagios": 223 _rc, l = stdout2list("%s/check_%s --metadata" % 224 (config.path.nagios_plugins, ra_type)) 225 return l 226 227 228@utils.memoize 229def get_pe_meta(): 230 return RAInfo(utils.pacemaker_schedulerd(), "metadata") 231 232 233@utils.memoize 234def get_crmd_meta(): 235 return RAInfo(utils.pacemaker_controld(), "metadata", 236 exclude_from_completion=constants.crmd_metadata_do_not_complete) 237 238 239@utils.memoize 240def get_stonithd_meta(): 241 return RAInfo(utils.pacemaker_fenced(), "metadata") 242 243 244@utils.memoize 245def get_cib_meta(): 246 return RAInfo(utils.pacemaker_based(), "metadata") 247 248 249@utils.memoize 250def get_properties_meta(): 251 meta = copy.deepcopy(get_crmd_meta()) 252 meta.add_ra_params(get_pe_meta()) 253 meta.add_ra_params(get_cib_meta()) 254 return meta 255 256 257@utils.memoize 258def get_properties_list(): 259 try: 260 return list(get_properties_meta().params().keys()) 261 except: 262 return [] 263 264 265def prog_meta(prog): 266 ''' 267 Do external program metadata. 268 ''' 269 prog = utils.pacemaker_daemon(prog) 270 if prog: 271 rc, l = stdout2list("%s metadata" % prog) 272 if rc == 0: 273 return l 274 common_debug("%s metadata exited with code %d" % (prog, rc)) 275 return [] 276 277 278def get_nodes_text(n, tag): 279 try: 280 return n.findtext(tag).strip() 281 except: 282 return '' 283 284 285def mk_monitor_name(role, depth): 286 depth = ("_%s" % depth) if depth != "0" else "" 287 return role and role != "Started" and \ 288 "monitor_%s%s" % (role, depth) or \ 289 "monitor%s" % depth 290 291 292def monitor_name_node(node): 293 depth = node.get("depth") or '0' 294 role = node.get("role") 295 return mk_monitor_name(role, depth) 296 297 298def monitor_name_pl(pl): 299 depth = find_value(pl, "depth") or '0' 300 role = find_value(pl, "role") 301 return mk_monitor_name(role, depth) 302 303 304def _param_type_default(n): 305 """ 306 Helper function to get (type, default) from XML parameter node 307 """ 308 try: 309 content = n.find("content") 310 return content.get("type"), content.get("default") 311 except: 312 return None, None 313 314 315class RAInfo(object): 316 ''' 317 A resource agent and whatever's useful about it. 318 ''' 319 ra_tab = " " # four horses 320 required_ops = ("start", "stop") 321 skip_ops = ("meta-data", "validate-all") 322 skip_op_attr = ("name", "depth", "role") 323 324 def __init__(self, ra_class, ra_type, ra_provider="heartbeat", exclude_from_completion=None): 325 self.excluded_from_completion = exclude_from_completion or [] 326 self.ra_class = ra_class 327 self.ra_type = ra_type 328 self.ra_provider = ra_provider 329 if ra_class == 'ocf' and not self.ra_provider: 330 self.ra_provider = "heartbeat" 331 self.ra_elem = None 332 self.broken_ra = False 333 334 def __str__(self): 335 return "%s:%s:%s" % (self.ra_class, self.ra_provider, self.ra_type) \ 336 if self.ra_class == "ocf" \ 337 else "%s:%s" % (self.ra_class, self.ra_type) 338 339 def error(self, s): 340 common_err("%s: %s" % (self, s)) 341 342 def warn(self, s): 343 common_warn("%s: %s" % (self, s)) 344 345 def info(self, s): 346 common_info("%s: %s" % (self, s)) 347 348 def debug(self, s): 349 common_debug("%s: %s" % (self, s)) 350 351 def add_ra_params(self, ra): 352 ''' 353 Add parameters from another RAInfo instance. 354 ''' 355 try: 356 if self.mk_ra_node() is None or ra.mk_ra_node() is None: 357 return 358 except: 359 return 360 try: 361 params_node = self.ra_elem.findall("parameters")[0] 362 except: 363 params_node = etree.SubElement(self.ra_elem, "parameters") 364 for n in ra.ra_elem.xpath("//parameters/parameter"): 365 params_node.append(copy.deepcopy(n)) 366 367 def mk_ra_node(self): 368 ''' 369 Return the resource_agent node. 370 ''' 371 if self.ra_elem is not None: 372 return self.ra_elem 373 # don't try again in vain 374 if self.broken_ra: 375 return None 376 self.broken_ra = True 377 meta = self.meta() 378 if meta is None: 379 if not config.core.ignore_missing_metadata: 380 self.error("got no meta-data, does this RA exist?") 381 return None 382 self.ra_elem = meta 383 try: 384 assert self.ra_elem.tag == 'resource-agent' 385 except Exception: 386 self.error("meta-data contains no resource-agent element") 387 return None 388 if self.ra_class == "stonith": 389 self.add_ra_params(get_stonithd_meta()) 390 self.broken_ra = False 391 return self.ra_elem 392 393 def params(self, completion=False): 394 ''' 395 Construct a dict of dicts: parameters are keys and 396 dictionary of attributes/values are values. Cached too. 397 398 completion: 399 If true, filter some (advanced) parameters out. 400 ''' 401 if completion: 402 if self.mk_ra_node() is None: 403 return None 404 return [c.get("name") 405 for c in self.ra_elem.xpath("//parameters/parameter") 406 if c.get("name") and c.get("name") not in self.excluded_from_completion] 407 ident = "ra_params-%s" % self 408 if cache.is_cached(ident): 409 return cache.retrieve(ident) 410 if self.mk_ra_node() is None: 411 return None 412 d = {} 413 for c in self.ra_elem.xpath("//parameters/parameter"): 414 name = c.get("name") 415 if not name: 416 continue 417 required = c.get("required") if not (c.get("deprecated") or c.get("obsoletes")) else "0" 418 unique = c.get("unique") 419 typ, default = _param_type_default(c) 420 d[name] = { 421 "required": required, 422 "unique": unique, 423 "type": typ, 424 "default": default, 425 } 426 return cache.store(ident, d) 427 428 def actions(self): 429 ''' 430 Construct a dict of dicts: actions are keys and 431 dictionary of attributes/values are values. Cached too. 432 ''' 433 ident = "ra_actions-%s" % self 434 if cache.is_cached(ident): 435 return cache.retrieve(ident) 436 if self.mk_ra_node() is None: 437 return None 438 d = {} 439 for c in self.ra_elem.xpath("//actions/action"): 440 name = c.get("name") 441 if not name or name in self.skip_ops: 442 continue 443 if name == "monitor": 444 name = monitor_name_node(c) 445 d[name] = {} 446 for a in list(c.attrib.keys()): 447 if a in self.skip_op_attr: 448 continue 449 v = c.get(a) 450 if v: 451 d[name][a] = v 452 # add monitor ops without role, if they don't already 453 # exist 454 d2 = {} 455 for op in d: 456 if re.match("monitor_[^0-9]", op): 457 norole_op = re.sub(r'monitor_[^0-9_]+_(.*)', r'monitor_\1', op) 458 if norole_op not in d: 459 d2[norole_op] = d[op] 460 d.update(d2) 461 return cache.store(ident, d) 462 463 def param_default(self, pname): 464 ''' 465 Parameter's default. 466 ''' 467 d = self.params() 468 try: 469 return d[pname]["default"] 470 except: 471 return None 472 473 def normalize_parameters(self, root): 474 """ 475 Find all instance_attributes/nvpair objects, 476 check if parameter exists. If not, normalize name 477 and check if THAT exists (replacing - with _). 478 If so, change the name of the parameter. 479 """ 480 params = self.params() 481 if not params: 482 return 483 for nvp in root.xpath("instance_attributes/nvpair"): 484 name = nvp.get("name") 485 if name is not None and name not in params: 486 name = name.replace("-", "_") 487 if name in params: 488 nvp.attrib["name"] = name 489 490 def sanity_check_params(self, ident, nvpairs, existence_only=False): 491 ''' 492 nvpairs is a list of <nvpair> tags. 493 - are all required parameters defined 494 - do all parameters exist 495 ''' 496 def reqd_params_list(): 497 ''' 498 List of required parameters. 499 ''' 500 d = self.params() 501 if not d: 502 return [] 503 return [x for x in d if d[x]["required"] == '1'] 504 505 def unreq_param(p): 506 ''' 507 Allow for some exceptions. 508 509 - the rhcs stonith agents sometimes require "action" (in 510 the meta-data) and "port", but they're automatically 511 supplied by stonithd 512 ''' 513 if self.ra_class == "stonith" and \ 514 (self.ra_type.startswith("rhcs/") or 515 self.ra_type.startswith("fence_")): 516 if p in ("action", "port"): 517 return True 518 return False 519 520 rc = 0 521 d = {} 522 for nvp in nvpairs: 523 if 'name' in nvp.attrib: 524 d[nvp.get('name')] = nvp.get('value') 525 if not existence_only: 526 for p in reqd_params_list(): 527 if unreq_param(p): 528 continue 529 if p not in d: 530 common_err("{}: required parameter \"{}\" not defined".format(ident, p)) 531 rc |= utils.get_check_rc() 532 for p in d: 533 if p.startswith("$"): 534 # these are special, non-RA parameters 535 continue 536 if p not in self.params(): 537 common_err("{}: parameter \"{}\" is not known".format(ident, p)) 538 rc |= utils.get_check_rc() 539 return rc 540 541 def get_adv_timeout(self, op, node=None): 542 if node is not None and op == "monitor": 543 name = monitor_name_node(node) 544 else: 545 name = op 546 try: 547 return self.actions()[name]["timeout"] 548 except: 549 return None 550 551 def sanity_check_ops(self, ident, ops, default_timeout): 552 ''' 553 ops is a list of operations 554 - do all operations exist 555 - are timeouts sensible 556 ''' 557 def sanity_check_op(op, n_ops, intervals): 558 """ 559 Helper method used by sanity_check_ops. 560 """ 561 rc = 0 562 if self.ra_class == "stonith" and op in ("start", "stop"): 563 return rc 564 if op not in self.actions(): 565 common_warn("%s: action '%s' not found in Resource Agent meta-data" % (ident, op)) 566 rc |= 1 567 if "interval" in n_ops[op]: 568 v = n_ops[op]["interval"] 569 v_msec = crm_msec(v) 570 if op in ("start", "stop") and v_msec != 0: 571 common_warn("%s: Specified interval for %s is %s, it must be 0" % (ident, op, v)) 572 rc |= 1 573 if op.startswith("monitor") and v_msec != 0: 574 if v_msec not in intervals: 575 intervals[v_msec] = 1 576 else: 577 common_warn("%s: interval in %s must be unique" % (ident, op)) 578 rc |= 1 579 try: 580 adv_timeout = self.actions()[op]["timeout"] 581 except: 582 return rc 583 if "timeout" in n_ops[op]: 584 v = n_ops[op]["timeout"] 585 timeout_string = "specified timeout" 586 else: 587 v = default_timeout 588 timeout_string = "default timeout" 589 if crm_msec(v) < 0: 590 return rc 591 if crm_time_cmp(adv_timeout, v) > 0: 592 common_warn("%s: %s %s for %s is smaller than the advised %s" % 593 (ident, timeout_string, v, op, adv_timeout)) 594 rc |= 1 595 return rc 596 597 rc = 0 598 n_ops = {} 599 for op in ops: 600 n_op = monitor_name_pl(op[1]) if op[0] == "monitor" else op[0] 601 n_ops[n_op] = {} 602 for p, v in op[1]: 603 if p in self.skip_op_attr: 604 continue 605 n_ops[n_op][p] = v 606 for req_op in self.required_ops: 607 if req_op not in n_ops: 608 if not (self.ra_class == "stonith" and req_op in ("start", "stop")): 609 n_ops[req_op] = {} 610 intervals = {} 611 for op in n_ops: 612 rc |= sanity_check_op(op, n_ops, intervals) 613 return rc 614 615 def meta(self): 616 ''' 617 RA meta-data as raw xml. 618 Returns an etree xml object. 619 ''' 620 sid = "ra_meta-%s" % self 621 if cache.is_cached(sid): 622 return cache.retrieve(sid) 623 if self.ra_class in constants.meta_progs: 624 l = prog_meta(self.ra_class) 625 elif self.ra_class in constants.meta_progs_20: 626 l = prog_meta(self.ra_class) 627 else: 628 l = ra_meta(self.ra_class, self.ra_type, self.ra_provider) 629 if not l: 630 return None 631 try: 632 xml = etree.fromstring('\n'.join(l)) 633 except Exception: 634 self.error("Cannot parse meta-data XML") 635 return None 636 self.debug("read and cached meta-data") 637 return cache.store(sid, xml) 638 639 def meta_pretty(self): 640 ''' 641 Print the RA meta-data in a human readable form. 642 ''' 643 if self.mk_ra_node() is None: 644 return '' 645 l = [] 646 title = self.meta_title() 647 l.append(title) 648 longdesc = get_nodes_text(self.ra_elem, "longdesc") 649 if longdesc: 650 l.append(longdesc) 651 if self.ra_class != "heartbeat": 652 params = self.meta_parameters() 653 if params: 654 l.append(params.rstrip()) 655 actions = self.meta_actions() 656 if actions: 657 l.append(actions) 658 return '\n\n'.join(l) 659 660 def get_shortdesc(self, n): 661 name = n.get("name") 662 shortdesc = get_nodes_text(n, "shortdesc") 663 longdesc = get_nodes_text(n, "longdesc") 664 if shortdesc and shortdesc not in (name, longdesc, self.ra_type): 665 return shortdesc 666 return '' 667 668 def meta_title(self): 669 s = str(self) 670 shortdesc = self.get_shortdesc(self.ra_elem) 671 if shortdesc: 672 s = "%s (%s)" % (shortdesc, s) 673 return s 674 675 def format_parameter(self, n): 676 def meta_param_head(): 677 name = n.get("name") 678 if not name: 679 return None 680 s = name 681 if n.get("required") == "1": 682 s = s + "*" 683 typ, default = _param_type_default(n) 684 if typ and default: 685 s = "%s (%s, [%s])" % (s, typ, default) 686 elif typ: 687 s = "%s (%s)" % (s, typ) 688 shortdesc = self.get_shortdesc(n) 689 s = "%s: %s" % (s, shortdesc) 690 return s 691 head = meta_param_head() 692 if not head: 693 self.error("no name attribute for parameter") 694 return "" 695 l = [head] 696 longdesc = get_nodes_text(n, "longdesc") 697 if longdesc: 698 l.append(self.ra_tab + longdesc.replace("\n", "\n" + self.ra_tab) + '\n') 699 return '\n'.join(l) 700 701 def meta_parameter(self, param): 702 if self.mk_ra_node() is None: 703 return '' 704 for c in self.ra_elem.xpath("//parameters/parameter"): 705 if c.get("name") == param: 706 return self.format_parameter(c) 707 708 def meta_parameters(self): 709 if self.mk_ra_node() is None: 710 return '' 711 l = [] 712 for c in self.ra_elem.xpath("//parameters/parameter"): 713 s = self.format_parameter(c) 714 if s: 715 l.append(s) 716 if l: 717 return "Parameters (*: required, []: default):\n\n" + '\n'.join(l) 718 719 def meta_actions(self): 720 def meta_action_head(n): 721 name = n.get("name") 722 if not name or name in self.skip_ops: 723 return '' 724 if name == "monitor": 725 name = monitor_name_node(n) 726 s = "%-13s" % name 727 for a in list(n.attrib.keys()): 728 if a in self.skip_op_attr: 729 continue 730 v = n.get(a) 731 if v: 732 s = "%s %s=%s" % (s, a, v) 733 return s 734 l = [] 735 for c in self.ra_elem.xpath("//actions/action"): 736 s = meta_action_head(c) 737 if s: 738 l.append(self.ra_tab + s) 739 if not l: 740 return None 741 return "Operations' defaults (advisory minimum):\n\n" + '\n'.join(l) 742 743 744def get_ra(r): 745 """ 746 Argument is either an xml resource tag with class, provider and type attributes, 747 or a CLI style class:provider:type string. 748 """ 749 if isinstance(r, str): 750 cls, provider, typ = disambiguate_ra_type(r) 751 else: 752 cls, provider, typ = r.get('class'), r.get('provider'), r.get('type') 753 # note order of arguments! 754 return RAInfo(cls, typ, provider) 755 756 757# 758# resource type definition 759# 760def ra_type_validate(s, ra_class, provider, rsc_type): 761 ''' 762 Only ocf ra class supports providers. 763 ''' 764 if not rsc_type: 765 common_err("bad resource type specification %s" % s) 766 return False 767 if ra_class == "ocf": 768 if not provider: 769 common_err("provider could not be determined for %s" % s) 770 return False 771 else: 772 if provider: 773 common_warn("ra class %s does not support providers" % ra_class) 774 return True 775 return True 776 777 778def pick_provider(providers): 779 ''' 780 Pick the most appropriate choice from a 781 list of providers, falling back to 782 'heartbeat' if no good choice is found 783 ''' 784 if not providers or 'heartbeat' in providers: 785 return 'heartbeat' 786 elif 'pacemaker' in providers: 787 return 'pacemaker' 788 return providers[0] 789 790 791def disambiguate_ra_type(s): 792 ''' 793 Unravel [class:[provider:]]type 794 ''' 795 l = s.split(':') 796 if not l or len(l) > 3: 797 return ["", "", ""] 798 if len(l) == 3: 799 return l 800 elif len(l) == 2: 801 cl, tp = l 802 else: 803 cl, tp = "ocf", l[0] 804 pr = pick_provider(ra_providers(tp, cl)) if cl == 'ocf' else '' 805 return cl, pr, tp 806 807 808def can_validate_agent(agent): 809 if utils.getuser() != 'root': 810 return False 811 if isinstance(agent, str): 812 c, p, t = disambiguate_ra_type(agent) 813 if c != "ocf": 814 return False 815 agent = RAInfo(c, t, p) 816 if agent.mk_ra_node() is None: 817 return False 818 if len(agent.ra_elem.xpath('.//actions/action[@name="validate-all"]')) < 1: 819 return False 820 return True 821 822 823def validate_agent(agentname, params, log=False): 824 """ 825 Call the validate-all action on the agent, given 826 the parameter hash params. 827 agent: either a c:p:t agent name, or an RAInfo instance 828 params: a hash of agent parameters 829 Returns: (rc, out) 830 """ 831 def find_agent(): 832 if not can_validate_agent(agentname): 833 return None 834 if isinstance(agentname, str): 835 c, p, t = disambiguate_ra_type(agentname) 836 if c != "ocf": 837 raise ValueError("Only OCF agents are supported by this command") 838 agent = RAInfo(c, t, p) 839 if agent.mk_ra_node() is None: 840 return None 841 else: 842 agent = agentname 843 if len(agent.ra_elem.xpath('.//actions/action[@name="validate-all"]')) < 1: 844 raise ValueError("validate-all action not supported by agent") 845 return agent 846 agent = find_agent() 847 if agent is None: 848 return (-1, "") 849 850 my_env = os.environ.copy() 851 my_env["OCF_ROOT"] = config.path.ocf_root 852 for k, v in params.items(): 853 my_env["OCF_RESKEY_" + k] = v 854 cmd = [os.path.join(config.path.ocf_root, "resource.d", agent.ra_provider, agent.ra_type), "validate-all"] 855 if options.regression_tests: 856 print(".EXT", " ".join(cmd)) 857 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=my_env) 858 _out, _ = p.communicate() 859 out = to_ascii(_out) 860 p.wait() 861 862 if log is True: 863 from . import msg as msglog 864 for msg in out.splitlines(): 865 if msg.startswith("ERROR: "): 866 msglog.err_buf.error(msg[7:]) 867 elif msg.startswith("WARNING: "): 868 msglog.err_buf.warning(msg[9:]) 869 elif msg.startswith("INFO: "): 870 msglog.err_buf.info(msg[6:]) 871 elif msg.startswith("DEBUG: "): 872 msglog.err_buf.debug(msg[7:]) 873 else: 874 msglog.err_buf.writemsg(msg) 875 return p.returncode, out 876 877 878DLM_RA_SCRIPTS = """ 879primitive {id} ocf:pacemaker:controld \ 880op start timeout=90 \ 881op stop timeout=100 \ 882op monitor interval=60 timeout=60""" 883FILE_SYSTEM_RA_SCRIPTS = """ 884primitive {id} ocf:heartbeat:Filesystem \ 885params directory="{mnt_point}" fstype="{fs_type}" device="{device}" \ 886op monitor interval=20 timeout=40 \ 887op start timeout=60 \ 888op stop timeout=60""" 889LVMLOCKD_RA_SCRIPTS = """ 890primitive {id} ocf:heartbeat:lvmlockd \ 891op start timeout=90 \ 892op stop timeout=100 \ 893op monitor interval=30 timeout=90""" 894LVMACTIVATE_RA_SCRIPTS = """ 895primitive {id} ocf:heartbeat:LVM-activate \ 896params vgname={vgname} vg_access_mode=lvmlockd activation_mode=shared \ 897op start timeout=90s \ 898op stop timeout=90s \ 899op monitor interval=30s timeout=90s""" 900GROUP_SCRIPTS = """ 901group {id} {ra_string}""" 902CLONE_SCRIPTS = """ 903clone {id} {group_id} meta interleave=true""" 904 905 906CONFIGURE_RA_TEMPLATE_DICT = { 907 "DLM": DLM_RA_SCRIPTS, 908 "Filesystem": FILE_SYSTEM_RA_SCRIPTS, 909 "LVMLockd": LVMLOCKD_RA_SCRIPTS, 910 "LVMActivate": LVMACTIVATE_RA_SCRIPTS, 911 "GROUP": GROUP_SCRIPTS, 912 "CLONE": CLONE_SCRIPTS 913 } 914# vim:ts=4:sw=4:et: 915