Whamcloud - gitweb
* support for toenal
[fs/lustre-release.git] / lustre / utils / lmc
index 009d75a..6f34b2b 100755 (executable)
@@ -1,7 +1,6 @@
 #!/usr/bin/env python
-#
-#  Copyright (C) 2002 Cluster File Systems, Inc.
-#   Author: Robert Read <rread@clusterfs.com>
+# Copyright (C) 2002 Cluster File Systems, Inc.
+# Author: Robert Read <rread@clusterfs.com>
 
 #   This file is part of Lustre, http://www.lustre.org.
 #
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 #
 
-# lmc - lustre configurtion data  manager
-#
-import sys, getopt
+"""
+lmc - lustre configurtion data  manager
+ Basic plan for lmc usage:
+# create nodes
+./lmc --output config.xml --node server --net server1 tcp 
+./lmc --merge config.xml  --node client --net client1 tcp
+./lmc --merge config.xml  --node client --route gw lo [hi]
+./lmc --merge config.xml --router --node gw1 --net gw1 tcp
+./lmc --merge config.xml --node gw1 --net 1 elan
+
+./lmc --merge config.xml --route elan 1 1 100
+./lmc --merge config.xml --route tcp gw1 ba1
+
+
+
+# configure server
+./lmc --merge config.xml  --node server --mds mds1 /tmp/mds1 50000
+
+# create lov
+./lmc --merge config.xml  --lov lov1 mds1 65536 0 0
+./lmc --merge config.xml  --node server --lov lov1 --ost /tmp/ost1 100000
+./lmc --merge config.xml  --node server --lov lov1 --ost /tmp/ost2 100000
+
+# create client config
+./lmc --merge config.xml  --node client --mtpt /mnt/lustre mds1 lov1
+
+"""
+
+import sys, os, getopt, string
 import xml.dom.minidom
+from xml.dom.ext import PrettyPrint
+
 
+DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use?
 
 def usage():
-    print """usage: lmc [--ost | --mtpt | --lov] cmd args
+    print """usage: lmc [--node --ost | --mtpt | --lov] args
 Commands:
---ost "device" "host" [size]
+--node node_name 
+   Node_name by itself it will create a new node. If the --router
+   option is used when creating a new node, then that node will also
+   be configured as a router. When used with other commands it
+   specifies the node to modify.
+
+--net hostname nettype [port, recv_buf, send_buf]
+   Nettype is either tcp, toe, elan, or gm.
+   Requires --node
+
+--route net gw lo [hi]
+   This command is used to create  routes.  NET is the
+   network type this route will be used on.  The GW is an address of
+   one of the local interfaces. LO and HI represent a range of
+   addresses that can be reached through the gateway. If HI is not
+   set, then a route to the specific host in LO is created.
+
+--mds device [size]
+   Create a MDS using the device
+   Requires --node 
+
+--lov lov_name [mds_name stripe_sz sub_stripe_count pattern]
+   Creates a logical volume
+   When used with other commands, it specifics the lov to modify
+
+--ost device [size]
    Creates an OBD/OST/OSC configuration triplet for a new device.
    When used on "host", the device will be initialized and the OST
    will be enabled. On client nodes, the OSC will be avaiable.
+   Requires --node
+   Optional --obduuid Specifies the UUID used for the obd. 
+   If --lov lov_name is used, this device is added to lov. 
 
---mtpt "mds" "ost/lov-name" /mnt/point
+--mtpt /mnt/point mds_name lov_name|osc_name 
    Creates a client mount point.
-
---lov "mds" "lov name" < "all-ost.xml"
-   Produces a logical volum striped over the OSTs found in all-ost.xml.
-   (Not sure how all-ost.xml is created, exactly.)
+   Requires --node
 
 Options:
 --merge="xml file"  Add the new objects to an existing file
 --format            Format the partitions if unformated
+                    NB: The autoformat option has been disabled until a safe
+                    method is implemented to determine if a block device has a
+                    filesystem.
 --reformat          Reformat partitions (this should be an lconf arg,
                     I think)
-(SCRIPT STILL UNDER DEVELOPMENT, MOST COMMANDS/OPTIONS UNIMPLEMENTED)
-"""  
+--obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
+                    This is only useful for the --ost command.
+                    The device parameters are ignored for the obdecho type.
+"""
+    sys.exit(1)
 
+def error(*args):
+    msg = string.join(map(str,args))
+    print "Error: ", msg
+    sys.exit(1)
+    
+def warning(*args):
+    msg = string.join(map(str,args))
+    print "Warning: ", msg
+    
 #
 # manage names and uuids
 # need to initialize this by walking tree to ensure
 # no duplicate names or uuids are created.
 # this are just place holders for now.
 # consider changing this to be like OBD-dev-host
-name_ctr = 1
 def new_name(base):
-    global name_ctr
-    name = "%s_%d" % (base, name_ctr)
-    name_ctr += 1
-    return name
+    ctr = 2
+    ret = base
+    while names.has_key(ret):
+        ret = "%s_%d" % (base, ctr)
+        ctr = 1 + ctr
+    names[ret] = 1
+    return ret
 
-def get_uuid(name):
+def new_uuid(name):
     return "%s_UUID" % (name)
 
-# simple pretty print XML
-def newline(dom, node, ind = ""):
-    node.appendChild(dom.createTextNode("\n" + ind))
-def indent(dom, node, ind = "   "):
-    node.appendChild(dom.createTextNode(ind))
-
-#
-# Create a new empty lustre document 
-def new_Lustre():
+ldlm_name = 'ldlm'
+ldlm_uuid = 'ldlm_UUID'
+def new_lustre(dom):
+    """Create a new empty lustre document"""
+    # adding ldlm here is a bit of a hack, but one is enough.
     str = """<lustre>
-</lustre>"""
-    dom = xml.dom.minidom.parseString(str)
-    return dom
-
-def new_network(dom, net, hostname, port):
-    name = new_name('net')
-    uuid = get_uuid(name)
-    str = """
-   <network name = "%s" uuid = "%s" type="%s" >
-      <server>%s</server>
-      <port>%d</port>
-   </network> """ % (name, uuid, net, hostname, port)
-    node = xml.dom.minidom.parseString(str)
-    return node.getElementsByTagName("network")[0]
+    <ldlm name="%s" uuid="%s"/>
+    </lustre>""" % (ldlm_name, ldlm_uuid)
+    return dom.parseString(str)
 
+names = {}
+uuids = {}
+
+def init_names(doc):
+    """initialize auto-name generation tables"""
+    global names, uuids
+    # get all elements that contain a name attribute
+    for n in doc.childNodes:
+        if n.nodeType == n.ELEMENT_NODE:
+            if getName(n):
+                names[getName(n)] = 1
+                uuids[getUUID(n)] = 1
+            init_names(n)
+
+def get_format_flag(options):
+    if options.has_key('format'):
+        if options['format']:
+            return 'yes'
+    return 'no'
+
+############################################################
+# Build config objects using DOM
 #
-# Create new object the fast and easy way
-# (note: dom is not needed for this way)
-def new_node(dom, net, hostname, port):
-    uuid = get_uuid(hostname)
-    str ="""
-<node name="%s" uuid="%s">
-</node> """ % (hostname, uuid)
-    node = xml.dom.minidom.parseString(str)
-    node = node.getElementsByTagName("node")[0]
-    indent(dom, node)
-    node.appendChild(new_network(dom, net, hostname, port))
-    newline(dom, node)
+class GenConfig:
+    doc = None
+    dom = None
+    def __init__(self, doc):
+        self.doc = doc
+
+    def ref(self, type, uuid):
+        """ generate <[type]_ref uuidref="[uuid]"/> """
+        tag = "%s_ref" % (type)
+        ref = self.doc.createElement(tag)
+        ref.setAttribute("uuidref", uuid)
+        return ref
+    
+    def newService(self, tag, name, uuid):
+        """ create a new  service elmement, which requires name and uuid attributes """
+        new = self.doc.createElement(tag)
+        new.setAttribute("name", name);
+        new.setAttribute("uuid", uuid);
+        return new
+    
+    def addText(self, node, str):
+        txt = self.doc.createTextNode(str)
+        node.appendChild(txt)
+
+    def addElement(self, node, tag, str=None):
+        """ create a new element and add it as a child to node. If str is passed,
+            a text node is created for the new element"""
+        new = self.doc.createElement(tag)
+        if str:
+            self.addText(new, str)
+        node.appendChild(new)
+        return new
+
+    def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
+        """create <network> node"""
+        network = self.newService("network", name, uuid)
+        network.setAttribute("type", net);
+        self.addElement(network, "server", hostname)
+        if port:
+            self.addElement(network, "port", "%d" %(port))
+        if tcpbuf:
+            self.addElement(network, "send_mem", "%d" %(tcpbuf))
+            self.addElement(network, "recv_mem", "%d" %(tcpbuf))
+            
+        return network
+
+    def route(self, net_type, gw, lo, hi):
+        """ create one entry for the route table """
+        ref = self.doc.createElement('route')
+        ref.setAttribute("type", net_type)
+        ref.setAttribute("gw", gw)
+        ref.setAttribute("lo", lo)
+        if hi:
+            ref.setAttribute("hi", hi)
+        return ref
+    
+    def node(self, name, uuid):
+        """ create a host """
+        node = self.newService("node", name, uuid)
+        self.addElement(node, 'profile')
+        return node
+
+    def ldlm(self, name, uuid):
+        """ create a ldlm """
+        ldlm = self.newService("ldlm", name, uuid)
+        return ldlm
+
+    def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
+        obd = self.newService("obd", name, uuid)
+        obd.setAttribute('type', obdtype)
+        if fs:
+            self.addElement(obd, "fstype", fs)
+        if devname:
+            dev = self.addElement(obd, "device", devname)
+            if (dev_size):
+                dev.setAttribute("size", "%s" % (dev_size))
+            self.addElement(obd, "autoformat", format)
+        return obd
+
+    def osc(self, name, uuid, obd_uuid, net_uuid):
+        osc = self.newService("osc", name, uuid)
+        osc.appendChild(self.ref("ost", net_uuid))
+        osc.appendChild(self.ref("obd", obd_uuid))
+        return osc
+
+    def ost(self, name, uuid, obd_uuid, net_uuid):
+        ost = self.newService("ost", name, uuid)
+        ost.appendChild(self.ref("network", net_uuid))
+        ost.appendChild(self.ref("obd", obd_uuid))
+        return ost
+
+    def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_count, pattern):
+        lov = self.newService("lov", name, uuid)
+        lov.appendChild(self.ref("mds", mds_uuid))
+        devs = self.addElement(lov, "devices" )
+        devs.setAttribute("stripesize", stripe_sz)
+        devs.setAttribute("stripecount", stripe_count)
+        devs.setAttribute("pattern", pattern)
+        return lov
+
+    def lovconfig(self, name, uuid, lov_uuid):
+        lovconfig = self.newService("lovconfig", name, uuid)
+        lovconfig.appendChild(self.ref("lov", lov_uuid))
+        return lovconfig
+
+    def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
+            failover_uuid = "", dev_size=0 ):
+        mds = self.newService("mds", name, uuid)
+        self.addElement(mds, "fstype", fs)
+        dev = self.addElement(mds, "device", devname)
+        if dev_size:
+            dev.setAttribute("size", "%s" % (dev_size))
+        self.addElement(mds, "autoformat", format)
+        mds.appendChild(self.ref("network", net_uuid))
+        mds.appendChild(self.ref("node", node_uuid))
+        if failover_uuid:
+            mds.appendChild(self.ref("failover", failover_uuid))
+        return mds
+
+    def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
+        mtpt = self.newService("mountpoint", name, uuid)
+        mtpt.appendChild(self.ref("mds", mds_uuid))
+        mtpt.appendChild(self.ref("osc", osc_uuid))
+        self.addElement(mtpt, "path", path)
+        return mtpt
+
+############################################################
+# Utilities to query a DOM tree
+# Using this functions we can treat use config information
+# directly as a database.
+def getName(n):
+    return n.getAttribute('name')
+
+def getUUID(node):
+    return node.getAttribute('uuid')
+
+
+def findByName(lustre, name, tag = ""):
+    for n in lustre.childNodes:
+        if n.nodeType == n.ELEMENT_NODE:
+            if tag and n.nodeName != tag:
+                continue
+            if getName(n) == name:
+                return n
+            else:
+                n = findByName(n, name)
+                if n: return n
+    return None
+
+
+def lookup(node, uuid):
+    for n in node.childNodes:
+        if n.nodeType == n.ELEMENT_NODE:
+            if getUUID(n) == uuid:
+                return n
+            else:
+                n = lookup(n, uuid)
+                if n: return n
+    return None
+            
+
+def mds2node(lustre, mds_name):
+    """ Find the node a MDS is configured on """
+    mds = findByName(lustre, mds_name, 'mds')
+    ref = mds.getElementsByTagName('node_ref')
+    if not ref:
+        error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
+    node_uuid = ref[0].getAttribute('uuidref')
+    node = lookup(lustre, node_uuid)
+    if not node:
+        error('mds2node:', "no node found for :", '"'+mds_name+'"')
     return node
-#
-# Create a new object the "correct" way by using the DOM api.
-#
-def new_OBD(dom, name, fs, devname, format, dev_size=0, dev_file=""):
-    uuid = get_uuid(name)
+
+
+def name2uuid(lustre, name, tag="",  fatal=1):
+    ret = findByName(lustre, name, tag)
+    if not ret:
+        if fatal:
+            error('name2uuid:', '"'+name+'"', tag, 'element not found.')
+        else:
+            return ""
+    return getUUID(ret)
     
-    obd = dom.createElement("obd")
-    obd.setAttribute("name", name)
-    obd.setAttribute("uuid", uuid)
-    obd.setAttribute('type', 'obdfilter')
-    obd.appendChild(dom.createTextNode("\n  "))
-
-    fstype = dom.createElement("fstype")
-    txt= dom.createTextNode(fs)
-    fstype.appendChild(txt)
-    obd.appendChild(fstype)
+
+# XXX: assumes only one network element per node. will fix this
+# as soon as support for routers is added
+def get_net_uuid(lustre, node_name):
+    """ get a network uuid for a node_name """
+    node = findByName(lustre, node_name, "node")
+    if not node:
+        error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
+    net = node.getElementsByTagName('network')
+    if net:
+        return getUUID(net[0])
+    return None
+
+
+def lov_add_osc(gen, lov, osc_uuid):
+    devs = lov.getElementsByTagName('devices')
+    if len(devs) == 1:
+        devs[0].appendChild(gen.ref("osc", osc_uuid))
+    else:
+        error("No devices element found for LOV:", lov)
+
+                            
+def node_add_profile(gen, node, ref, uuid):
+    ret = node.getElementsByTagName('profile')
+    if not ret:
+        error('node has no profile:', node)
+    ret[0].appendChild(gen.ref(ref, uuid))
     
-    dev = dom.createElement("device")
-    if (dev_size):
-        dev.setAttribute("size", "%s" % (dev_size))
-    txt = dom.createTextNode(devname)
-    dev.appendChild(txt)
-    newline(dom, obd, "  ")
-    obd.appendChild(dev)
-    newline(dom, obd, "   ")
-    fmt = dom.createElement("autoformat")
-    txt = dom.createTextNode(format)
-    fmt.appendChild(txt)
-    obd.appendChild(fmt)
-    newline(dom, obd)
-
-    return obd
+def get_attr(dom_node, attr, default=""):
+    v = dom_node.getAttribute(attr)
+    if v:
+        return v
+    return default
 
+############################################################
+# Top level commands
 #
-# Create new object the fast and easy way
-# (note: dom is not needed for this way)
-def new_OSC(dom, osc, obd):
-    osc_uuid = get_uuid(osc)
-    obd_uuid = get_uuid(obd)
-
-    osc_str ="""
-<osc name="%s" uuid="%s">
-   <service_id num="1" name="%s" uuid="%s"/>
-</osc> """ % (osc, osc_uuid, obd, obd_uuid)
-    osc = xml.dom.minidom.parseString(osc_str)
-    return osc.getElementsByTagName("osc")[0]
+def do_add_node(gen, lustre,  options, node_name):
+    uuid = new_uuid(node_name)
+    node = gen.node(node_name, uuid)
+    node_add_profile(gen, node, 'ldlm', ldlm_uuid)
+    if options.has_key('router'):
+        node.setAttribute('router', '1')
+    lustre.appendChild(node)
+    return node
 
-#
-# Create new object the fast and easy way
-#
-def new_OST(dom, ost, host, port, obd):
-    ost_uuid = get_uuid(ost)
-    obd_uuid = get_uuid(obd)
-
-    str ="""
-<ost name="%s" uuid="%s">
-   <network type="tcp">
-      <server>%s</server>
-      <port>%d</port>
-   </network>
-   <server_id num="1" name="%s" uuid="%s"/>
-</ost> """ % (ost, ost_uuid, host, port, obd, obd_uuid)
-    node = xml.dom.minidom.parseString(str)
-    return node.getElementsByTagName("ost")[0]
+    
+def add_node(gen, lustre, options, args):
+    """ create a node with a network config """
+    if len(args) > 1:
+        usage()
 
-#
-# Create a new obd, osc, and ost. Add them to the DOM.
-#
-def add_OST(dom, options, args):
-    # XXX need some error checking
+    node_name = options['node']
+
+    ret = findByName(lustre, node_name, "node")
+    if ret:
+        print "Node:", node_name, "exists."
+        return
+    do_add_node(gen, lustre, options, node_name)
+
+
+def add_net(gen, lustre, options, args):
+    """ create a node with a network config """
+    if len(args) < 2:
+        usage()
+
+    node_name = options['node']
+    nid = args[0]
+    net_type = args[1]
+    port = 0
+    tcpbuf = 0
+
+    if net_type in ('tcp', 'toe'):
+        if len(args) > 2:
+            port = int(args[2])
+        else:
+            port = DEFAULT_PORT
+        if options.has_key('tcpbuf'):
+            tcpbuf = int(options['tcpbuf'])
+    elif net_type in ('elan', 'gm'):
+        port = 0
+    else:
+        print "Unknown net_type: ", net_type
+        sys.exit(2)
+
+    ret = findByName(lustre, node_name, "node")
+    if not ret:
+        node = do_add_node(gen, lustre, options, node_name)
+    else:
+        node = ret
+    net_name = new_name('NET_'+ node_name +'_'+ net_type)
+    net_uuid = new_uuid(net_name)
+    node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
+    node_add_profile(gen, node, "network", net_uuid)
+
+
+def add_route(gen, lustre, options, args):
+    """ create a node with a network config """
+    if len(args) < 3:
+        usage()
+
+    node_name = options['node']
+    net_type= args[0]
+    gw = args[1]
+    lo = args[2]
+    hi = ''
+
+    if len(args) > 3:
+        hi = args[3]
+
+    node = findByName(lustre, node_name, "node")
+    if not node:
+        error (node_name, " not found.")
+    
+    netlist = node.getElementsByTagName('network')
+    net = netlist[0]
+    rlist = net.getElementsByTagName('route_tbl')
+    if len(rlist) > 0:
+        rtbl = rlist[0]
+    else:
+        rtbl = gen.addElement(net, 'route_tbl')
+    rtbl.appendChild(gen.route(net_type, gw, lo, hi))
+
+
+def add_mds(gen, lustre, options, args):
+    if len(args) < 1:
+        usage()
+
+    if options.has_key('node'):
+        node_name = options['node']
+    else:
+        error("--mds requires a --node argument")
+
+    mds_name = new_name(options['mds'])
+    if mds_name != options['mds']:
+        warning("name:", options['mds'], "already used. using:", mds_name)
     devname = args[0]
-    host = args[1]
-    size = args[2]
+    if len(args) > 1:
+        size = args[1]
+    else:
+        size = 0
+
+    mds_uuid = new_uuid(mds_name)
+
+    node_uuid = name2uuid(lustre, node_name, 'node')
 
-    obdname = new_name("obd")
-    oscname = new_name("osc")
-    ostname = new_name("ost")
+    node = findByName(lustre, node_name, "node")
+    node_add_profile(gen, node, "mds", mds_uuid)
+    net_uuid = get_net_uuid(lustre, node_name)
+    if not net_uuid:
+        error("NODE: ", node_name, "not found")
+
+
+    mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
+                  net_uuid, node_uuid, dev_size=size)
+    lustre.appendChild(mds)
+                   
+
+def add_ost(gen, lustre, options, args):
+    lovname = ''
+    obdtype = 'obdfilter'
+    devname = ''
+    size = 0
+    fstype = 'extN'
     
-    node = new_node(dom, "tcp", host, 2436)
-    obd = new_OBD(dom, obdname, "extN", devname, "no", size)
-    osc = new_OSC(dom, oscname, obdname)
-    ost = new_OST(dom, ostname, host, 2020, obdname)
+    if options.has_key('node'):
+        node_name = options['node']
+    else:
+        error("--ost requires a --node argument")
+
+    if options.has_key('lov'):
+        lovname = options['lov']
+
+    if options.has_key('obdtype'):
+        obdtype = options['obdtype']
+    if obdtype == 'obdecho':
+        fstype = ''
+    else:
+        if len(args) < 1:
+            usage()
+        devname = args[0]
+        if len(args) > 1:
+            size = args[1]
+        
+    obdname = new_name('OBD_'+ node_name)
+    oscname = new_name('OSC_'+ node_name)
+    ostname = new_name('OST_'+ node_name)
+    if options.has_key('obduuid'):
+        obd_uuid = options['obduuid']
+        obd = lookup(lustre, obd_uuid)
+        if obd:
+            error("Duplicate OBD UUID:", obd_uuid)
+    else:
+        obd_uuid = new_uuid(obdname)
+    ost_uuid = new_uuid(ostname)
+    osc_uuid = new_uuid(oscname)
+
+    net_uuid = get_net_uuid(lustre, node_name)
+    if not net_uuid:
+        error("NODE: ", node_name, "not found")
     
-    lustre = dom.getElementsByTagName("lustre")[0]
-    lustre.appendChild(node)
-    newline(dom, lustre)
+    obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
+    ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
+    osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
+    
+    if lovname:
+        lov = findByName(lustre, lovname, "lov")
+        if not lov:
+            error('add_ost:', '"'+lovname+'"', "lov element not found.")
+        lov_add_osc(gen, lov, osc_uuid)
+
+    node = findByName(lustre, node_name, "node")
+    node_add_profile(gen, node, 'obd', obd_uuid)
+    node_add_profile(gen, node, 'ost', ost_uuid)
+
     lustre.appendChild(obd)
-    newline(dom, lustre)    
     lustre.appendChild(osc)
-    newline(dom, lustre)    
     lustre.appendChild(ost)
-    newline(dom, lustre)    
 
-#
+                   
+# this is generally only used by llecho.sh
+def add_osc(gen, lustre, options, args):
+    """ add the osc to the profile for this node. """
+    if len(args) < 1:
+        usage()
+    osc_name = args[0]
+    if options.has_key('node'):
+        node_name = options['node']
+    else:
+        error("--osc requires a --node argument")
+    osc_uuid = name2uuid(lustre, osc_name) # either 'osc' or 'lov'
+    node = findByName(lustre, node_name, "node")
+    node_add_profile(gen, node, 'osc', osc_uuid)
+
+
+def add_lov(gen, lustre, options, args):
+    """ create a lov """
+    if len(args) < 4:
+        usage()
+
+    name = new_name(options['lov'])
+    if name != options['lov']:
+        warning("name:", options['lov'], "already used. using:", name)
+
+    mds_name = args[0]
+    stripe_sz = args[1]
+    stripe_count = args[2]
+    pattern = args[3]
+    uuid = new_uuid(name)
+
+    ret = findByName(lustre, name, "lov")
+    if ret:
+        error("LOV: ", name, " already exists.")
+
+    mds_uuid = name2uuid(lustre, mds_name, 'mds')
+    lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
+    lustre.appendChild(lov)
+    
+    # add an lovconfig entry to the mds profile
+    lovconfig_name = new_name('LVCFG_' + name)
+    lovconfig_uuid = new_uuid(lovconfig_name)
+    node = mds2node(lustre, mds_name)
+    node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
+    lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
+    lustre.appendChild(lovconfig)
+
+
+
+def add_mtpt(gen, lustre, options, args):
+    """ create mtpt on a node """
+    if len(args) < 3:
+        usage()
+
+    if options.has_key('node'):
+        node_name = options['node']
+    else:
+        error("--mtpt requires a --node argument")
+
+    path = args[0]
+    mds_name = args[1]
+    lov_name = args[2]
+
+    name = new_name('MNT_'+ node_name)
+
+    ret = findByName(lustre, name, "mountpoint")
+    if ret:
+        error("MOUNTPOINT: ", name, " already exists.")
+
+    mds_uuid = name2uuid(lustre, mds_name, tag='mds')
+    lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
+    if not lov_uuid:
+        lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
+
+    uuid = new_uuid(name)
+    mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
+    node = findByName(lustre, node_name, "node")
+    if not node:
+            error('node:',  node_name, "not found.")
+    node_add_profile(gen, node, "mountpoint", uuid)
+    lustre.appendChild(mtpt)
+
+
+############################################################
 # Command line processing
 #
-
 def parse_cmdline(argv):
-    short_opts = "ho:"
-    long_opts = ["ost", "mtpt", "lov",
-                 "merge=", "format", "reformat", "output=",
-                 "help"]
+    short_opts = "ho:i:m:"
+    long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
+                 "route", "router", "merge=", "format", "reformat", "output=",
+                 "obdtype=", "obduuid=", "in=", "help", "batch="]
     opts = []
     args = []
     options = {}
     try:
         opts, args = getopt.getopt(argv, short_opts, long_opts)
-    except getopt.GetoptError:
+    except getopt.error:
         print "invalid opt"
         usage()
-        sys.exit(2)
 
     for o, a in opts:
+        # Commands to create new devices
+        if o == "--ost":
+            options['ost'] = 1
+        if o == "--osc":
+            options['osc'] = 1
+        if o == "--mds":
+            options['mds'] = a
+        if o == "--net":
+            options['net'] = 1
+        if o == "--mtpt":
+            options['mtpt'] = 1
+        if o == "--node":
+            options['node'] = a
+        if o == "--route":
+            options['route'] = 1
+        if o == "--router":
+            options['router'] = 1
+        if o == "--lov":
+            options['lov'] = a
+
+        # Options for commands
+        if o == "--obdtype":
+            options['obdtype'] = a
+        if o == "--obduuid":
+            options['obduuid'] = a
+        if o == "--tcpbuf":
+            options['tcpbuf'] = a
+
+        # lmc options
         if o in ("-h", "--help"):
             usage()
-            sys.exit()
         if o in ("-o", "--output"):
             options['output'] = a
-        if o == "--ost":
-            options['ost'] = 1
-        if o == "--merge":
+        if o in ("-m", "--merge"):
             options['merge'] = a
         if o == "--format":
             options['format'] = 1
         if o  == "--reformat":
             options['reformat'] = 1
+        if o  == "--batch":
+            options['batch'] = a
+        if o  in ("--in" , "-i"):
+            options['in'] = a
             
     return options, args
 
+
+# simple class for profiling
+import time
+class chrono:
+    def __init__(self):
+        self._start = 0
+    def start(self):
+        self._stop = 0
+        self._start = time.time()
+    def stop(self, msg=''):
+        self._stop = time.time()
+        if msg:
+            self.display(msg)
+    def dur(self):
+        return self._stop - self._start
+    def display(self, msg):
+        d = self.dur()
+        str = '%s: %g secs' % (msg, d)
+        print str
+
+############################################################
+# Main
+#
+def do_command(gen, lustre, options, args):
+    if options.has_key('ost'):
+        add_ost(gen, lustre, options, args)
+    elif options.has_key('osc'):
+        add_osc(gen, lustre, options, args)
+    elif options.has_key('mtpt'):
+        add_mtpt(gen, lustre, options, args)
+    elif options.has_key('mds'):
+        add_mds(gen, lustre, options, args)
+    elif options.has_key('net'):
+        add_net(gen, lustre, options, args)
+    elif options.has_key('lov'):
+        add_lov(gen, lustre, options, args)
+    elif options.has_key('route'):
+        add_route(gen, lustre, options, args)
+    elif options.has_key('node'):
+        add_node(gen, lustre, options, args)
+    else:
+        print "Missing command"
+        usage()
+
 def main():
     options, args = parse_cmdline(sys.argv[1:])
     outFile = '-'
 
     if options.has_key('merge'):
         outFile = options['merge']
-        dom = xml.dom.minidom.parse(outFile)
+        if os.access(outFile, os.R_OK):
+            doc = xml.dom.minidom.parse(outFile)
+        else:
+            doc = new_lustre(xml.dom.minidom)
+    elif options.has_key('in'):
+        doc = xml.dom.minidom.parse(options['in'])
     else:
-        dom = new_Lustre()
+        doc = new_lustre(xml.dom.minidom)
 
     if options.has_key('output'):
         outFile = options['output']
 
-    if options.has_key('ost'):
-        add_OST(dom, options, args)
-    elif options.has_key('mtpt'):
-        print "--mtpt not implemented"
-    elif options.has_key('lov'):
-        print "--lov not implemented"
-    else:
-        print "Missing command"
-        usage()
+    lustre = doc.documentElement
+    init_names(lustre)
+    if lustre.tagName != "lustre":
+        print "Existing config not valid."
         sys.exit(1)
-        
+
+    gen = GenConfig(doc)
+
+    if options.has_key('batch'):
+        fp = open(options['batch'])
+        batchCommands = fp.readlines()
+        fp.close()
+        for cmd in batchCommands:
+            options, args = parse_cmdline(string.split(cmd))
+            do_command(gen, lustre, options, args)
+    else:
+        do_command(gen, lustre, options, args)
 
     if outFile == '-':
-        print dom.toxml()
+        PrettyPrint(doc)
     else:
-        dom.writexml(open(outFile,"w"))
-    
+        PrettyPrint(doc, open(outFile,"w"))
+
 if __name__ == "__main__":
     main()
 
+