Whamcloud - gitweb
Fix OST AMD - mark new OST active when it is added.
[fs/lustre-release.git] / lustre / utils / lmc
index a341dd8..335572a 100755 (executable)
@@ -35,7 +35,7 @@ def printDoc(doc, stream=sys.stdout):
     except ImportError:
         stream.write(doc.toxml())
         stream.write("\n")
-    
+
 
 PYMOD_DIR = "/usr/lib/lustre/python"
 
@@ -50,7 +50,7 @@ if not development_mode():
 
 import Lustre
 
-DEFAULT_PORT = 988 
+DEFAULT_PORT = 988
 DEFAULT_STRIPE_SZ = 1048576
 DEFAULT_STRIPE_CNT = 1
 DEFAULT_STRIPE_PATTERN = 0
@@ -73,8 +73,8 @@ Object creation command summary:
 --add net
   --node node_name
   --nid nid
-  --cluster_id 
-  --nettype tcp|elan|gm|scimac
+  --cluster_id
+  --nettype tcp|elan|gm
   --hostaddr addr
   --port port
   --tcpbuf size
@@ -84,12 +84,18 @@ Object creation command summary:
 --add mds
   --node node_name
   --mds mds_name
+  --failover
   --dev path
-  --fstype extN|ext3
+  --backdev path
+  --fstype ldiskfs|ext3
+  --backfstype ldiskfs|ext3|tmpfs
   --size size
   --nspath
   --journal_size size
   --inode_size size
+  --lmv lmv_name
+  --mkfsoptions options
+  --mountfsoptions options
 
 --add lov
   --lov lov_name
@@ -97,29 +103,49 @@ Object creation command summary:
   --stripe_sz num
   --stripe_cnt num
   --stripe_pattern num
+  --lmv lmv_name
 
 --add ost
   --node node_name
   --ost ost_name 
+  --failover
   --lov lov_name 
+  --index index
   --dev path
+  --backdev path
   --size size
-  --fstype extN|ext3
+  --fstype ldiskfs|ext3
+  --backfstype ldiskfs|ext3|tmpfs
   --journal_size size
   --inode_size size
-  --obdtype obdecho|obdfilter
+  --osdtype obdecho|obdfilter
   --ostuuid uuid
+  --mkfsoptions options
+  --mountfsoptions options
+  --nspath
  
+--delete ost
+  --node node_name
+  --ost ost_name
+  --migrate
+--deactivate ost
+  --node node_name
+  --ost ost_name
+
 --add mtpt  - Mountpoint
   --node node_name
   --path /mnt/point
   --mds mds_name
+  --lmv lmv_name
   --ost ost_name OR --lov lov_name
+  --clientoptions options
 
 --add route
   --node nodename
+  --router
   --gw nid
-  --tgt nid
+  --gateway_cluster_id nid
+  --target_cluster_id nid
   --lo nid
   --hi nid
 
@@ -129,12 +155,27 @@ Object creation command summary:
 --add mgmt  - Management/monitoring service
   --node node_name
   --mgmt mgmt_service_name
+
+--add lmv
+  --lmv lmv_name
+
+--add cobd
+  --node node_name
+  --real_obd obd_name
+  --cache_obd obd_name
+
+--add cmobd
+  --node node_name
+  --master_dev obd_name
+  --cache_dev  obd_name
+
+--commit - Close a configuration version, and start a new one
 """
 
 PARAM = Lustre.Options.PARAM
 lmc_options = [
     # lmc input/output options
-    ('reference', "Print short reference for commands."), 
+    ('reference', "Print short reference for commands."),
     ('verbose,v', "Print system commands as they are run."),
     ('merge,m', "Append to the specified config file.", PARAM),
     ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
@@ -143,7 +184,10 @@ lmc_options = [
 
     # commands
     ('add', "", PARAM),
-    
+    ('delete', "", PARAM),
+    ('deactivate', "", PARAM),
+    ('commit', "Commit all config changes and start a new version"),
+
     # node options
     ('node', "Add a new node in the cluster configuration.", PARAM),
     ('timeout', "Set timeout to initiate recovery.", PARAM),
@@ -153,8 +197,8 @@ lmc_options = [
     ('ptldebug', "Set the portals debug level",  PARAM),
     ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log",  PARAM),
 
-    # network 
-    ('nettype', "Specify the network type. This can be tcp/elan/gm/scimac.", PARAM),
+    # network
+    ('nettype', "Specify the network type. This can be tcp/elan/gm.", PARAM),
     ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
     ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"),
     ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
@@ -172,37 +216,54 @@ lmc_options = [
     ('hi', "For a range route, this is a hi value nid.", PARAM,""),
 
     # servers: mds and ost
-    ('mds', "Specify MDS name.", PARAM),
+    ('mds', "Specify MDS name.", PARAM,""),
     ('ost', "Specify the OST name.", PARAM,""),
     ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
     ('failover', "Enable failover support on OSTs or MDS?"),
     ('group', "", PARAM),
     ('dev', "Path of the device on local system.", PARAM,""),
+    ('backdev', "Path of the device for backing storage on local system.", PARAM,""),
     ('size', "Specify the size of the device if needed.", PARAM,"0"),
-    ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"),
-    ('inode_size', "Specify new inode size for underlying ext3 file system.", PARAM,"0"),
+    ('journal_size', "Specify new journal size for underlying file system.", PARAM,"0"),
+    ('inode_size', "Specify new inode size for underlying file system.", PARAM,"0"),
     ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
+    ('backfstype', "Optional argument to specify the backing filesystem type.", PARAM, "ext3"),
     ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
+    ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""),
     ('ostuuid', "", PARAM,""),
     ('nspath', "Local mount point of server namespace.", PARAM,""),
     ('format', ""),
+    ('migrate', "used for offline migrate of an ost in conjunctio with add/delete"),
 
     # clients: mountpoint and echo
     ('echo_client', "", PARAM),
     ('path', "Specify the mountpoint for Lustre.", PARAM),
     ('filesystem', "Lustre filesystem name", PARAM,""),
+    ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""),
 
     # lov
     ('lov', "Specify LOV name.", PARAM,""),
+    ('index', "Specify index for OBD in LOV target table.", PARAM),
     ('stripe_sz', "Specify the stripe size in bytes.", PARAM),
     ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0),
     ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
 
     # cobd
-    ('real_obd', "", PARAM),
-    ('cache_obd', "", PARAM),
+    
+    ('real_obd', "Specify the real device for the cache obd system.", PARAM),
+    ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
+    ('cobd', "Specify COBD name", PARAM),
+
+    # cmobd
+    ('master_dev', "Specify the master device for the cmobd system.", PARAM),
+    ('cache_dev',  "Specify the cache device for the cmobd obd system.", PARAM),
+    ('cmobd',      "Specify COBD name", PARAM),
+
 
     ('mgmt', "Specify management/monitoring service name.", PARAM, ""),
+
+    # lmv
+    ('lmv', "Specify LMV name.", PARAM,""),
     ]
 
 def error(*args):
@@ -214,11 +275,11 @@ def panic(cmd, msg):
     print 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
@@ -293,14 +354,20 @@ class GenConfig:
         ref = self.doc.createElement(tag)
         ref.setAttribute("uuidref", uuid)
         return ref
-    
+       
+    def dev(self, devname):
+        """ generate <dev devpath="[devname]"/> """
+       tgt = self.doc.createElement('dev')
+        tgt.setAttribute("dev", devname)
+       return tgt
+
     def newService(self, tag, name, uuid):
         """ create a new  service elmement, which requires name and uuid attributes """
         new = self.doc.createElement(tag)
         new.setAttribute("uuid", uuid);
         new.setAttribute("name", name);
         return new
-    
+
     def addText(self, node, str):
         txt = self.doc.createTextNode(str)
         node.appendChild(txt)
@@ -330,14 +397,14 @@ class GenConfig:
             self.addElement(network, "recvmem", "%d" %(tcpbuf))
         if irq_aff:
             self.addElement(network, "irqaffinity", "%d" %(irq_aff))
-            
+
         return network
 
     def routetbl(self, name, uuid):
         """create <routetbl> node"""
         rtbl = self.newService("routetbl", name, uuid)
         return rtbl
-        
+
     def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
         """ create one entry for the route table """
         ref = self.doc.createElement('route')
@@ -349,7 +416,7 @@ class GenConfig:
         if hi:
             ref.setAttribute("hi", hi)
         return ref
-    
+
     def profile(self, name, uuid):
         """ create a host """
         profile = self.newService("profile", name, uuid)
@@ -366,14 +433,21 @@ class GenConfig:
         ldlm = self.newService("ldlm", name, uuid)
         return ldlm
 
-    def osd(self, name, uuid, fs, osdtype, devname, format, ost_uuid,
-            node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", mkfsoptions=""):
+    def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
+            node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", 
+            mkfsoptions="", mountfsoptions="", backfstype="", backdevname=""):
         osd = self.newService("osd", name, uuid)
         osd.setAttribute('osdtype', osdtype)
         osd.appendChild(self.ref("target", ost_uuid))
         osd.appendChild(self.ref("node", node_uuid))
-        if fs:
-            self.addElement(osd, "fstype", fs)
+       osd.appendChild(self.dev(devname)) 
+
+       if fstype:
+            self.addElement(osd, "fstype", fstype)
+        if backfstype:
+            self.addElement(osd, "backfstype", backfstype)
+        if backdevname:
+            self.addElement(osd, "backdevpath", backdevname)
         if devname:
             dev = self.addElement(osd, "devpath", devname)
             self.addElement(osd, "autoformat", format)
@@ -385,6 +459,8 @@ class GenConfig:
                 self.addElement(osd, "inodesize", "%s" % (inode_size))
             if mkfsoptions:
                 self.addElement(osd, "mkfsoptions", mkfsoptions)
+            if mountfsoptions:
+                self.addElement(osd, "mountfsoptions", mountfsoptions)
         if nspath:
             self.addElement(osd, "nspath", nspath)
         return osd
@@ -395,6 +471,12 @@ class GenConfig:
         cobd.appendChild(self.ref("cacheobd",cache_uuid))
         return cobd
 
+    def cmobd(self, name, uuid, real_uuid, cache_uuid):
+        cmobd = self.newService("cmobd", name, uuid)
+        cmobd.appendChild(self.ref("masterobd",real_uuid))
+        cmobd.appendChild(self.ref("cacheobd",cache_uuid))
+        return cmobd
+
     def ost(self, name, uuid, osd_uuid, group=""):
         ost = self.newService("ost", name, uuid)
         ost.appendChild(self.ref("active", osd_uuid))
@@ -414,24 +496,41 @@ class GenConfig:
         lov.setAttribute("stripepattern", str(pattern))
         return lov
 
+    def lov_tgt(self, obd_uuid, index, generation):
+        tgt = self.doc.createElement('lov_tgt')
+        tgt.setAttribute("uuidref", obd_uuid)
+        tgt.setAttribute("index", index)
+        tgt.setAttribute("generation", generation)
+        tgt.setAttribute("active", '1')
+        return tgt
+
     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, mdd_uuid, group=""):
+    def lmv(self, name, uuid):
+        lmv = self.newService("lmv", name, uuid)
+        return lmv
+
+    def mds(self, name, uuid, mdd_uuid, group="", lmv=""):
         mds = self.newService("mds", name, uuid)
         mds.appendChild(self.ref("active",mdd_uuid))
         if group:
             self.addElement(mds, "group", group)
         return mds
 
-    def mdsdev(self, name, uuid, fs, devname, format, node_uuid,
+    def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
                mds_uuid, dev_size=0, journal_size=0, inode_size=256,
-               nspath="", mkfsoptions=""):
+               nspath="", mkfsoptions="", mountfsoptions="", backfstype="", 
+               backdevname="", lmv_uuid=""):
         mdd = self.newService("mdsdev", name, uuid)
-        self.addElement(mdd, "fstype", fs)
+        self.addElement(mdd, "fstype", fstype)
+        if backfstype:
+                self.addElement(mdd, "backfstype", backfstype)
         dev = self.addElement(mdd, "devpath", devname)
+        if backdevname:
+            self.addElement(mdd, "backdevpath", backdevname)
         self.addElement(mdd, "autoformat", format)
         if dev_size:
                 self.addElement(mdd, "devsize", "%s" % (dev_size))
@@ -443,8 +542,17 @@ class GenConfig:
             self.addElement(mdd, "nspath", nspath)
         if mkfsoptions:
             self.addElement(mdd, "mkfsoptions", mkfsoptions)
+        if mountfsoptions:
+            self.addElement(mdd, "mountfsoptions", mountfsoptions)
+
         mdd.appendChild(self.ref("node", node_uuid))
         mdd.appendChild(self.ref("target", mds_uuid))
+       mdd.appendChild(self.dev(devname)) 
+       
+       if lmv_uuid:
+            mdd.appendChild(self.ref("lmv", lmv_uuid))
+           self.addElement(mdd, "lmv", lmv_uuid)
+
         return mdd
 
     def mgmt(self, mgmt_name, mgmt_uuid, node_uuid):
@@ -454,10 +562,12 @@ class GenConfig:
         mgmt.appendChild(self.ref("active", mgmt_uuid))
         return mgmt
 
-    def mountpoint(self, name, uuid, fs_uuid, path):
+    def mountpoint(self, name, uuid, fs_uuid, path, clientoptions):
         mtpt = self.newService("mountpoint", name, uuid)
         mtpt.appendChild(self.ref("filesystem", fs_uuid))
         self.addElement(mtpt, "path", path)
+        if clientoptions:
+            self.addElement(mtpt, "clientoptions", clientoptions)
         return mtpt
 
     def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid):
@@ -467,12 +577,36 @@ class GenConfig:
         if mgmt_uuid:
             fs.appendChild(self.ref("mgmt", mgmt_uuid))
         return fs
-        
+
     def echo_client(self, name, uuid, osc_uuid):
         ec = self.newService("echoclient", name, uuid)
         ec.appendChild(self.ref("obd", osc_uuid))
         return ec
 
+    def update(self, version):
+        new = self.doc.createElement("update")
+        new.setAttribute("version", version)
+        return new
+
+    def add(self, lov, ost, index, gen):
+        new = self.doc.createElement("add")
+        new.setAttribute("lov_uuidref", lov)
+        new.setAttribute("ost_uuidref", ost)
+        new.setAttribute("index", index)
+        new.setAttribute("generation", gen)
+        return new
+
+    def delete(self, lov, ost, index, gen, options):
+        if options.delete:
+            new = self.doc.createElement("delete")
+        else:
+            new = self.doc.createElement("deactivate")
+        new.setAttribute("lov_uuidref", lov)
+        new.setAttribute("ost_uuidref", ost)
+        new.setAttribute("index", index)
+        new.setAttribute("generation", gen)
+        return new
+
 ############################################################
 # Utilities to query a DOM tree
 # Using this functions we can treat use config information
@@ -483,6 +617,40 @@ def getName(n):
 def getUUID(node):
     return node.getAttribute('uuid')
 
+def findLastUpdate(lustre):
+    node = None
+    version = 0
+    for n in lustre.childNodes:
+        if n.nodeType == n.ELEMENT_NODE:
+            if n.nodeName != 'update':
+                continue
+            tmp = int(n.getAttribute('version'))
+            if not tmp:
+                error('malformed XML: update tag without a version attribute')
+            if tmp != version + 1:
+                error('malformed XML: expecting update record '+str(version + 1)+', found '+str(tmp)+'.')
+            version = tmp
+            node = n
+    return node
+
+def addUpdate(gen, lustre, node):
+    update = findLastUpdate(lustre)
+    if not update:
+        return
+    #add_record = update.getElementsByTagName('add')
+    #if not add_record:
+    #    add_record = gen.add()
+    #    update.appendChild(add_record)
+    #else:
+    #    add_record = add_record[0]
+    #add_record.appendChild(node)
+    update.appendChild(node)
+
+def delUpdate(gen, lustre, node):
+    update = findLastUpdate(lustre)
+    if not update:
+        return
+    update.appendChild(node)
 
 def findByName(lustre, name, tag = ""):
     for n in lustre.childNodes:
@@ -516,7 +684,7 @@ def name2uuid(lustre, name, tag="",  fatal=1):
         else:
             return ""
     return getUUID(ret)
-    
+
 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
     for n in lustre.childNodes:
         if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
@@ -536,9 +704,91 @@ def get_net_uuid(lustre, node_name):
         return getUUID(net[0])
     return None
 
-
-def lov_add_obd(gen, lov, osc_uuid):
-    lov.appendChild(gen.ref("obd", osc_uuid))
+def lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options):
+    tgt.setAttribute('uuidref', osc_uuid)
+    if options.migrate:
+        gener = int(tgt.getAttribute('generation'))
+    else:
+        gener = int(tgt.getAttribute('generation')) + 1
+    tgt.setAttribute('generation', str(gener))
+    tgt.setAttribute('active', '1')
+    lov_index = int(tgt.getAttribute('index'))
+    addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index),
+              str(gener)))
+    return
+
+def lov_add_obd(gen, lustre, lov, osc_uuid, options):
+    lov_name = getName(lov)
+    if options.index:
+        lov_index = get_option_int(options, 'index')
+        for tgt in lustre.getElementsByTagName('lov_tgt'):
+            if str(lov_index) == tgt.getAttribute('index'):
+                uuidref = tgt.getAttribute('uuidref')
+                if uuidref != '':
+                    raise OptionError("%s --index %d is still in use: %s" %
+                                      (lov_name, lov_index, uuidref))
+                lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options)
+                return
+    else:
+         lov_index = 0
+         for tgt in lustre.getElementsByTagName('lov_tgt'):
+             uuidref = tgt.getAttribute('uuidref')
+             tmp = int(tgt.getAttribute('index'))
+             if tmp != lov_index:
+                 error('malformed xml: LOV targets are not ordered; found index '+str(tmp)+', expected '+str(lov_index)+'.')
+             uuidref = tgt.getAttribute('uuidref')
+             if uuidref == '':
+                 lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options)
+                 return
+             lov_index = lov_index + 1
+
+    lov.appendChild(gen.lov_tgt(osc_uuid, str(lov_index), '1'))
+    addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), '1'))
+
+def lov_del_obd(gen, lustre, lov, osc_uuid, options):
+    lov_name = getName(lov)
+    if options.index:
+        lov_index = get_option_int(options, 'index')
+        for tgt in lustre.getElementsByTagName('lov_tgt'):
+            index = tgt.getAttribute('index')
+            if index == lov_index:
+                uuidref = tgt.getAttribute('uuidref')
+                if uuidref != osc_uuid:
+                    raise OptionError("%s --index %d contains %s, not %s" %
+                                      (lov_name, lov_index, osc_uuid, uuidref))
+                if options.delete:
+                    tgt.setAttribute('uuidref', '')
+
+                # bump the generation just in case...
+                if options.migrate:
+                    gen = int(tgt.getAttribute('generation'))
+                else:
+                    gen = int(tgt.getAttribute('generation')) + 1
+
+                tgt.setAttribute('active', '0')
+                tgt.setAttribute('generation', str(gen))
+                return
+        raise OptionError("%s --index %d not in use by %s." %
+                          (lov_name, lov_index, osc_uuid))
+
+    for tgt in lustre.getElementsByTagName('lov_tgt'):
+        uuidref = tgt.getAttribute('uuidref')
+        if uuidref == osc_uuid:
+            genera = int(tgt.getAttribute('generation'))
+            delete_rec = gen.delete(getUUID(lov),
+                                    osc_uuid,tgt.getAttribute('index'),
+                                    str(genera), options)
+            delUpdate(gen, lustre, delete_rec)
+
+            if options.delete:
+                tgt.setAttribute('uuidref', '')
+            if not options.migrate:
+                genera = genera + 1
+            tgt.setAttribute('active', '0')
+            tgt.setAttribute('generation', str(genera))
+
+def lmv_add_obd(gen, lmv, mdc_uuid):
+    lmv.appendChild(gen.ref("mds", mdc_uuid))
                             
 def ref_exists(profile, uuid):
     elist = profile.childNodes
@@ -548,7 +798,7 @@ def ref_exists(profile, uuid):
             if ref == uuid:
                 return 1
     return 0
-        
+
 # ensure that uuid is not already in the profile
 # return true if uuid is added
 def node_add_profile(gen, node, ref, uuid):
@@ -564,7 +814,41 @@ def node_add_profile(gen, node, ref, uuid):
         return 0
     profile.appendChild(gen.ref(ref, uuid))
     return 1
-    
+
+# ensure that uuid is not already in the profile
+# return true if uuid is added
+def node_found_target_by_dev(gen, lustre, node, devname):
+    refname = "%s_ref" % "profile"
+    ret = node.getElementsByTagName(refname)
+    if not ret:
+        error('node has no profile ref:', node)
+    prof_uuid = ret[0].getAttribute('uuidref')
+    profile = lookup(node.parentNode, prof_uuid)
+    if not profile:
+        error("no profile found:", prof_uuid)
+   
+    osd_list = lustre.getElementsByTagName('osd')
+
+    for osd in osd_list:
+       obd_dev = osd.getElementsByTagName('dev') 
+       if obd_dev and obd_dev[0].getAttribute('dev') == devname:
+           for ost in lustre.getElementsByTagName('ost'):
+               active_ret = ost.getElementsByTagName('active_ref')
+               if active_ret[0].getAttribute('uuidref') == osd.getAttribute('uuid'):
+                       return ost.getAttribute('uuid')
+    mdsdev_list = lustre.getElementsByTagName('mdsdev')
+
+    for mdsdev in mdsdev_list:
+       obd_dev = mdsdev.getElementsByTagName('dev') 
+       if obd_dev and obd_dev[0].getAttribute('dev') == devname:
+           for mds in lustre.getElementsByTagName('mds'):
+               active_ret = mds.getElementsByTagName('active_ref')
+               if active_ret[0].getAttribute('uuidref') == mdsdev.getAttribute('uuid'):
+                       return mds.getAttribute('uuid')
+   
+    return "" 
+
 def get_attr(dom_node, attr, default=""):
     v = dom_node.getAttribute(attr)
     if v:
@@ -586,7 +870,7 @@ def set_node_options(gen, node, options):
     if default_upcall or options.lustre_upcall:
         if options.lustre_upcall:
             gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
-        else: 
+        else:
             gen.addElement(node, 'lustreUpcall', default_upcall)
     if default_upcall or options.portals_upcall:
         if options.portals_upcall:
@@ -610,9 +894,10 @@ def do_add_node(gen, lustre,  options, node_name):
 
     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
     set_node_options(gen, node, options)
+
     return node
 
-    
+
 def add_node(gen, lustre, options):
     """ create a node with a network config """
 
@@ -637,7 +922,7 @@ def add_net(gen, lustre, options):
         port = get_option_int(options, 'port')
         tcpbuf = get_option_int(options, 'tcpbuf')
         irq_aff = get_option_int(options, 'irq_affinity')
-    elif net_type in ('elan', 'gm', 'scimac'):
+    elif net_type in ('elan', 'gm'):
         port = 0
         tcpbuf = 0
         irq_aff = 0
@@ -675,7 +960,7 @@ def add_route(gen, lustre, options):
     node = findByName(lustre, node_name, "node")
     if not node:
         error (node_name, " not found.")
-    
+
     rlist = node.getElementsByTagName('routetbl')
     if len(rlist) > 0:
         rtbl = rlist[0]
@@ -692,26 +977,40 @@ def add_route(gen, lustre, options):
 def add_mds(gen, lustre, options):
     node_name = get_option(options, 'node')
     mds_name = get_option(options, 'mds')
+    lmv_name = get_option(options, 'lmv')
     mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
     mdd_uuid = new_uuid(mdd_name)
 
+    lmv_uuid = ""
+    if lmv_name:
+        lmv = findByName(lustre, lmv_name, "lmv")
+        if not lmv:
+            error('add_mds:', '"' + lmv_name + '"', "lmv element not found.")
+        lmv_uuid = name2uuid(lustre, lmv_name, fatal=0)
+
     mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
     if not mds_uuid:
         mds_uuid = new_uuid(mds_name)
         mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
         lustre.appendChild(mds)
+        if lmv_name:
+            lmv_add_obd(gen, lmv, mds_uuid)
     else:
         mds = lookup(lustre, mds_uuid)
+
     if options.failover:
         mds.setAttribute('failover', "1")
 
     devname = get_option(options, 'dev')
+    backdevname = get_option(options, 'backdev')
     size = get_option(options, 'size')
     fstype = get_option(options, 'fstype')
+    backfstype = get_option(options, 'backfstype')
     journal_size = get_option(options, 'journal_size')
     inode_size = get_option(options, 'inode_size')
     nspath = get_option(options, 'nspath')
     mkfsoptions = get_option(options, 'mkfsoptions')
+    mountfsoptions = get_option(options, 'mountfsoptions')
 
     node_uuid = name2uuid(lustre, node_name, 'node')
 
@@ -721,11 +1020,15 @@ def add_mds(gen, lustre, options):
     if not net_uuid:
         error("NODE: ", node_name, "not found")
 
+    if lmv_name:
+        mds.appendChild(gen.ref("lmv", lmv_uuid))
+
     mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
                      get_format_flag(options), node_uuid, mds_uuid,
-                     size, journal_size, inode_size, nspath, mkfsoptions)
+                     size, journal_size, inode_size, nspath, mkfsoptions, 
+                     mountfsoptions, backfstype, backdevname, lmv_uuid)
     lustre.appendChild(mdd)
-                   
+
 
 def add_mgmt(gen, lustre, options):
     node_name = get_option(options, 'node')
@@ -753,20 +1056,25 @@ def add_ost(gen, lustre, options):
 
     if osdtype == 'obdecho':
         fstype = ''
+        backfstype = ''
         devname = ''
+        backdevname = ''
         size = 0
-        fstype = ''
         journal_size = ''
         inode_size = ''
         mkfsoptions = ''
+        mountfsoptions = ''
     else:
         devname = get_option(options, 'dev') # can be unset for bluearcs
+        backdevname = get_option(options, 'backdev')
         size = get_option(options, 'size')
         fstype = get_option(options, 'fstype')
+        backfstype = get_option(options, 'backfstype')
         journal_size = get_option(options, 'journal_size')
         inode_size = get_option(options, 'inode_size')
         mkfsoptions = get_option(options, 'mkfsoptions')
-        
+        mountfsoptions = get_option(options, 'mountfsoptions')
+
     nspath = get_option(options, 'nspath')
 
     ostname = get_option(options, 'ost')
@@ -787,21 +1095,23 @@ def add_ost(gen, lustre, options):
 
         ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
         lustre.appendChild(ost)
-        if lovname:
-            lov = findByName(lustre, lovname, "lov")
-            if not lov:
-                error('add_ost:', '"'+lovname+'"', "lov element not found.")
-            lov_add_obd(gen, lov, ost_uuid)
     else:
         ost = lookup(lustre, ost_uuid)
 
+    if lovname:
+        lov = findByName(lustre, lovname, "lov")
+        if not lov:
+            error('add_ost:', '"'+lovname+'"', "lov element not found.")
+        lov_add_obd(gen, lustre, lov, ost_uuid, options)
+
     if options.failover:
         ost.setAttribute('failover', "1")
-    
+
 
     osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
                   get_format_flag(options), ost_uuid, node_uuid, size,
-                  journal_size, inode_size, nspath, mkfsoptions)
+                  journal_size, inode_size, nspath, mkfsoptions, 
+                  mountfsoptions, backfstype, backdevname)
 
     node = findByName(lustre, node_name, "node")
 
@@ -814,20 +1124,117 @@ def add_ost(gen, lustre, options):
     node_add_profile(gen, node, 'osd', osd_uuid)
     lustre.appendChild(osd)
 
-                   
+def del_ost(gen, lustre, options):
+    ostname = get_option(options, 'ost')
+    if not ostname:
+        raise OptionError("del_ost: --ost requires a <ost name>")
+    ost = findByName(lustre, ostname, "ost")
+    if not ost:
+        error('del_ost: ', 'Unable to find ', ostname)
+    ost_uuid = name2uuid(lustre, ostname, fatal=0)
+    if not ost_uuid:
+        error('del_ost: ', 'Unable to find uuid for ', ostname)
+    lovname = get_option(options, 'lov')
+    if lovname:
+        lov = findByName(lustre, lovname, "lov")
+        if not lov:
+            error('del_ost:', '"'+lovname+'"', "lov element not found.")
+        lov_del_obd(gen, lustre, lov, ost_uuid, options)
+        # if the user specified a speficic LOV don't delete the OST itself
+        return
+
+    # remove OSD references from all LOVs
+    for n in lustre.getElementsByTagName('lov'):
+        lov_del_obd(gen, lustre, n, ost_uuid, options)
+    if not options.migrate:
+        return
+    # delete the OSDs
+    for osd in lustre.getElementsByTagName('osd'):
+        if ref_exists(osd, ost_uuid):
+            osd_uuid = osd.getAttribute('uuid')
+            # delete all profile references to this OSD
+            for profile in lustre.getElementsByTagName('profile'):
+                for osd_ref in profile.getElementsByTagName('osd_ref'):
+                    if osd_uuid == osd_ref.getAttribute('uuidref'):
+                        profile.removeChild(osd_ref)
+            lustre.removeChild(osd)
+
+    # delete the OST
+    lustre.removeChild(ost)
+
+def add_cmobd(gen, lustre, options):
+    node_name = get_option(options, 'node')
+    name = get_option(options, 'cmobd')
+    uuid = new_uuid(name)
+
+    real_name = get_option(options, 'master_dev')
+    cache_name = get_option(options, 'cache_dev')
+
+    node = findByName(lustre, node_name, "node")
+    node_add_profile(gen, node, "cmobd", uuid)
+    real_uuid = node_found_target_by_dev(gen, lustre, node, real_name)     
+    cache_uuid = node_found_target_by_dev(gen, lustre, node, cache_name)
+    if not real_uuid: 
+       panic("add_cmobd", "can not find real_uuid")
+    if not cache_uuid: 
+       panic("add_cmobd", "can not find cache_uuid")
+    cmobd = gen.cmobd(name, uuid, real_uuid, cache_uuid)
+    lustre.appendChild(cmobd)
+
 def add_cobd(gen, lustre, options):
     node_name = get_option(options, 'node')
-    name = new_name('COBD_' + node_name)
+    name = get_option(options, 'cobd')
     uuid = new_uuid(name)
 
     real_name = get_option(options, 'real_obd')
     cache_name = get_option(options, 'cache_obd')
-    
-    real_uuid = name2uuid(lustre, real_name, tag='obd')
-    cache_uuid = name2uuid(lustre, cache_name, tag='obd')
+
+    real_uuid = name2uuid(lustre, real_name, tag='lov', fatal=0)
+    cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0)
+
+    if real_uuid:
+        node = lookup(lustre, real_uuid)
+        rets = node.getElementsByTagName('lov_tgt')
+        for ret in rets:
+            ost_uuid = ret.getAttribute('uuidref')
+            ost_node = lookup(lustre, ost_uuid)
+            ret = ost_node.getElementsByTagName('active_ref')
+            if ret:
+                osd_uuid = ret[0].getAttribute('uuidref')
+                osd_node = lookup(lustre, osd_uuid)
+                gen.addElement(osd_node, 'cachetype', 'master')
+
+    if cache_uuid:
+        node = lookup(lustre, cache_uuid)
+        rets = node.getElementsByTagName('lov_tgt')
+        for ret in rets:
+            ost_uuid = ret.getAttribute('uuidref')
+            ost_node = lookup(lustre, ost_uuid)
+            ret = ost_node.getElementsByTagName('active_ref')
+            if ret:
+                osd_uuid = ret[0].getAttribute('uuidref')
+                osd_node = lookup(lustre, osd_uuid)
+                gen.addElement(osd_node, 'cachetype', 'cache')
+
+    if not real_uuid or not cache_uuid:
+        real_uuid = name2uuid(lustre,real_name, tag='mds')
+        cache_uuid = name2uuid(lustre,cache_name, tag='mds')
+        if real_uuid:
+            mds_node = lookup(lustre, real_uuid)
+            ret = mds_node.getElementsByTagName('active_ref')
+            if ret:
+                mdsdev_uuid = ret[0].getAttribute('uuidref')
+                mdsdev_node = lookup(lustre, mdsdev_uuid)
+                gen.addElement(mdsdev_node, 'cachetype', 'master')
+        if cache_uuid:
+            mds_node = lookup(lustre, cache_uuid)
+            ret = mds_node.getElementsByTagName('active_ref')
+            if ret:
+                mdsdev_uuid = ret[0].getAttribute('uuidref')
+                mdsdev_node = lookup(lustre, mdsdev_uuid)
+                gen.addElement(mdsdev_node, 'cachetype', 'cache')
 
     node = findByName(lustre, node_name, "node")
-    node_add_profile(gen, node, "cobd", uuid)
     cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
     lustre.appendChild(cobd)
 
@@ -854,12 +1261,17 @@ def add_echo_client(gen, lustre, options):
 def add_lov(gen, lustre, options):
     """ create a lov """
 
+    lmv_name = get_option(options, 'lmv')
     lov_orig = get_option(options, 'lov')
     name = new_name(lov_orig)
     if name != lov_orig:
         warning("name:", lov_orig, "already used. using:", name)
 
     mds_name = get_option(options, 'mds')
+    if not mds_name:
+       if not lmv_name:
+           error("LOV: MDS or LMV must be specified.");
+
     stripe_sz = get_option_int(options, 'stripe_sz')
     stripe_cnt = get_option_int(options, 'stripe_cnt')
     pattern = get_option_int(options, 'stripe_pattern')
@@ -869,34 +1281,42 @@ def add_lov(gen, lustre, options):
     if ret:
         error("LOV: ", name, " already exists.")
 
-    mds_uuid = name2uuid(lustre, mds_name, 'mds')
+    if not mds_name:
+        mds_uuid = name2uuid(lustre, lmv_name, 'lmv')
+    else:
+        mds_uuid = name2uuid(lustre, mds_name, 'mds')
+
     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
     lustre.appendChild(lov)
-    
+
     # add an lovconfig entry to the active mdsdev profile
     lovconfig_name = new_name('LVCFG_' + name)
     lovconfig_uuid = new_uuid(lovconfig_name)
-    mds = findByName(lustre, mds_name, "mds")
-    mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
+    if mds_name:
+        mds = findByName(lustre, mds_name, "mds")
+        mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
+    if lmv_name:
+        lmv = findByName(lustre, lmv_name, "lmv")
+        lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
     lustre.appendChild(lovconfig)
 
 def add_default_lov(gen, lustre, mds_name, lov_name):
     """ create a default lov """
-                                                                                                                                               
+
     stripe_sz = DEFAULT_STRIPE_SZ
     stripe_cnt = DEFAULT_STRIPE_CNT
     pattern = DEFAULT_STRIPE_PATTERN
     uuid = new_uuid(lov_name)
-                                                                                                                                               
+
     ret = findByName(lustre, lov_name, "lov")
     if ret:
         error("LOV: ", lov_name, " already exists.")
-                                                                                                                                               
+
     mds_uuid = name2uuid(lustre, mds_name, 'mds')
     lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
     lustre.appendChild(lov)
-                                                                                                                                               
+
     # add an lovconfig entry to the active mdsdev profile
     lovconfig_name = new_name('LVCFG_' + lov_name)
     lovconfig_uuid = new_uuid(lovconfig_name)
@@ -905,18 +1325,52 @@ def add_default_lov(gen, lustre, mds_name, lov_name):
     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
     lustre.appendChild(lovconfig)
 
+def add_lmv(gen, lustre, options):
+    """ create a lmv """
+
+    lmv_orig = get_option(options, 'lmv')
+    name = new_name(lmv_orig)
+    if name != lmv_orig:
+        warning("name:", lmv_orig, "already used. using:", name)
+
+    uuid = new_uuid(name)
+    ret = findByName(lustre, name, "lmv")
+    if ret:
+        error("LMV: ", name, " already exists.")
+
+    lmv = gen.lmv(name, uuid)
+    lustre.appendChild(lmv)
+
 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
     fs_name = new_name("FS_fsname")
     fs_uuid = new_uuid(fs_name)
-    mds = lookup(lustre, mds_uuid)
-    mds.appendChild(gen.ref("filesystem", fs_uuid))
+    cobd = lookup(lustre, mds_uuid)
+    #SHOULD appendChild filesystem to real mds not cobd
+    ret = cobd.getElementsByTagName("cacheobd_ref")
+    if ret:
+        cacheobd_uuid = ret[0].getAttribute('uuidref') 
+        cacheobd = lookup(lustre, cacheobd_uuid)
+        cacheobd.appendChild(gen.ref("filesystem", fs_uuid))
+        ret = cobd.getElementsByTagName("realobd_ref")
+        if ret:
+            realobd_uuid = ret[0].getAttribute('uuidref')
+            realobd = lookup(lustre, realobd_uuid)
+            realobd.appendChild(gen.ref("filesystem", fs_uuid))
+    else:
+        cobd.appendChild(gen.ref("filesystem", fs_uuid))
     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid)
     lustre.appendChild(fs)
     return fs_uuid
 
 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
-    mds_uuid = name2uuid(lustre, mds_name, tag='mds')
+    mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0)
+    if not mds_uuid:
+        mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0)
+    if not mds_uuid:
+        mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=1) 
     obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
+    if obd_uuid == '':
+       obd_uuid = name2uuid(lustre, obd_name, tag='cobd')
     if mgmt_name:
         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
     else:
@@ -925,17 +1379,22 @@ def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
     if not fs_uuid:
         fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid)
     return fs_uuid
-    
+
 def add_mtpt(gen, lustre, options):
     """ create mtpt on a node """
     node_name = get_option(options, 'node')
 
     path = get_option(options, 'path')
+    clientoptions = get_option(options, "clientoptions")
     fs_name = get_option(options, 'filesystem')
 
     lov_name = get_option(options, 'lov')
     ost_name = get_option(options, 'ost')
     mds_name = get_option(options, 'mds')
+    if mds_name == '':
+        mds_name = get_option(options, 'lmv')
+        if mds_name == '':
+            error("--add mtpt requires either --mds or --lmv.")
     if lov_name == '':
         if ost_name == '':
             error("--add mtpt requires --lov lov_name or --ost ost_name")
@@ -947,7 +1406,7 @@ def add_mtpt(gen, lustre, options):
             if not ost_uuid:
                 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
             lov = findByName(lustre, lov_name, "lov")
-            lov_add_obd(gen, lov, ost_uuid)
+            lov_add_obd(gen, lustre, lov, ost_uuid, options)
 
     if fs_name == '':
         mgmt_name = get_option(options, 'mgmt')
@@ -963,13 +1422,24 @@ def add_mtpt(gen, lustre, options):
         error("MOUNTPOINT: ", name, " already exists.")
 
     uuid = new_uuid(name)
-    mtpt = gen.mountpoint(name, uuid, fs_uuid, path)
+    mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions)
     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)
 
+def commit_version(gen, lustre):
+    update = findLastUpdate(lustre)
+    if update:
+        version = int(update.getAttribute("version")) + 1
+    else:
+        version = 1
+
+    new = gen.update(str(version))
+    lustre.appendChild(new)
+    
+
 ############################################################
 # Command line processing
 #
@@ -991,7 +1461,7 @@ def get_option_int(options, tag):
     try:
         n = int(val)
     except ValueError:
-        raise OptionError("--%s <num> (value must be integer)" % (tag))        
+        raise OptionError("--%s <num> (value must be integer)" % (tag))
     return n
 
 # simple class for profiling
@@ -1017,13 +1487,13 @@ class chrono:
 # function cmdlinesplit used to split cmd line from batch file
 #
 def cmdlinesplit(cmdline):
-                                                                                                                                               
+
     double_quote  = re.compile(r'"(([^"\\]|\\.)*)"')
     single_quote  = re.compile(r"'(.*?)'")
     escaped = re.compile(r'\\(.)')
     esc_quote = re.compile(r'\\([\\"])')
-    outside = re.compile(r"""([^\s\\'"]+)""")
-                                                                                                                                               
+    outside = re.compile(r"""([^\s\\'"]+)""") #" fucking emacs.
+
     arg_list = []
     i = 0; arg = None
     while i < len(cmdline):
@@ -1036,7 +1506,7 @@ def cmdlinesplit(cmdline):
             i = match.end()
             if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
             else:           arg = arg + esc_quote.sub(r'\1', match.group(1))
-                                                                                                                                               
+
         elif c == "'":
             match = single_quote.match(cmdline, i)
             if not match:
@@ -1045,7 +1515,7 @@ def cmdlinesplit(cmdline):
             i = match.end()
             if arg is None: arg = match.group(1)
             else:           arg = arg + match.group(1)
-                                                                                                                                               
+
         elif c == "\\":
             match = escaped.match(cmdline, i)
             if not match:
@@ -1054,7 +1524,7 @@ def cmdlinesplit(cmdline):
             i = match.end()
             if arg is None: arg = match.group(1)
             else:           arg = arg + match.group(1)
-                                                                                                                                               
+
         elif c in string.whitespace:
             if arg != None:
                 arg_list.append(str(arg))
@@ -1067,9 +1537,9 @@ def cmdlinesplit(cmdline):
             i = match.end()
             if arg is None: arg = match.group()
             else:           arg = arg + match.group()
-                                                                                                                                               
+
     if arg != None: arg_list.append(str(arg))
-                                                                                                                                               
+
     return arg_list
 
 ############################################################
@@ -1095,14 +1565,37 @@ def add(devtype, gen, lustre, options):
         add_echo_client(gen, lustre, options)
     elif devtype == 'cobd':
         add_cobd(gen, lustre, options)
+    elif devtype == 'cmobd':
+        add_cmobd(gen, lustre, options)
     elif devtype == 'mgmt':
         add_mgmt(gen, lustre, options)
+    elif devtype == 'lmv':
+        add_lmv(gen, lustre, options)
     else:
         error("unknown device type:", devtype)
-    
+
+def delete(devtype, gen, lustre, options):
+    if devtype == 'ost':
+        del_ost(gen, lustre, options)
+    elif options.delete:
+        error("delete not supported for device type:", devtype)
+    elif options.deactivate:
+        error("deactivate not supported for device type:", devtype)
+    else:
+        error("in delete(), but neither .delete nor .deactivate are set.  Tell CFS.")
+
+def commit(gen, lustre):
+    commit_version(gen, lustre)
+
 def do_command(gen, lustre, options, args):
     if options.add:
         add(options.add, gen, lustre, options)
+    elif options.delete:
+        delete(options.delete, gen, lustre, options)
+    elif options.deactivate:
+        delete(options.deactivate, gen, lustre, options)
+    elif options.commit:
+        commit(gen, lustre)
     else:
         error("Missing command")