#!/usr/bin/python

import getopt, sys
import socket, ssl
from xml.dom import minidom
import logging
import os
import os.path
import re
import threading
import time, random
from Queue import Queue
import getpass

RICCI_PORT = 11111
CLUSTERRNG = "cluster.rng.in"

password = None
debug = False
sync = False
usefile = False
hostname = False
filename = False
activate = False

class StopNodeThread (threading.Thread):
    def __init__ (self, host):
        self.host = host
        threading.Thread.__init__(self)

    def getoutput(self):
        return self.output

    def run (self):
        self.output = stop_node(self.host,True)
        if self.output == 0:
            self.output = "Stopped %s" % self.host

class StartNodeThread (threading.Thread):
    def __init__ (self, host):
        self.host = host
        threading.Thread.__init__(self)
        self.output = ""

    def getoutput(self):
        return self.output

    def run (self):
        self.output = start_node(self.host,True)
        if self.output == 0:
            self.output = "Started %s" % self.host

def main(argv):
    getconf = sendconf = False
    status = start = stop = startall = stopall = False
    listnodes = listservices = listdomains = False
    addnode = removenode = getversion = False
    incrementversion = setversion = False
    addmethod = removemethod = createcluster = False
    addfencedev = removefencedev = False
    addfenceinst = removefenceinst = False
    lsfencedev = lsfenceinst = False
    addfailoverdomain = removefailoverdomain = False
    lsfailoverdomain = addfailoverdomainnode = removefailoverdomainnode = False
    addservice = addsubservice = addresource = False
    removeservice = removesubservice = removeresource = False
    listquorum = setquorumd = addheuristic = removeheuristic = False
    settotem = setrm = setcman = setfencedaemon = False
    setlogging = addlogger = removelogger = False
    listmisc = False
    setdlm = setmulticast = False
    passwordset = False
    nodeid = votes = False
    checkconf = False
    exp = exprm = False

    global password, debug, sync, hostname, usefile, filename, activate
#    logging.basicConfig(level=logging.DEBUGRemove
    try:
        opts, args = getopt.getopt(argv, "dsh:p:f:", ["help","host=","getconf","status","start","stop","lsnodes",
      "lsservices", "listdomains", "addnode=", "rmnode=", "getversion","setversion=","incversion",
      "createcluster=", "password=", "addmethod=", "rmmethod=", "addfencedev=", "rmfencedev=",
      "addfenceinst=", "rmfenceinst=", "lsfencedev", "lsfenceinst", "sync", "addfailoverdomain=",
      "rmfailoverdomain=", "addservice=", "rmservice=", "addsubservice=", "rmsubservice=", "addresource=",
      "rmresource=", "setquorumd","addheuristic","settotem", "setrm", "setcman", "setfencedaemon",
      "setlogging", "addlogger","rmlogger","setmulticast=","sync","addfailoverdomainnode=", "file=", "nodeid=",
      "votes=", "rmfailoverdomainnode=", "setdlm", "rmheuristic", "startall", "stopall", "activate", "setconf", "lsquorum", "lsfailoverdomain", "lsmisc", "checkconf", "exp=", "exprm="])
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for opt, arg in opts:
        if opt == ("--help"): usage() ; sys.exit()
        elif opt in ("--host","-h"):
            hostname = arg
            logging.debug("Hostname = %s" % hostname)
        elif opt in ("--file","-f"):
            usefile = True
            filename = arg
            logging.debug("Filename = %s" % filename)
        elif opt in ("--password", "-p"): passwordset = True ; password = arg
        elif opt in ("--getconf"): getconf = True
        elif opt in ("--checkconf"): checkconf = True
        elif opt in ("--setconf"): sendconf = True
        elif opt in ("--status"): status = True
        elif opt in ("--start"): start = True
        elif opt in ("--stop"): stop = True
        elif opt in ("--startall"): startall = True
        elif opt in ("--stopall"): stopall = True
        elif opt in ("--lsnodes"): listnodes = True
        elif opt in ("--lsservices"): listservices = True
        elif opt in ("--listdomains"): listdomains = True
        elif opt in ("--addnode"): addnode = True ; node = arg 
        elif opt in ("--rmnode"): removenode = True ; node = arg 
        elif opt in ("--getversion"): getversion = True
        elif opt in ("--setversion"): setversion = True ; version = arg 
        elif opt in ("--incversion"): incrementversion = True
        elif opt in ("--sync"): sync = True
        elif opt in ("--activate"): activate = True
        elif opt in ("--createcluster"):
            createcluster = True
            clustername = arg
        elif opt in ("--addmethod"):
            addmethod = True
            method = arg
            options = args
        elif opt in ("--rmmethod"):
            removemethod = True
            method = arg
            options = args
        elif opt in ("--addfencedev"):
            addfencedev = True
            name = arg
            options = args
        elif opt in ("--rmfencedev"): removefencedev = True; name = arg
        elif opt in ("--addfenceinst"):
            addfenceinst = True
            name = arg
            options = args
        elif opt in ("--rmfenceinst"):
            removefenceinst = True
            name = arg
            options = args
        elif opt in ("--lsfencedev"): lsfencedev = True;
        elif opt in ("--lsfenceinst"): lsfenceinst = True; options = args
        elif opt in ("--lsfailoverdomain"): lsfailoverdomain = True
        elif opt in ("--addfailoverdomain"):
            addfailoverdomain = True
            name = arg
            options = args
        elif opt in ("--rmfailoverdomain"):
            removefailoverdomain = True
            name = arg
        elif opt in ("--addfailoverdomainnode"):
            addfailoverdomainnode = True
            name = arg
            options = args
        elif opt in ("--rmfailoverdomainnode"):
            removefailoverdomainnode = True
            name = arg
            options = args
        elif opt in ("--addservice"):
            addservice = True
            name = arg
            options = args
        elif opt in ("--rmservice"):
            removeservice = True
            name = arg
        elif opt in ("--addsubservice"):
            addsubservice = True
            name = arg
            options = args
        elif opt in ("--rmsubservice"):
            removesubservice = True
            name = arg
            options = args
        elif opt in ("--addresource"):
            addresource = True
            name = arg
            options = args
        elif opt in ("--rmresource"):
            removeresource = True
            name = arg
            options = args
        elif opt in ("--lsquorumd"): listquorum = True
        elif opt in ("--setquorumd"): setquorumd = True; options = args
        elif opt in ("--addheuristic"): addheuristic = True; options = args
        elif opt in ("--rmheuristic"): removeheuristic = True; options = args
        elif opt in ("--lsmisc"): listmisc = True
        elif opt in ("--settotem"): settotem = True; options = args
        elif opt in ("--setrm"): setrm = True; options = args
        elif opt in ("--setcman"): setcman = True; options = args
        elif opt in ("--setdlm"): setdlm = True; options = args
        elif opt in ("--setfencedaemon"): setfencedaemon = True; options = args
        elif opt in ("--setlogging"): setlogging = True; options = args
        elif opt in ("--addlogger"): addlogger = True; options = args
        elif opt in ("--rmlogger"): removelogger = True; options = args
        elif opt in ("--setmulticast"): setmulticast = True
        elif opt in ("--nodeid"): nodeid = arg
        elif opt in ("--votes"): votes = arg
        elif opt in ("-d"): debug = True
      	elif opt in ("--exp"): exp = True; tag = arg; options = args
      	elif opt in ("--exprm"): exprm = True; location = arg

    if not hostname and not filename:
        print "Must specify a hostname or filename."
        sys.exit(2)

    if (getconf): get_cluster_conf()
    if (sendconf): send_cluster_conf()
    if (checkconf): check_cluster_conf()
    if (status): get_cluster_status()
    if (stop): stop_node(hostname) 
    if (start): start_node(hostname)
    if (startall): start_all()
    if (stopall): stop_all()
    if (listnodes): list_nodes()
    if (listservices): list_services()
    if (listdomains): list_domains()
    if (addnode): add_node(node, nodeid, votes)
    if (removenode): remove_node(node)
    if (getversion): print get_version()
    if (setversion): set_version(version)
    if (incrementversion): increment_version()
    if (createcluster): create_cluster(clustername)
    if (addmethod): add_method(method, options)
    if (removemethod): remove_method(method, options)
    if (addfencedev): add_fencedev(name, options)
    if (removefencedev): remove_fencedev(name)
    if (addfenceinst): add_fenceinst(name, options)
    if (removefenceinst): remove_fenceinst(name, options)
    if (lsfencedev): list_fencedev()
    if (lsfenceinst): list_fenceinst(options)
    if (lsfailoverdomain): list_failoverdomain()
    if (addfailoverdomain): add_failoverdomain(name, options)
    if (removefailoverdomain): remove_failoverdomain(name)
    if (addfailoverdomainnode): add_failoverdomainnode(name, options)
    if (removefailoverdomainnode): remove_failoverdomainnode(name, options)
    if (addservice): add_service(name, options)
    if (removeservice): remove_service(name)
    if (addsubservice): add_subservice(name, options)
    if (removesubservice): remove_subservice(name, options)
    if (addresource): add_resource(name, options)
    if (removeresource): remove_resource(name, options)
    if (listquorum): list_quorum()
    if (setquorumd): set_quorumd(options)
    if (addheuristic): add_heuristic(options)
    if (removeheuristic): remove_heuristic(options)
    if (listmisc): list_misc()
    if (settotem): set_totem(options)
    if (setrm): set_rm(options)
    if (setcman): set_cman(options)
    if (setdlm): set_dlm(options)
    if (setfencedaemon): set_fencedaemon(options)
    if (setlogging): set_logging(options)
    if (addlogger): add_logger(options)
    if (removelogger): remove_logger(options)
    if (setmulticast): set_multicast(arg)
    if (exp): expert_mode(tag, options)
    if (exprm): expert_mode_remove(location)
    if (sync): sync_cluster_conf()

def usage():
    print """Usage: ccs [OPTION]...
Cluster configuration system.

      --help            Display this help and exit
  -h, --host host       Cluster node to perform actions on
  -f, --file file       File to perform actions on
  -p, --password        Ricci user password for node running ricci
      --getconf         Print current cluster.conf file
      --setconf         Use the file specified by '-f' to send to the host
                        specified with '-h'
      --checkconf       If file is specified, verify that all the nodes in the
                        file have the same cluster.conf as the file.  If a
                        host is specified then verify that all nodes in the
                        host's cluster.conf file have the identical
                        cluster.conf file
                        same cluster.conf
      --sync            Sync config file to all nodes
      --activate        Activate config on node (use this option with --sync
                        to activate config on all nodes)
      --debug           Display debugging information to help troubleshoot
                        connection issues with ricci
      --exp tag [location] [options]
                        Expert mode to add elements not currently defined in
                        ccs (see man page for more information)
      --exprm location
                        Expert mode to remove elements not currently defined in
                        ccs (see man page for more information)

Cluster Operations:
      --createcluster <cluster>
                        Create a new cluster.conf (removing old one if it
                                                   exists)
      --getversion      Get the current cluster.conf version
      --setversion <n>  Set the cluster.conf version
      --incversion      Increment the cluster.conf version by 1
                        scp and put them in the current directory
      --startall        Start cluster services on all nodes
      --stopall         Stop cluster services on all nodes
      --start           Start cluster services on host specified with -h
      --stop            Stop cluster services on host specified with -h

Node Operations:
      --lsnodes         List all nodes in the cluster
      --addnode <node>  Add node <node> to the cluster
      --rmnode <node>
                        Remove a node from the cluster
      --nodeid <nodeid> Specify nodeid when adding a node
      --votes <votes>   Specify number of votes for a node

Fencing Operations:
      --lsfencedev      List all of the fence devices configured
      --lsfenceinst [<node>]
                        List all of the fence methods and instances on the
                        specified node or all nodes if no node is specified
      --addmethod <method> <node>
                        Add a fence method to a specific node
      --rmmethod <method> <node>
                        Remove a fence method from a specific node
      --addfencedev <device name> [fence device options]
                        Add fence device
      --rmfencedev <fence device name>
                        Remove fence device
      --addfenceinst <fence device name> <node> <method> <options>
                        Add fence instance
      --rmfenceinst <fence device name> <node> <method>
                        Remove all instances of the fence device listed from
                        the given method and node

Failover Domain Operations:
      --lsfailoverdomain
                        Lists all of the failover domains and failover domain
                        nodes configured in the cluster
      --addfailoverdomain <name> [restricted] [ordered] [nofailback]
                        Add failover domain
      --rmfailoverdomain <name>
                        Remove failover domain
      --addfailoverdomainnode <failover domain> <node> [priority=xx]
                        Add node to given failover domain
      --rmfailoverdomainnode <failover domain> <node>
                        Remove node from failover domain

Service Operations:
      --lsservices      List currently configured services and resources in
                        the cluster
      --addresource <resource type> [resource options] ...
                        Add resources to the cluster
      --rmresource <resource type> [resource options]
                        Remove specified resource with resource options
      --addservice <servicename> [service options] ...
                        Add service to cluster
      --rmservice <servicename>
                        Removes a service and all of its subservices
      --addsubservice <servicename> <subservice> [service options] ...
                        Add individual sub services, if adding services within
                        a tree, use ':' to separate elements and brackets to
                        identify subservices of the same type
                        Example: --addsubservice service_a nfs[1]:nfs[2]:nfs
      --rmsubservice <servicename> <subservice>
                        Removes a specific subservice specified by the
                        subservice, using ':' to separate elements and
                        brackets to identify between subservices of the
                        same type.

Quorum Operations:
      --lsquorum        List quorum options and heuristics
      --setquorumd [quorumd options] ...
                        Add quorumd options
      --addheuristic [heuristic options] ...
                        Add heuristics to quorumd
      --rmheuristic [heuristic options] ...
                        Remove heuristic specified by heurstic options

Misc Options
      --lsmisc          List all of the misc options
      --settotem [totem options]
                        Set totem options
      --setdlm [dlm options]
                        Set dlm options
      --setrm [resource manager options]
                        Set resource manager options
      --setcman [cman options]
                        Set cman options
      --setmulticast [multicast address]
                        Set's the multicast address to use (or removes it
                        if no multicast address is given)
      --setfencedaemon [fence daemon options]
                        Set fence daemon options
      --setlogging [logging options]
                        Set logging options
      --addlogger [logger options]
                        Add a logger
      --rmlogger [logger options]
                        Remove a logger
  """

def stop_node(node, inthread=False):
    xml = send_ricci_command("cluster", "stop_node", node)

    dom = minidom.parseString(xml)
    vars = dom.getElementsByTagName('var')
    for var in vars:
        if var.getAttribute("name") != "success":
            continue
        if not var.hasAttribute("value"):
            myerr = "Unable to get valid response from ricci for %s." % node
            if inthread:
                return myerr
            else:
                print myerr
                sys.exit(1)
        if var.getAttribute("value") == "false":
            myerr = "Unable to stop %s." % node
            if inthread:
                return myerr
            else:
                print myerr
                sys.exit(1)

        # If it succeeds print nothing and return 0
        if var.getAttribute("value") == "true":
            return 0

    myerr = "Unable to get valid response from ricci for %s." % node
    if inthread:
        return myerr
    else:
        print myerr
        sys.exit(1)
    
def start_node(node, inthread=False):
    error_description = ""
    xml = send_ricci_command("cluster", "start_node", node)

    dom = minidom.parseString(xml)
    vars = dom.getElementsByTagName('var')
    for var in vars:
        if var.getAttribute("name") == "error_description":
            error_description = var.getAttribute("value")
            break

    for var in vars:
        if var.getAttribute("name") != "success":
            continue

        if not var.hasAttribute("value"):
            myerr = "Unable to get valid response from ricci for %s." % node
            if inthread:
                return myerr
            else:
                print myerr
                sys.exit(1)

        if var.getAttribute("value") == "false":
            myerr = "Unable to start %s, possibly do to lack of quorum, try --startall\n" % node
            myerr += "Error: " +error_description
            if inthread:
                return myerr
            else:
                print myerr
                sys.exit(1)

        # If it succeeds print nothing and return 0
        if var.getAttribute("value") == "true":
            return 0

    myerr = "Unable to get valid response from ricci for %s." % node
    if inthread:
        return myerr
    else:
        print myerr
        sys.exit(1)

def start_all(nodelist = None):
    if nodelist == None:
        nodelist = get_nodelist()
    threads = {}

    for node in nodelist:
        threads[node] = StartNodeThread(node)
        threads[node].start()

    for thread in threads.values():
        thread.join()
        print thread.getoutput()

def stop_all(nodelist = None):
    if nodelist == None:
        nodelist = get_nodelist()
    threads = {}

    for node in nodelist:
        threads[node] = StopNodeThread(node)
        threads[node].start()

    for thread in threads.values():
        thread.join()
        print thread.getoutput()

def get_nodelist():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    nodelist = []
    for node in dom.getElementsByTagName('clusternode'):
        nodelist.append(node.getAttribute("name"))
    return nodelist

def get_cluster_conf():
    xml = get_cluster_conf_xml()
    xml = minidom.parseString(xml).getElementsByTagName('cluster')[0].toprettyxml(indent='  ',newl='')
    print xml

def get_cluster_status():
    xml = send_ricci_command("cluster","status")
    xml = minidom.parseString(xml).getElementsByTagName('cluster')[0].toprettyxml(indent='  ',newl='')
    print xml

def sync_cluster_conf():
    global hostname
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml).getElementsByTagName('cluster')[0]
    xml = dom.toprettyxml(indent='  ',newl='')
    dom = minidom.parseString(xml)
    for node in dom.getElementsByTagName('clusternode'):
        nodename = node.getAttribute("name")
        hostname = nodename
        set_cluster_conf(xml)

def list_nodes():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    for node in dom.getElementsByTagName('clusternode'):
        print node.getAttribute("name") + ": " + getNodeAttr(node, "name")

def list_services():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    for node in dom.getElementsByTagName('service'):
        print_services_map(node,0)
    for node in dom.getElementsByTagName('resources'):
        print_services_map(node,0)

def print_services_map(node,level):
    num_spaces = level * 2
    prefix = ""
    
    if node.nodeType == minidom.Node.TEXT_NODE:
        return
    for i in range(num_spaces):
        prefix = " " + prefix

    nodeattr = getNodeAttr(node)

    print prefix + node.tagName + ": " + nodeattr
    for cn in node.childNodes:
        print_services_map(cn, level + 1)

def getNodeAttr(node, ignore = []):
    nodeattr = ""
    if node.attributes != None:
        length = node.attributes.length
        for i in range(length):
            if node.attributes.item(i).name not in ignore:
                nodeattr = nodeattr + node.attributes.item(i).name + "=" + node.attributes.item(i).value + ", "
    nodeattr = re.sub(r", $","", nodeattr)
    return nodeattr

def list_domains():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    for node in dom.getElementsByTagName('failoverdomain'):
        print node.getAttribute("name")

# Add a node to the cluster.conf
#   Before adding a node we need to verify another node
#   with the same name doesn't already exist
def add_node(node_to_add,nodeid,votes):
    nodeid_list = set()

    if nodeid and nodeid.isdigit(): nodeid = int(nodeid)
    else: nodeid = False

    if votes and votes.isdigit(): votes = int(votes)
    else: votes = False

    dom = minidom.parseString(get_cluster_conf_xml())
    for node in dom.getElementsByTagName('clusternode'):
        if (node.getAttribute("name") == node_to_add):
            print "Node '%s' already exists in cluster.conf" % node_to_add
            sys.exit(1)
        if nodeid and (node.getAttribute("nodeid") == str(nodeid)):
            print "Nodeid '%d' already exists in cluster.conf" % nodeid
            sys.exit(1)
        nodeid_list.add(node.getAttribute("nodeid"))
    node = dom.createElement("clusternode")
    node.setAttribute("name",node_to_add)

    # Use the first nodeid above 0 that isn't already used
    if nodeid == False:
        nodeid = 0
        while (True):
            nodeid = nodeid + 1
            if (str(nodeid) not in nodeid_list): break

    node.setAttribute("nodeid",str(nodeid))
    if votes: node.setAttribute("votes",str(votes))
    dom.getElementsByTagName("clusternodes")[0].appendChild(node)
    set_cluster_conf(dom.toxml())
    print "Node %s added." % (node_to_add)

def remove_node(node_to_remove):
    nodeFound = False

    dom = minidom.parseString(get_cluster_conf_xml())
    for node in dom.getElementsByTagName('clusternode'):
        if (node.getAttribute("name") == node_to_remove):
            node.parentNode.removeChild(node)
            nodeFound = True

    if (nodeFound == False):
        print "Unable to find node %s" % node_to_remove
        sys.exit(1)

    set_cluster_conf(dom.toxml())

def get_version():
    dom = minidom.parseString(get_cluster_conf_xml())
    return dom.getElementsByTagName('cluster')[0].getAttribute("config_version")

def set_version(version):
    dom = minidom.parseString(get_cluster_conf_xml())
    dom.getElementsByTagName('cluster')[0].setAttribute("config_version",version)
    set_cluster_conf(dom.toxml(), False)

def increment_version():
    new_version = int(get_version()) + 1
    set_version(str(new_version))
    print new_version

def get_cluster_conf_xml():
    if usefile:
        try:
            f = open(filename, 'r')
            xml = f.read()
            f.close()
        except IOError:
            print "Unable to open file: %s" % filename
            sys.exit(1)
    else:
        xml = send_ricci_command("cluster", "get_cluster.conf")

    dom = minidom.parseString(xml)
    if dom.getElementsByTagName('cluster').length > 0:
      return dom.getElementsByTagName('cluster')[0].toxml()
    else:
      return empty_cluster_conf()

# Create a minimal cluster.conf file similiar to the one
# created by system-config-cluster
def empty_cluster_conf(name="cluster"):
    impl = minidom.getDOMImplementation()
    newdoc = impl.createDocument(None, "cluster", None)

    top = newdoc.documentElement
    top.setAttribute('config_version','1')
    top.setAttribute('name',name)
    fence_daemon = newdoc.createElement("fence_daemon")
    clusternodes = newdoc.createElement("clusternodes")
    cman = newdoc.createElement("cman")
    fencedevices = newdoc.createElement("fencedevices")
    rm = newdoc.createElement("rm")
    failoverdomains = newdoc.createElement("failoverdomains")
    resources = newdoc.createElement("resources")
    
    top.appendChild(fence_daemon)
    top.appendChild(clusternodes)
    top.appendChild(cman)
    top.appendChild(fencedevices)
    rm.appendChild(failoverdomains)
    rm.appendChild(resources)
    top.appendChild(rm)

    return newdoc.toprettyxml()

def create_cluster(clustername):
    xml = empty_cluster_conf(clustername)
    # No need to increment on cluster.conf creation
    set_cluster_conf(xml, False)

def add_method(method, options):
    method_found = False
    node_found = False

    if len(options) != 1:
        usage()
        sys.exit(2)

    nodename = options[0]

    dom = minidom.parseString(get_cluster_conf_xml())
    for node in dom.getElementsByTagName('clusternode'):
        if (node.getAttribute("name") == nodename):
            node_found = True
            for methodnode in node.getElementsByTagName("method"):
                if methodnode.getAttribute("name") == method:
                    method_found = True
                    break
            break

    if node_found == False:
        print "Node '%s' does not currently exist in cluster.conf." % (nodename)
        sys.exit(1)

    if method_found == True:
        print "Method '%s' already exists in cluster.conf." % (method)
        sys.exit(1)

    fencenodes = node.getElementsByTagName("fence")
    if len(fencenodes) == 0:
        fencenode = dom.createElement("fence")
    else:
        fencenode = fencenodes[0]

    methodnode = dom.createElement("method")
    methodnode.setAttribute("name", method)
    fencenode.appendChild(methodnode)
    node.appendChild(fencenode)
    set_cluster_conf(dom.toxml())
    print "Method %s added to %s." % (method, nodename)

def remove_method(method, options):
    method_found = False
    node_found = False
    
    if len(options) != 1:
        usage()
        sys.exit(2)

    nodename = options[0]
    dom = minidom.parseString(get_cluster_conf_xml())
    for node in dom.getElementsByTagName('clusternode'):
        if (node.getAttribute("name") == nodename):
            node_found = True
            for methodnode in node.getElementsByTagName("method"):
                if methodnode.getAttribute("name") == method:
                    method_found = True
                    break
            break

    if node_found == False:
        print "Node %s does not exist in cluster.conf." % (nodename)
        sys.exit(1)
    if method_found == False:
        print "Method %s is not present for %s." % (method, nodename)
        sys.exit(1)

    methodnode.parentNode.removeChild(methodnode)
    set_cluster_conf(dom.toxml())
    print "Method %s removed from %s." % (method, nodename)

def add_fencedev(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify fencedevices section exists in cluster.conf
    fencedevices = dom.getElementsByTagName('fencedevices')
    if len(fencedevices) == 0:
        dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("fencedevices"))
    elif len(fencedevices) > 1:
        print "Error: Too many fencedevices elements in cluster.conf"
        sys.exit(1)
    
    # Verify fence device with same name does not already exist
    for fencedev in dom.getElementsByTagName('fencedevice'):
        if fencedev.getAttribute("name") == name:
            print "Fence device '%s' already exists in cluster.conf." % name
            sys.exit(1)

    newfencedev = dom.createElement("fencedevice")
    newfencedev = set_element_attributes(newfencedev, options)

    newfencedev.setAttribute("name", name)
    fencedevelem = dom.getElementsByTagName('fencedevices')[0]
    fencedevelem.appendChild(newfencedev)
    set_cluster_conf(dom.toxml())

def remove_fencedev(name):
    fencedev_found = False
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify fence device exists before attempting to remove
    for fencedev in dom.getElementsByTagName('fencedevice'):
        if fencedev.getAttribute("name") == name:
            fencedev.parentNode.removeChild(fencedev)
            fencedev_found = True

    if fencedev_found == False:
        print "Fence device '%s' does not exist in cluster.conf." % name
        sys.exit(1)

    set_cluster_conf(dom.toxml())

def add_fenceinst(name, options):
    fencedev_found = method_found = False

    if len(options) < 2:
        usage()
        sys.exit(2)
        
    nodename = options[0]
    methodname = options[1]
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify fence device exists
    for fencedev in dom.getElementsByTagName('fencedevice'):
        if fencedev.getAttribute("name") == name:
            fencedev_found = True
            break
    
    # Verify method exists for specified node
    for node in dom.getElementsByTagName('clusternode'):
        if node.getAttribute("name") == nodename:
            for method in node.getElementsByTagName('method'):
                if method.getAttribute("name") == methodname:
                    method_found = True
                    break
            break 

    if fencedev_found == False:
        print "Fence device '%s' not found." % name
        sys.exit(1)

    if method_found == False:
        print "Method '%s' not found in node '%s'." % (methodname, nodename)
        sys.exit(1)

    newfenceinst = dom.createElement("device")
    newfenceinst = set_element_attributes(newfenceinst, options[2:])

    newfenceinst.setAttribute("name", name)
    method.appendChild(newfenceinst)
    set_cluster_conf(dom.toxml())
    
def remove_fenceinst(name, options):
    fenceinst_found = False

    if len(options) < 2:
        usage()
        sys.exit(2)
        
    nodename = options[0]
    methodname = options[1]
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify fence instance exists before attempting to remove
    for node in dom.getElementsByTagName('clusternode'):
        if node.getAttribute("name") == nodename:
            for method in node.getElementsByTagName('method'):
                if method.getAttribute("name") == methodname:
                    for instance in method.getElementsByTagName('device'):
                        if instance.getAttribute("name") == name:
                            instance.parentNode.removeChild(instance)
                            fenceinst_found = True


    if fenceinst_found == False:
        print "Fence instance '%s' for node '%s' in method '%s' does not exist in cluster.conf." % (name, nodename, methodname)
        sys.exit(1)

    set_cluster_conf(dom.toxml())

def list_failoverdomain():
    dom = minidom.parseString(get_cluster_conf_xml())
    fds = dom.getElementsByTagName("failoverdomain")
    if len(fds) == 0:
        sys.exit(0)

    for fd in fds:
        print fd.getAttribute("name") + ": " + getNodeAttr(fd,"name")
        for fdn in fd.getElementsByTagName("failoverdomainnode"):
            print "  " + fdn.getAttribute("name") + ": " + getNodeAttr(fdn,"name")

def add_failoverdomain(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    failoverdomains_array = dom.getElementsByTagName("failoverdomains")

    # Verify failoverdomains and rm exist in cluster.conf file
    if len(failoverdomains_array) > 0:
        failoverdomains = failoverdomains_array[0]
    else:
        rm_array = dom.getElementsByTagName("rm")
        if len(rm_array) == 0:
            rm = dom.getElementsByTagName("cluster")[0].appendChild(dom.createElement("rm"));
        else:
            rm = rm_array[0]
        failoverdomains = rm.appendChild(dom.createElement("failoverdomains"))

    # Verify that there already isn't a failover domain with the same name
    failoverdomain_found = False
    for failoverdomain in failoverdomains.getElementsByTagName("failoverdomain"):
        if failoverdomain.getAttribute("name") == name:
            failoverdomain_found = True
            break
    
    if failoverdomain_found:
        print "Failover domain '%s' already exists." % (name)
        sys.exit(1)
    
    failoverdomain = failoverdomains.appendChild(dom.createElement("failoverdomain"))
    failoverdomain.setAttribute("name",name)

    if "restricted" in options: failoverdomain.setAttribute("restricted", "1")
    else: failoverdomain.setAttribute("restricted", "0")
    if "ordered" in options: failoverdomain.setAttribute("ordered", "1")
    else: failoverdomain.setAttribute("ordered", "0")
    if "nofailback" in options: failoverdomain.setAttribute("nofailback", "1")
    else: failoverdomain.setAttribute("nofailback", "0")

    set_cluster_conf(dom.toxml())

def remove_failoverdomain(name):
    dom = minidom.parseString(get_cluster_conf_xml())

    failoverdomains = dom.getElementsByTagName("failoverdomains")
    if len(failoverdomains) > 0:
        for failoverdomain in failoverdomains[0].getElementsByTagName("failoverdomain"):
            if failoverdomain.getAttribute("name") == name:
                failoverdomains[0].removeChild(failoverdomain)
                set_cluster_conf(dom.toxml())
                return
    else:
        print "No failoverdomains section found in cluster.conf"
        sys.exit(1)

    print "Unable to find failover domain '%s' in cluster.conf file." % (name)
    sys.exit(1)
    
# Add's a failoverdomainnode entry under a failoverdomain with 'name' and
# a node in options[0] if a failoverdomainnode already exists it is overwritten
def add_failoverdomainnode(name, options):
    if len(options) == 0:
        usage()
        sys.exit(2)

    node = options[0]
    if len(options) >= 2:
        if not options[1].isdigit():
            print "Priority must be an integer between 1 and 100"
            sys.exit(2)

        priority = int(options[1])
        if priority < 1 or priority > 100:
            print "Priority must be between 1 and 100"
            sys.exit(2)
    else:
        priority = -1 

    dom = minidom.parseString(get_cluster_conf_xml())

    
    failoverdomains_array = dom.getElementsByTagName("failoverdomains")

    if len(failoverdomains_array) > 0:
        failoverdomains = failoverdomains_array[0]
    else:
        print "No failoverdomains section is present, add a failover domain first."
        sys.exit(1)

    for failoverdomain in failoverdomains.getElementsByTagName("failoverdomain"):
        if failoverdomain.getAttribute("name") == name:
            for failoverdomainnode in failoverdomain.getElementsByTagName("failoverdomainnode"):
                if failoverdomainnode.getAttribute("name") == node:
                    failoverdomain.removeChild(failoverdomainnode)
            failoverdomain = failoverdomain.appendChild(dom.createElement("failoverdomainnode"))
            failoverdomain.setAttribute("name",node)
            if priority != -1:
                failoverdomain.setAttribute("priority",str(priority))
            set_cluster_conf(dom.toxml())
            return

    print "Unable to find failoverdomain '%s'" % (name)
    sys.exit(1)



def remove_failoverdomainnode(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    if len(options) != 1:
        usage()
        sys.exit(2)

    node = options[0]

    failoverdomains = dom.getElementsByTagName("failoverdomains")
    if len(failoverdomains) > 0:
        for failoverdomain in failoverdomains[0].getElementsByTagName("failoverdomain"):
            if failoverdomain.getAttribute("name") == name:
                for failoverdomainnode in failoverdomain.getElementsByTagName("failoverdomainnode"):
                    if failoverdomainnode.getAttribute("name") == node:
                        failoverdomain.removeChild(failoverdomainnode)
                        set_cluster_conf(dom.toxml())
                        return
    else:
        print "No failoverdomains section found in cluster.conf"
        sys.exit(1)

    print "Unable to find node '%s' in failover domain '%s'." % (node, name)
    sys.exit(1)


def list_fencedev():
    dom = minidom.parseString(get_cluster_conf_xml())
    fds = dom.getElementsByTagName('fencedevices')

    # If no fencedevice section, we don't print anything
    if len(fds) == 0:
        sys.exit(0)

    for fd in fds[0].getElementsByTagName('fencedevice'):
        print fd.getAttribute("name") + ": " + getNodeAttr(fd,"name")

def list_fenceinst(options):
    if len(options) == 0:
        node = None
    else:
        node = options[0]

    dom = minidom.parseString(get_cluster_conf_xml())

    nodes = dom.getElementsByTagName("clusternode")

    if node != None:
        for n in nodes:
            if n.getAttribute("name") != node:
                nodes.remove(n)
                break;

    for n in nodes:
        print n.getAttribute("name")
        for method in n.getElementsByTagName('method'):
            print "  " + method.getAttribute('name')
            for inst in method.getElementsByTagName('device'):
                print "    " + inst.getAttribute("name") + ": " + getNodeAttr(inst, "name")
    return


# Disabling these functions until we get a better idea of how to
# display fencing options
#def ls_fencedev():
#    rng = open(CLUSTERRNG)
#    dom = minidom.parseString(rng.read())
#    for elem in dom.getElementsByTagName("group"):
#        if elem.getAttribute("rha:fence_agent"):
#            print elem.getAttribute("rha:fence_agent")
#
#def ls_fenceinst(fencedev):
#    rng = open(CLUSTERRNG)
#    dom = minidom.parseString(rng.read())
#    for elem in dom.getElementsByTagName("group"):
#        if elem.getAttribute("rha:fence_agent") == fencedev:
#            for attr in elem.getElementsByTagName("attribute"):
#                attrname = attr.getAttribute("name")
#                invisible_attrs = ["verbose", "debug", "version", "help",
#                        "option", "action", "separator"]
#                if attrname not in invisible_attrs:
#                    print attr.getAttribute("name") + " - " + attr.getAttribute("rha:description")

def add_service(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify rm section exists in cluster.conf
    rm_array = dom.getElementsByTagName("rm")
    if len(rm_array) == 0:
        rm = dom.getElementsByTagName("cluster")[0].appendChild(dom.createElement("rm"));
    else:
        rm = rm_array[0]

    # Verify service doesn't exist with the same name
    for service in dom.getElementsByTagName('service'):
        if service.getAttribute("name") == name:
            print "Service '%s' already exists in cluster.conf." % name
            sys.exit(1)

    service = dom.getElementsByTagName('rm')[0].appendChild(dom.createElement("service"))
    service = set_element_attributes(service, options)

    service.setAttribute("name", name)
    set_cluster_conf(dom.toxml())

def remove_service(name):
    serviceRemoved = False

    dom = minidom.parseString(get_cluster_conf_xml())

    rm = dom.getElementsByTagName("rm")
    if len(rm) > 0:
        rm = rm[0]
        services = rm.getElementsByTagName("service")
        for service in services:
            if service.getAttribute("name") == name:
                rm.removeChild(service)
                serviceRemoved = True
    else:
        print "No <rm> section in cluster.conf"
        sys.exit(1)

    if not serviceRemoved:
        print "Unable to find service: %s" % name
        sys.exit(1)

    set_cluster_conf(dom.toxml())

# Add a subservice checking for slashes and brackets
def add_subservice(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())
    serviceFound = False

    location = options.pop(0).split(':')
    subservice_type = location.pop(-1)
    location = ":".join(location)

    # Verify top level service exists
    for service in dom.getElementsByTagName("service"):
        if service.getAttribute("name") == name:
            serviceFound = True
            break;

    if serviceFound == False:
        print "Unable to find service: %s" % name
        sys.exit(1)


    service = getLocation(service, location)
    if service == None:
        print "Unable to find service %s" % location
        sys.exit(1)

    subservice = service.appendChild(dom.createElement(subservice_type))
    subservice = set_element_attributes(subservice, options)

    set_cluster_conf(dom.toxml())

def remove_subservice(name, options):
    dom = minidom.parseString(get_cluster_conf_xml())
    serviceFound = False

    location = options.pop(0)

    # Verify that the service exists
    for service in dom.getElementsByTagName("service"):
        if service.getAttribute("name") == name:
            serviceFound = True
            break;

    if serviceFound == False:
        print "Unable to find service: %s" % name
        sys.exit(1)

    service = getLocation(service, location)
    if service == None:
        print "Unable to find service %s" % location
        sys.exit(1)

    service.parentNode.removeChild(service)
    set_cluster_conf(dom.toxml())

def expert_mode(tag, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    parent = dom.getElementsByTagName('cluster')[0]
    if len(options) != 0:
        location = options.pop(0)

        parent = getLocation(parent,location) 
        if parent == None:
            print "Unable to find %s" % location
            sys.exit(1)

    newelement = parent.appendChild(dom.createElement(tag))
    newelement = set_element_attributes(newelement, options)
    set_cluster_conf(dom.toxml())

def expert_mode_remove(location):
    dom = minidom.parseString(get_cluster_conf_xml())

    parent = dom.getElementsByTagName('cluster')[0]
    parent = getLocation(parent,location) 
    if parent == None:
        print "Unable to find %s" % location
        sys.exit(1)

    parent.parentNode.removeChild(parent)
    set_cluster_conf(dom.toxml())

# Return the element specified by the location in dom
# Elements separated by ':' and elements of the same name referenced by [n]
def getLocation(dom, location):
    if location == "":
        return dom

    elems = location.split(':')

    for elem in elems:
        element_number = 0
        r = re.search(r"\[(\d+)\]$", elem)
        if r:
            if r.group(1):
                element_number = int(r.group(1))
        elem = re.sub(r"\[\d+\]$", "",  elem)

        count = 0
        element_found = False
        for node in dom.childNodes:
            if node.nodeType != node.ELEMENT_NODE:
                continue
            if node.tagName == elem:
                if count == element_number:
                    dom = node
                    element_found = True
                    break
                else: count = count + 1
        if element_found == False:
            return None

    return dom

def add_resource(type, options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # Verify resources section exists in cluster.conf
    resources = dom.getElementsByTagName('resources')
    if len(resources) == 0:
        resources = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("resources"))
    elif len(resources) > 1:
        print "Error: Too many resources elements in cluster.conf"
        sys.exit(1)
    else:
        resources = resources[0]

    # Verify name is unique, if no name then we don't search for duplicates
    resname = False
    for option in options:
        (attr, sep, val) = option.partition('=')
        if attr == "name":
            resname = val
            break

    if resname != False:
        for resource in resources.childNodes:
            if resource.nodeType == minidom.Node.TEXT_NODE:
                continue
            if resource.getAttribute("name") == resname:
                print "Duplicate name: %s" % resname
                sys.exit(1)

    newresource = dom.createElement(type)
    newresource = set_element_attributes(newresource, options)

    resources = dom.getElementsByTagName('resources')[0]
    resources.appendChild(newresource)
    set_cluster_conf(dom.toxml())

# Removes the first resource that matches all the options
def remove_resource(type, options):
    dom = minidom.parseString(get_cluster_conf_xml())
    resourceMatch = False

    # Verify resources section exists in cluster.conf
    resources = dom.getElementsByTagName('resources')
    if len(resources) != 1:
        print "Error: resources section not present or improperly formatted"
        sys.exit(1)
    else:
        resources = resources[0]

    resource_options = {}
    for option in options:
        (attr, sep, val) = option.partition('=')
        if (sep == ""):
            print "Invalid option: %s" % option
            sys.exit(1)
        resource_options[attr] = val

    for resource in resources.childNodes:
        if resource.nodeType == minidom.Node.TEXT_NODE:
            continue
        if resource.tagName == type:
            resourceMatch = False
            for option,val in resource_options.iteritems():
                if resource.getAttribute(option) != val:
                    resourceMatch = False
                    break
                else:
                    resourceMatch = True
            if resourceMatch == True:
                break

    if resourceMatch != True:
        print "Unable to find matching resource: %s, %s" % (type,options)
        sys.exit(1)

    resources.removeChild(resource)
            
    set_cluster_conf(dom.toxml())

def list_quorum():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    
    list_tag(dom,"quorumd", "Quorumd")
    list_tag(dom, "heuristic", "  heuristic")

def set_quorumd(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If quorumd section exists we rewrite it, else we create a new one
    quorumd = dom.getElementsByTagName('quorumd')
    if len(quorumd) == 0:
        quorumd = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("quorumd"))
    elif len(quorumd) > 1:
        print "Error: Too many quorumd elements in cluster.conf"
        sys.exit(1)
    else:
        quorumd = quorumd[0]

    while(quorumd.attributes.length != 0):
        quorumd.removeAttribute(quorumd.attributes.item(0).name)
    quorumd = set_element_attributes(quorumd, options)

    set_cluster_conf(dom.toxml())

def add_heuristic(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    if len(options) == 0:
        print "You must specify at least one option when adding a heuristic."
        sys.exit(1)

    # If quorumd section is present we use it, else we create an empty one
    quorumd = dom.getElementsByTagName('quorumd')
    if len(quorumd) == 0:
        quorumd = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("quorumd"))
    elif len(quorumd) > 1:
        print "Error: Too many quorumd elements in cluster.conf"
        sys.exit(1)
    else:
        quorumd = quorumd[0]

    heuristic = dom.createElement("heuristic")
    heuristic = set_element_attributes(heuristic, options)

    quorumd.appendChild(heuristic)
    set_cluster_conf(dom.toxml())

def remove_heuristic(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If quorumd section is present we use it, else we create an empty one
    quorumd = dom.getElementsByTagName('quorumd')
    if len(quorumd) == 0:
        print "Error: no quorumd section in cluster.conf"
        sys.exit(1)
    elif len(quorumd) > 1:
        print "Error: Too many quorumd elements in cluster.conf"
        sys.exit(1)
    else:
        quorumd = quorumd[0]

    heuristic_options = {}
    for option in options:
        (attr, sep, val) = option.partition('=')
        if (sep == ""):
            print "Invalid option: %s" % option
            sys.exit(1)
        heuristic_options[attr] = val

    heuristicMatch = False
    for heuristic in dom.getElementsByTagName('heuristic'):
        if options == [] and heuristic.attributes.length == 0:
            heuristicMatch = True
            break
        for option,val in heuristic_options.iteritems():
            if heuristic.getAttribute(option) != val:
                heuristicMatch = False
                break
            else:
                heuristicMatch = True
        if heuristicMatch == True:
            break

    if heuristicMatch != True:
        print "Unable to find matching heuristic: %s" % (options)
        sys.exit(1)

    quorumd.removeChild(heuristic)
            
    set_cluster_conf(dom.toxml())

def list_misc():
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    
    list_tag(dom,"totem", "Totem")
    list_tag(dom, "dlm", "DLM")
    list_tag(dom, "rm", "Resource Manager")
    list_tag(dom, "cman", "CMAN")
    list_tag(dom, "multicast", "Multicast")
    list_tag(dom, "fencedaemon", "Fence Daemon")
    list_tag(dom, "logging", "Logging")
    list_tag(dom, "logger", "  logger")

def set_totem(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If totem section is present we use it, else we create it
    totem = dom.getElementsByTagName('totem')
    if len(totem) == 0:
        totem = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("totem"))
    elif len(totem) > 1:
        print "Error: Too many totem elements in cluster.conf"
        sys.exit(1)
    else:
        totem = totem[0]

    while(totem.attributes.length != 0):
        totem.removeAttribute(totem.attributes.item(0).name)

    totem = set_element_attributes(totem, options)
    set_cluster_conf(dom.toxml())

def set_rm(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If rm section is present we use it, else we create it
    rm = dom.getElementsByTagName('rm')
    if len(rm) == 0:
        rm = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("rm"))
    elif len(rm) > 1:
        print "Error: Too many rm elements in cluster.conf"
        sys.exit(1)
    else:
        rm = rm[0]

    while(rm.attributes.length != 0):
        rm.removeAttribute(rm.attributes.item(0).name)
    rm = set_element_attributes(rm, options)
    set_cluster_conf(dom.toxml())

def set_cman(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If cman section is present we use it, else we create it
    cman = dom.getElementsByTagName('cman')
    if len(cman) == 0:
        cman = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("cman"))
    elif len(cman) > 1:
        print "Error: Too many cman elements in cluster.conf"
        sys.exit(1)
    else:
        cman = cman[0]

    while(cman.attributes.length != 0):
        cman.removeAttribute(cman.attributes.item(0).name)
    cman = set_element_attributes(cman, options)
    set_cluster_conf(dom.toxml())

def set_dlm(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If dlm section is present we use it, else we create it
    dlm = dom.getElementsByTagName('dlm')
    if len(dlm) == 0:
        dlm = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("dlm"))
    elif len(dlm) > 1:
        print "Error: Too many dlm elements in cluster.conf"
        sys.exit(1)
    else:
        dlm = dlm[0]

    while(dlm.attributes.length != 0):
        dlm.removeAttribute(dlm.attributes.item(0).name)
    dlm = set_element_attributes(dlm, options)
    set_cluster_conf(dom.toxml())

def set_fencedaemon(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If fence_daemon section is present we use it, else we create it
    fence_daemon = dom.getElementsByTagName('fence_daemon')
    if len(fence_daemon) == 0:
        fence_daemon = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("fence_daemon"))
    elif len(fence_daemon) > 1:
        print "Error: Too many fence_daemon elements in cluster.conf"
        sys.exit(1)
    else:
        fence_daemon = fence_daemon[0]

    while(fence_daemon.attributes.length != 0):
        fence_daemon.removeAttribute(fence_daemon.attributes.item(0).name)
    fence_daemon = set_element_attributes(fence_daemon, options)
    set_cluster_conf(dom.toxml())

def set_logging(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    # If logging section is present we use it, else we create it
    logging = dom.getElementsByTagName('logging')
    if len(logging) == 0:
        logging = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("logging"))
    elif len(logging) > 1:
        print "Error: Too many logging elements in cluster.conf"
        sys.exit(1)
    else:
        logging = logging[0]

    while(logging.attributes.length != 0):
        logging.removeAttribute(logging.attributes.item(0).name)
    logging = set_element_attributes(logging, options)
    set_cluster_conf(dom.toxml())

def add_logger(options):
    dom = minidom.parseString(get_cluster_conf_xml())

    logging = dom.getElementsByTagName('logging')
    if len(logging) == 0:
        logging = dom.getElementsByTagName('cluster')[0].appendChild(dom.createElement("logging"))
    elif len(logging) > 1:
        print "Error: Too many logging elements in cluster.conf"
        sys.exit(1)
    else:
        logging = logging[0]

    logger = dom.createElement("logger")
    logger = set_element_attributes(logger, options)

    logging.appendChild(logger)
    set_cluster_conf(dom.toxml())

def remove_logger(options):
    dom = minidom.parseString(get_cluster_conf_xml())
    loggerMatch = False

    # Verify logging section exists in cluster.conf
    logging = dom.getElementsByTagName('logging')
    if len(logging) != 1:
        print "Error: logging section not present or improperly formatted"
        sys.exit(1)
    else:
        logging = logging[0]

    logger_options = {}
    for option in options:
        (attr, sep, val) = option.partition('=')
        if (sep == ""):
            print "Invalid option: %s" % option
            sys.exit(1)
        logger_options[attr] = val

    for logger in logging.childNodes:
        if logger.nodeType == minidom.Node.TEXT_NODE:
            continue
        loggerMatch = False
        for option,val in logger_options.iteritems():
            if logger.getAttribute(option) != val:
                loggerMatch = False
                break
            else:
                loggerMatch = True
        if loggerMatch == True:
            break

    if loggerMatch != True:
        print "Unable to find matching logger: %s" % (options)
        sys.exit(1)

    logging.removeChild(logger)
            
    set_cluster_conf(dom.toxml())

def set_multicast(arg):
    dom = minidom.parseString(get_cluster_conf_xml())

    cman = dom.getElementsByTagName("cman")
    if len(cman) == 0:
        cman = dom.createElement("cman")
        dom.getElementsByTagName("cluster")[0].appendChild(cman)
    elif len(cman) == 1:
        cman = cman[0]
    else:
        print "Error: more than one cman section is present."
        sys.exit(1)

    mc = cman.getElementsByTagName('multicast')
    for i in mc:
        cman.removeChild(i)

    if arg != "":
        mc = dom.createElement("multicast")
        mc.setAttribute("addr",arg)
        cman.appendChild(mc)

    set_cluster_conf(dom.toxml())

# Lists options for a specific tag
def list_tag(dom, tag, prettyname):
    elements = dom.getElementsByTagName(tag)

    # If no 'tag' section, we don't print anything
    if len(elements) == 0:
        return

    for element in elements:
        print prettyname + ": " + getNodeAttr(element)

# Sets the attributes of a specific xml element where options
# is an array of strings containing attribute=value
# returns the modified element
def set_element_attributes(element, options):
    for option in options:
        (attr, sep, val) = option.partition('=')
        if (sep == ""):
            print "Invalid option: %s" % option
            sys.exit(1)
        element.setAttribute(attr,val)
    return element

# Send the cluster.conf file specified by -f to the host specified by -h
def send_cluster_conf():
    global usefile, filename
    if not usefile or not filename:
        print "Error: must specify a filename and host when using --setconf"
        sys.exit(2)

    # Will default to the file
    xml = get_cluster_conf_xml()
    # After reading file, zero out usefile and filename to send to host
    usefile = filename = False
    set_cluster_conf(xml, False)

# Set the cluster.conf file and increment the version
def set_cluster_conf(xml, increment = True):
    dom = minidom.parseString(xml)

    if increment:
      version = dom.getElementsByTagName("cluster")[0].getAttribute("config_version")
      version = int(version) + 1
      dom.getElementsByTagName("cluster")[0].setAttribute("config_version",str(version))
      xml = dom.toxml()

    xml = xml.replace('<?xml version="1.0" ?>','')
    log_msg (xml)

    if usefile:
        xml = dom.toprettyxml("  ","\n")
        xml = xml.replace('<?xml version="1.0" ?>','')
        xml = re.sub(r"(?m)^\s*\n","", xml)
        f = open(filename, 'w')
        f.write(xml)
        f.close()
    else:
        if activate:
            log_msg (send_ricci_command("cluster", "set_cluster.conf", hostname, ("propagate", "boolean", "true"),("cluster.conf","xml","",xml)))
        else:
            log_msg (send_ricci_command("cluster", "set_cluster.conf", hostname, ("cluster.conf","xml","",xml)))

def check_cluster_conf():
    global hostname, usefile
    xml = get_cluster_conf_xml()
    dom = minidom.parseString(xml)
    
    usefile = False
    bad_nodes = []
    orig_hostname = hostname
    for node in dom.getElementsByTagName("clusternode"):
        hostname = node.getAttribute("name")
        if hostname == orig_hostname:
            continue
        xml2 = get_cluster_conf_xml()
        if xml2 != xml:
            print "Node: %s does not match" % hostname
            bad_nodes << hostname

    hostname = orig_hostname
    if len(bad_nodes) > 0:
        sys.exit(1)
    else:
        print "All nodes in sync."

def verify_authentication(dom):
    ricci =  dom.getElementsByTagName("ricci")
    if len(ricci) > 0:
        if ricci[0].getAttribute("authenticated") != "true":
            return False
        else:
            return True
    print "Error: no ricci tag in ricci response"
    sys.exit(1)

def check_authentication(host):
    msg = '<ricci function="authenticate" password="%s" version="1.0"/>' % password
    res = send_to_ricci(msg, host)     
    dom = minidom.parseString(res[1])
    if (not verify_authentication(dom)):
        print "Unable to authenticate with ricci node, please check password."
        sys.exit(1)

def send_ricci_command(module, command, host = None, *vars):
    global password
    variables = ""

    if host == None:
        host = hostname

    for value in vars:
        variables = variables + '<var mutable="false" name="%s" type="%s" value="%s">' % (value[0],value[1],value[2])
        if (len(value) > 3):
            variables = variables + value[3]
        variables = variables + "</var>"

    # If a password is set, then we authenticate
    if password != None:
        check_authentication(host)
    
    msg = '<ricci function="process_batch" async="false" version="1.0"><batch><module name="%s"><request API_version="1.0"><function_call name="%s">%s</function_call></request></module></batch></ricci>' % (module,command,variables)
    res = send_to_ricci(msg, host)
    dom = minidom.parseString(res[1].replace('\t',''))
    if (not verify_authentication(dom)):
        password = getpass.getpass(host + " password: ");
        check_authentication(host)
        res = send_to_ricci(msg, host)
        dom = minidom.parseString(res[1].replace('\t',''))
        if (not verify_authentication(dom)):
            print "Error: Not yet authenticated with %s (try -p option)" % host
            sys.exit(1)
    xml = dom.getElementsByTagName('function_response')[0].toxml()
    return xml 

def send_to_ricci(msg, host=hostname):
    cert = os.path.expanduser("~/.ccs/cacert.pem")
    key = os.path.expanduser("~/.ccs/privkey.pem")
    config = os.path.expanduser("~/.ccs/cacert.config")
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # Make sure we have a client certificate and private key
    # If not we need to autogenerate them (including creating an
    # openssl configuration file
    if (os.path.isfile(cert) == False or os.path.isfile(key) == False):
        log_msg("Autogenerating private key and certificate.")
        if not os.path.exists(os.path.expanduser("~/.ccs")):
            os.mkdir(os.path.expanduser("~/.ccs"),0700);
        f = open (config, 'w')
        f.write("""
[ req ]
distinguished_name     = req_distinguished_name
attributes             = req_attributes
prompt                 = no

[ req_distinguished_name ]
C                      = US
ST                     = State or Province
L                      = Locality
O                      = Organization Name
OU                     = Organizational Unit Name
CN                     = Common Name
emailAddress           = root@localhost

[ req_attributes ]
""")
        f.close()
        if (debug):
            redirect = ""
        else:
            redirect = " > /dev/null 2&>1"

        os.system ("/usr/bin/openssl genrsa -out %s 2048 %s" % (key,redirect))
        os.system ("/usr/bin/openssl req -new -x509 -key %s -out %s -days 1825 -config ~/.ccs/cacert.config" % (key,cert))

    ss = ssl.wrap_socket(s, key, cert)
    try:
        ss.connect((host, RICCI_PORT))
    except socket.error:
        print "Unable to connect to %s, make sure the ricci server is started." % host
        sys.exit(1)

    log_msg ("***Sending to ricci server:")
    log_msg (msg)
    log_msg ("***Sending End")
    logging.debug("Connected...")
    res1 = ss.read(1024)
    logging.debug("Writing...")
    logging.debug(msg)
    ss.write(msg)
    logging.debug("Writen...")
    res2 = ''
    while True:
        logging.debug("Waiting to read...")
        buff = ss.read(10485760)
        logging.debug(buff)
        logging.debug("Read...")
        if buff == '':
            break
        res2 += buff
        try:
            minidom.parseString(res2)
            break
        except:
            pass
    log_msg ("***Received from ricci server")
    log_msg (res2)
    log_msg ("***Receive End")
    return res1, res2

def log_msg(message):
    if debug == True:
      print message

if __name__ == "__main__":
    main(sys.argv[1:])
