diff --git a/tools/hb2openais-helper.py b/tools/hb2openais-helper.py index 6fbb17dc26..8b693c13e6 100755 --- a/tools/hb2openais-helper.py +++ b/tools/hb2openais-helper.py @@ -1,383 +1,398 @@ #!/usr/bin/env python # Copyright (C) 2008 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import os,sys import getopt import xml.dom.minidom def usage(): print "usage: %s [-T] [-c ha_cf] {set_property |analyze_cib|convert_cib}"%sys.argv[0] sys.exit(1) TEST = False try: optlist, arglist = getopt.getopt(sys.argv[1:], "hTc:") except getopt.GetoptError: usage() for opt,arg in optlist: if opt == '-h': usage() elif opt == '-c': HA_CF = arg elif opt == '-T': TEST = True if len(arglist) < 1: usage() def load_cib(): doc = xml.dom.minidom.parse(sys.stdin) return doc def is_whitespace(node): return node.nodeType == node.TEXT_NODE and not node.data.strip() def rmnodes(node_list): for node in node_list: node.parentNode.removeChild(node) node.unlink() def set_id2uname(node_list): for node in node_list: id = node.getAttribute("id") uname = node.getAttribute("uname") if uname: node.setAttribute("id",uname) else: print >> sys.stderr, "WARNING: node %s has no uname attribute" % id def is_element(xmlnode): return xmlnode.nodeType == xmlnode.ELEMENT_NODE def xml_processnodes(xmlnode,filter,proc): ''' Process with proc all nodes that match filter. ''' node_list = [] for child in xmlnode.childNodes: if filter(child): node_list.append(child) elif child.hasChildNodes(): xml_processnodes(child,filter,proc) if node_list: proc(node_list) def skip_first(s): l = s.split('\n') return '\n'.join(l[1:]) def get_attribute(tag,node,p): attr_set = node.getElementsByTagName(tag) if not attr_set: return '' attributes = attr_set[0].getElementsByTagName("attributes") if not attributes: return '' attributes = attributes[0] for nvpair in attributes.getElementsByTagName("nvpair"): if p == nvpair.getAttribute("name"): return nvpair.getAttribute("value") return '' def get_param(node,p): return get_attribute("instance_attributes",node,p) def mknvpair(id,name,value): nvpair = doc.createElement("nvpair") nvpair.setAttribute("id",id + "-" + name) nvpair.setAttribute("name",name) nvpair.setAttribute("value",value) return nvpair def set_attribute(tag,node,p,value): id = node.getAttribute("id") attr_set = node.getElementsByTagName(tag) if not attr_set: return attributes = attr_set[0].getElementsByTagName("attributes") if not attributes: attributes = doc.createElement("attributes") attr_set.appendChild(attributes) else: attributes = attributes[0] for nvp in attributes.getElementsByTagName("nvpair"): if p == nvp.getAttribute("name"): nvp.setAttribute("value",value) return attributes.appendChild(mknvpair(id,p,value)) doc = load_cib() xml_processnodes(doc,is_whitespace,rmnodes) resources = doc.getElementsByTagName("resources")[0] constraints = doc.getElementsByTagName("constraints")[0] nodes = doc.getElementsByTagName("nodes")[0] crm_config = doc.getElementsByTagName("crm_config")[0] if not resources: print >> sys.stderr, "ERROR: sorry, no resources section in the CIB, cannot proceed" sys.exit(1) if not constraints: print >> sys.stderr, "ERROR: sorry, no constraints section in the CIB, cannot proceed" sys.exit(1) if not nodes: print >> sys.stderr, "ERROR: sorry, no nodes section in the CIB, cannot proceed" sys.exit(1) if arglist[0] == "set_node_ids": xml_processnodes(nodes,lambda x:1,set_id2uname) s = skip_first(doc.toprettyxml()) print s sys.exit(0) if arglist[0] == "set_property": if len(arglist) != 3: usage() set_attribute("cluster_property_set",crm_config,arglist[1],arglist[2]) s = skip_first(doc.toprettyxml()) print s sys.exit(0) if arglist[0] == "analyze_cib": rc = 0 for rsc in doc.getElementsByTagName("primitive"): rsc_type = rsc.getAttribute("type") if rsc_type == "EvmsSCC": print >> sys.stderr, "INFO: evms configuration found; conversion required" rc = 1 elif rsc_type == "Filesystem": if get_param(rsc,"fstype") == "ocfs2": print >> sys.stderr, "INFO: ocfs2 configuration found; conversion required" rc = 1 sys.exit(rc) def rm_attribute(tag,node,p): attr_set = node.getElementsByTagName(tag) if not attr_set: return '' attributes = attr_set[0].getElementsByTagName("attributes") if not attributes: return '' attributes = attributes[0] for nvpair in attributes.getElementsByTagName("nvpair"): if p == nvpair.getAttribute("name"): nvpair.parentNode.removeChild(nvpair) def set_param(node,p,value): set_attribute("instance_attributes",node,p,value) def rm_param(node,p): rm_attribute("instance_attributes",node,p) def evms2lvm(node,a): v = node.getAttribute(a) if v: v = v.replace("EVMS","LVM") v = v.replace("Evms","LVM") v = v.replace("evms","lvm") node.setAttribute(a,v) def replace_evms_strings(node_list): for node in node_list: evms2lvm(node,"id") if node.tagName in ("rsc_colocation","rsc_order"): evms2lvm(node,"to") evms2lvm(node,"from") def get_input(msg): if TEST: print >> sys.stderr, "%s: setting to /dev/null" % msg return "/dev/null" while True: ans = raw_input(msg) if ans: if os.access(ans,os.F_OK): return ans else: print >> sys.stderr, "Cannot read %s" % ans print >> sys.stderr, "We do need this input to continue." def mk_lvm(rsc_id,volgrp): node = doc.createElement("primitive") node.setAttribute("id",rsc_id) node.setAttribute("type","LVM") node.setAttribute("provider","heartbeat") node.setAttribute("class","ocf") operations = doc.createElement("operations") node.appendChild(operations) mon_op = doc.createElement("op") operations.appendChild(mon_op) mon_op.setAttribute("id", rsc_id + "_mon") mon_op.setAttribute("name","monitor") interval = "120s" timeout = "60s" mon_op.setAttribute("interval", interval) mon_op.setAttribute("timeout", timeout) instance_attributes = doc.createElement("instance_attributes") instance_attributes.setAttribute("id", rsc_id + "_inst_attr") node.appendChild(instance_attributes) attributes = doc.createElement("attributes") instance_attributes.appendChild(attributes) attributes.appendChild(mknvpair(rsc_id,"volgrpname",volgrp)) return node def mk_clone(id,ra_type,ra_class,prov): c = doc.createElement("clone") c.setAttribute("id",id + "-clone") meta = doc.createElement("meta_attributes") c.appendChild(meta) meta.setAttribute("id",id + "_meta") attributes = doc.createElement("attributes") meta.appendChild(attributes) attributes.appendChild(mknvpair(id,"globally-unique","false")) attributes.appendChild(mknvpair(id,"interleave","true")) p = doc.createElement("primitive") c.appendChild(p) p.setAttribute("id",id) p.setAttribute("type",ra_type) if prov: p.setAttribute("provider",prov) p.setAttribute("class",ra_class) operations = doc.createElement("operations") p.appendChild(operations) mon_op = doc.createElement("op") operations.appendChild(mon_op) mon_op.setAttribute("id", id + "_mon") mon_op.setAttribute("name","monitor") interval = "60s" timeout = "30s" mon_op.setAttribute("interval", interval) mon_op.setAttribute("timeout", timeout) return c def add_ocfs_clones(id): c1 = mk_clone("o2cb","o2cb","ocf","ocfs2") c2 = mk_clone("dlm","controld","ocf","pacemaker") resources.appendChild(c1) resources.appendChild(c2) c1 = mk_order("dlm-clone","o2cb-clone") c2 = mk_colocation("dlm-clone","o2cb-clone") constraints.appendChild(c1) constraints.appendChild(c2) def mk_order(r1,r2): rsc_order = doc.createElement("rsc_order") rsc_order.setAttribute("id","rsc_order_"+r1+"_"+r2) rsc_order.setAttribute("from",r1) rsc_order.setAttribute("to",r2) rsc_order.setAttribute("type","before") rsc_order.setAttribute("symmetrical","true") return rsc_order def mk_colocation(r1,r2): rsc_colocation = doc.createElement("rsc_colocation") rsc_colocation.setAttribute("id","rsc_colocation_"+r1+"_"+r2) rsc_colocation.setAttribute("from",r1) rsc_colocation.setAttribute("to",r2) rsc_colocation.setAttribute("score","INFINITY") return rsc_colocation def add_ocfs_constraints(rsc,id): node = rsc.parentNode if node.tagName != "clone": node = rsc clone_id = node.getAttribute("id") c1 = mk_order("o2cb-clone",clone_id) c2 = mk_colocation("o2cb-clone",clone_id) constraints.appendChild(c1) constraints.appendChild(c2) def change_ocfs2_device(rsc): print >> sys.stderr, "The current device for ocfs2 depends on evms: %s"%get_param(rsc,"device") dev = get_input("Please supply the device where %s ocfs2 resource resides: "%rsc.getAttribute("id")) set_param(rsc,"device",dev) def stop_ocfs2(rsc): node = rsc.parentNode if node.tagName != "clone": node = rsc id = node.getAttribute("id") l = rsc.getElementsByTagName("meta_attributes") if l: meta = l[0] else: meta = doc.createElement("meta_attributes") meta.setAttribute("id",id + "_meta") node.appendChild(meta) attributes = doc.createElement("attributes") meta.appendChild(attributes) rm_param(rsc,"target_role") set_attribute("meta_attributes",node,"target_role","Stopped") def new_pingd_rsc(options,host_list): rsc_id = "pingd" c = mk_clone(rsc_id,"pingd","ocf","pacemaker") node = c.getElementsByTagName("primitive")[0] instance_attributes = doc.createElement("instance_attributes") instance_attributes.setAttribute("id", rsc_id + "_inst_attr") node.appendChild(instance_attributes) attributes = doc.createElement("attributes") instance_attributes.appendChild(attributes) - attributes.appendChild(mknvpair(rsc_id,"options",options)) + if options: + attributes.appendChild(mknvpair(rsc_id,"options",options)) + set_param(node,"host_list",host_list) return c +def new_cloned_rsc(rsc_class,rsc_provider,rsc_type): + return mk_clone(rsc_type,rsc_type,rsc_class,rsc_provider) def replace_evms_ids(): return c -def handle_pingd_respawn(): +def find_respawn(prog): + rc = False + f = open(HA_CF or "/etc/ha.d/ha.cf", 'r') + for l in f: + s = l.split() + if not s: + continue + if s[0] == "respawn" and s[2].find(prog) > 0: + rc = True + break + f.close() + return rc +def parse_pingd_respawn(): f = open(HA_CF or "/etc/ha.d/ha.cf", 'r') opts = '' ping_list = [] for l in f: s = l.split() if not s: continue if s[0] == "respawn" and s[2].find("pingd") > 0: opts = ' '.join(s[3:]) elif s[0] == "ping": ping_list.append(s[1]) f.close() return opts,' '.join(ping_list) def process_cib(): ocfs_clones = [] evms_present = False for rsc in doc.getElementsByTagName("primitive"): rsc_id = rsc.getAttribute("id") rsc_type = rsc.getAttribute("type") if rsc_type == "Evmsd": print >> sys.stderr, "INFO: Evmsd resource %s will change type to clvmd"%rsc_id rsc.setAttribute("type","clvmd") rsc.setAttribute("provider","lvm2") print >> sys.stderr, "INFO: adding constraints for %s"%rsc_id add_ocfs_constraints(rsc,rsc_id) elif rsc_type == "EvmsSCC": evms_present = True print >> sys.stderr, "INFO: EvmsSCC resource is going to be replaced by LVM" vg = get_input("Please supply the VG name corresponding to %s: "%rsc_id) node = mk_lvm(rsc_id,vg) parent = rsc.parentNode parent.removeChild(rsc) parent.appendChild(node) rsc.unlink() - elif rsc_type == "pingd": - if pingd_host_list: - set_param(rsc,"host_list",pingd_host_list) elif rsc_type == "Filesystem": if get_param(rsc,"fstype") == "ocfs2": if get_param(rsc,"device").find("evms") > 0: change_ocfs2_device(rsc) ocfs_clones.append(rsc) id = rsc.getAttribute("id") print >> sys.stderr, "INFO: adding constraints for %s"%id add_ocfs_constraints(rsc,id) print >> sys.stderr, "INFO: adding target_role=Stopped to %s"%id stop_ocfs2(rsc) if ocfs_clones: print >> sys.stderr, "INFO: adding required cloned resources for ocfs2" add_ocfs_clones(id) if evms_present: xml_processnodes(doc,lambda x:1,replace_evms_strings) if arglist[0] == "convert_cib": - opts,pingd_host_list = handle_pingd_respawn() - if opts: + opts,pingd_host_list = parse_pingd_respawn() + if pingd_host_list: clone = new_pingd_rsc(opts,pingd_host_list) resources.appendChild(clone) + if find_respawn("evmsd"): + resources.appendChild(new_cloned_rsc("ocf","lvm2","clvmd")) process_cib() s = skip_first(doc.toprettyxml()) print s sys.exit(0) # shouldn't get here usage() # vim:ts=4:sw=4:et: diff --git a/tools/hb2openais.sh.in b/tools/hb2openais.sh.in index 054801f96d..943abcdca4 100755 --- a/tools/hb2openais.sh.in +++ b/tools/hb2openais.sh.in @@ -1,743 +1,752 @@ #!/bin/sh # Copyright (C) 2008 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # . @sysconfdir@/ha.d/shellfuncs . $HA_NOARCHBIN/utillib.sh . $HA_NOARCHBIN/ha_cf_support.sh PROG=`basename $0` # FIXME: once this is part of the package! PROGDIR=`dirname $0` echo "$PROGDIR" | grep -qs '^/' || { test -f @sbindir@/$PROG && PROGDIR=@sbindir@ test -f $HA_NOARCHBIN/$PROG && PROGDIR=$HA_NOARCHBIN } # the default syslog facility is not (yet) exported by heartbeat # to shell scripts # DEFAULT_HA_LOGFACILITY="daemon" export DEFAULT_HA_LOGFACILITY AIS_CONF=/etc/ais/openais.conf AIS_KEYF=/etc/ais/authkey AUTHENTICATION=on MAXINTERFACE=2 MCASTPORT=5405 RRP_MODE=active +SUPPORTED_RESPAWNS="pingd evmsd" PY_HELPER=$HA_BIN/hb2openais-helper.py CRM_VARLIB=$HA_VARLIB/crm CIB=$CRM_VARLIB/cib.xml CIBSIG=$CRM_VARLIB/cib.xml.sig CIBLAST=$CRM_VARLIB/cib.xml.last CIBLAST_SIG=$CRM_VARLIB/cib.xml.sig.last HOSTCACHE=$HA_VARLIB/hostcache HB_UUID=$HA_VARLIB/hb_uuid DONE_F=$HA_VARRUN/heartbeat/.$PROG.conv_done BACKUPDIR=/var/tmp/`basename $PROG .sh`.backup RM_FILES=" $CIBSIG $HOSTCACHE $HB_UUID $CIBLAST $CIBLAST_SIG" REMOTE_RM_FILES=" $CIB $RM_FILES" BACKUP_FILES=" $AIS_CONF $AIS_KEYF $REMOTE_RM_FILES " DIST_FILES=" $AIS_CONF $AIS_KEYF $DONE_F " MAN_TARF=/var/tmp/`basename $PROG .sh`.tar.gz : ${SSH_OPTS="-T"} usage() { cat</dev/null else ssh -T -o Batchmode=yes $1 true 2>/dev/null fi } findsshuser() { for u in "" $TRY_SSH; do rc=0 for n in `getnodes`; do [ "$node" = "$WE" ] && continue testsshuser $n $u || { rc=1 break } done if [ $rc -eq 0 ]; then echo $u return 0 fi done return 1 } important() { echo "IMPORTANT: $*" >&2 } newportinfo() { important "the multicast port number on $1 is set to $2" important "please update your firewall rules (if any)" } changemediainfo() { important "openais uses multicast for communication" important "please make sure that your network infrastructure supports it" } multicastinfo() { info "multicast for openais ring $1 set to $2:$3" } netaddrinfo() { info "network address for openais ring $1 set to $2" } backup_files() { [ "$TEST_DIR" ] && return info "backing up $BACKUP_FILES to $BACKUPDIR" $DRY mkdir $BACKUPDIR || { echo sorry, could not create $BACKUPDIR directory echo please cleanup exit 1 } if [ -z "$DRY" ]; then tar cf - $BACKUP_FILES | gzip > $BACKUPDIR/$WE.tar.gz || { echo sorry, could not create $BACKUPDIR/$WE.tar.gz exit 1 } else $DRY "tar cf - $BACKUP_FILES | gzip > $BACKUPDIR/$WE.tar.gz" fi } revert() { [ "$TEST_DIR" ] && return test -d $BACKUPDIR || { echo sorry, there is no $BACKUPDIR directory echo cannot revert exit 1 } info "restoring $BACKUP_FILES from $BACKUPDIR/$WE.tar.gz" gzip -dc $BACKUPDIR/$WE.tar.gz | (cd / && tar xf -) || { echo sorry, could not unpack $BACKUPDIR/$WE.tar.gz exit 1 } } pls_press_enter() { [ "$TEST_DIR" ] && return cat</dev/null | prochbmedia 2>/dev/null | sort -u | wc -l` if [ $mediacnt -ge 2 ]; then setvalue rrp_mode $RRP_MODE fi changemediainfo endstanza # the logging stanza getlogvars # enforce some syslog facility debugsetting=`setdebug` newstanza logging setvalue debug $debugsetting setvalue fileline off setvalue to_stderr no if [ "$HA_LOGFILE" ]; then setvalue to_file yes setvalue logfile $HA_LOGFILE else setvalue to_file no fi if [ "$HA_LOGFACILITY" ]; then setvalue to_syslog yes setvalue syslog_facility $HA_LOGFACILITY else setvalue to_syslog no fi endstanza newstanza amf setvalue mode disabled endstanza } if [ -z "$DRY" ]; then openaisconf > $AIS_CONF || fatal "cannot create $AIS_CONF" grep -wqs interface $AIS_CONF || fatal "no media found in $HA_CF" else openaisconf fi [ "$AIS_KEYF" ] && if [ "$TEST_DIR" ]; then info "Skipping OpenAIS authentication key generation ..." else info "Generating a key for OpenAIS authentication ..." $DRY ais-keygen || fatal "cannot generate the key using ais-keygen" fi # remove various files which could get in a way if [ -z "$TEST_DIR" ]; then $DRY rm -f $RM_FILES fi fixcibperms() { [ "$TEST_DIR" ] && return uid=`ls -ldn $CRM_VARLIB | awk '{print $3}'` gid=`ls -ldn $CRM_VARLIB | awk '{print $4}'` $DRY $MYSUDO chown $uid:$gid $CIB } upgrade_cib() { $DRY $MYSUDO cibadmin --upgrade --force } # remove the nodes section from the CIB tmpfile=`maketempfile` $MYSUDO sh -c "python $PY_HELPER set_node_ids <$CIB >$tmpfile" || fatal "cannot set nodes's ids in the CIB" $DRY $MYSUDO mv $tmpfile $CIB info "Edited the nodes's ids in the CIB" numnodes=`getnodes | wc -w` if [ $numnodes -eq 2 ]; then # set tmpfile=`maketempfile` $MYSUDO sh -c "python $PY_HELPER set_property no-quorum-policy ignore <$CIB >$tmpfile" || fatal "cannot set the no-quorum-policy property" $DRY $MYSUDO mv $tmpfile $CIB info "Since this is a 2-node cluster," info "the no-quorum-policy cluster property was set to ignore." fi tmpfile=`maketempfile` $MYSUDO sh -c "python $PY_HELPER set_property expected-nodes $numnodes <$CIB >$tmpfile" || fatal "cannot set the expected-nodes property" $DRY $MYSUDO mv $tmpfile $CIB info "the expected-nodes cluster property was set to $numnodes." info "Done converting ha.cf to openais.conf" important "Please check the resulting $AIS_CONF" important "and in particular interface stanzas and logging." important "If you find problems, please edit $AIS_CONF now!" # # first part done (openais), on to the CIB analyze_cib() { info "Analyzing the CIB..." $MYSUDO sh -c "python $PY_HELPER analyze_cib <$CIB" } +check_respawns() { + rc=1 + for p in $SUPPORTED_RESPAWNS; do + grep -qs "^respawn.*$p" $HA_CF && { + info "a $p resource has to be created" + rc=0 + } + done + return $rc +} part2() { intro_part2 || return 0 tmpfile=`maketempfile` opts="-c $HA_CF" [ "$TEST_DIR" ] && opts="-T $opts" $MYSUDO sh -c "python $PY_HELPER $opts convert_cib <$CIB >$tmpfile" || fatal "failed to process the CIB" $DRY $MYSUDO mv $tmpfile $CIB info "Processed the CIB successfully" } # make the user believe that something's happening :) some_dots_idle() { cnt=0 printf "$2 ." while [ $cnt -lt $1 ]; do sleep 1 printf "." ctn=$((cnt+1)) done echo } print_dc() { crm_mon -1 | awk '/Current DC/{print $3}' } dcidle() { dc=`$MYSUDO print_dc` if [ "$dc" = "$WE" ]; then maxcnt=60 cnt=0 while [ $cnt -lt $maxcnt ]; do stat=`$MYSUDO crmadmin -S $dc` echo $stat | grep -qs S_IDLE && break [ "$1" = "-v" ] && echo $stat sleep 1 printf "." cnt=$((cnt+1)) done echo $stat | grep -qs S_IDLE else some_dots_idle 10 #just wait for 10 seconds fi } wait_crm() { cnt=10 dc="" while [ -z "$dc" -a $cnt -gt 0 ]; do dc=`$MYSUDO print_dc` cnt=$((cnt-1)) done if [ x = x"$dc" ]; then echo "sorry, no dc found/elected" exit 1 fi dcidle } start_cluster() { $DRY /etc/init.d/openais start } tune_ocfs2() { [ "$TEST_DIR" ] && return cat< $MAN_TARF) fi