Whamcloud - gitweb
6d63f195c81fb6f680ded9bd1e6517d41cf1bbea
[fs/lustre-release.git] / lustre / utils / lmc
1 #!/usr/bin/env python
2 # Copyright (C) 2002 Cluster File Systems, Inc.
3 # Author: Robert Read <rread@clusterfs.com>
4
5 #   This file is part of Lustre, http://www.lustre.org.
6 #
7 #   Lustre is free software; you can redistribute it and/or
8 #   modify it under the terms of version 2 of the GNU General Public
9 #   License as published by the Free Software Foundation.
10 #
11 #   Lustre is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with Lustre; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #
20
21 """
22 lmc - lustre configuration data manager
23
24   See Lustre book (http://www.lustre.org/docs/lustre.pdf) for documentation on lmc.
25
26 """
27
28 import sys, os, getopt, string, exceptions, re
29 import xml.dom.minidom
30
31 def printDoc(doc, stream=sys.stdout):
32     try:
33         from xml.dom.ext import PrettyPrint
34         PrettyPrint(doc, stream)
35     except ImportError:
36         stream.write(doc.toxml())
37         stream.write("\n")
38     
39
40 PYMOD_DIR = "/usr/lib/lustre/python"
41
42 def development_mode():
43     base = os.path.dirname(sys.argv[0])
44     if os.access(base+"/Makefile.am", os.R_OK):
45         return 1
46     return 0
47
48 if not development_mode():
49     sys.path.append(PYMOD_DIR)
50
51 import Lustre
52
53 DEFAULT_PORT = 988 
54 DEFAULT_STRIPE_SZ = 1048576
55 DEFAULT_STRIPE_CNT = 1
56 DEFAULT_STRIPE_PATTERN = 0
57 UUID_MAX_LENGTH = 31
58
59 def reference():
60     print """usage: lmc --add object [object parameters]
61
62 Object creation command summary:
63
64 --add node
65   --node node_name
66   --timeout num
67   --upcall path
68   --lustre_upcall path
69   --portals_upcall path
70   --ptldebug debug_level
71   --subsystem subsystem_name
72
73 --add net
74   --node node_name
75   --nid nid
76   --cluster_id 
77   --nettype tcp|elan|gm
78   --hostaddr addr
79   --port port
80   --tcpbuf size
81   --irq_affinity 0|1
82   --router
83
84 --add mds
85   --node node_name
86   --mds mds_name
87   --failover
88   --dev path
89   --backdev path
90   --fstype ldiskfs|ext3
91   --backfstype ldiskfs|ext3|tmpfs
92   --size size
93   --nspath
94   --journal_size size
95   --inode_size size
96   --lmv lmv_name
97   --mkfsoptions options
98   --mountfsoptions options
99
100 --add lov
101   --lov lov_name
102   --mds mds_name
103   --stripe_sz num
104   --stripe_cnt num
105   --stripe_pattern num
106   --lmv lmv_name
107
108 --add ost
109   --node node_name
110   --ost ost_name 
111   --failover
112   --lov lov_name 
113   --dev path
114   --backdev path
115   --size size
116   --fstype ldiskfs|ext3
117   --backfstype ldiskfs|ext3|tmpfs
118   --journal_size size
119   --inode_size size
120   --osdtype obdecho|obdfilter
121   --ostuuid uuid
122   --mkfsoptions options
123   --mountfsoptions options
124   --nspath
125  
126 --add mtpt  - Mountpoint
127   --node node_name
128   --path /mnt/point
129   --mds mds_name
130   --lmv lmv_name
131   --ost ost_name OR --lov lov_name
132
133 --add route
134   --node nodename
135   --router
136   --gw nid
137   --gateway_cluster_id nid
138   --target_cluster_id nid
139   --lo nid
140   --hi nid
141
142 --add echo_client
143   --node nodename
144
145 --add mgmt  - Management/monitoring service
146   --node node_name
147   --mgmt mgmt_service_name
148
149 --add lmv
150   --lmv lmv_name
151
152 --add cobd
153   --node node_name
154   --real_obd obd_name
155   --cache_obd obd_name
156 """
157
158 PARAM = Lustre.Options.PARAM
159 lmc_options = [
160     # lmc input/output options
161     ('reference', "Print short reference for commands."), 
162     ('verbose,v', "Print system commands as they are run."),
163     ('merge,m', "Append to the specified config file.", PARAM),
164     ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
165     ('input,i', "", PARAM),
166     ('batch', "Used to execute lmc commands in batch mode.", PARAM),
167
168     # commands
169     ('add', "", PARAM),
170     
171     # node options
172     ('node', "Add a new node in the cluster configuration.", PARAM),
173     ('timeout', "Set timeout to initiate recovery.", PARAM),
174     ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
175     ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
176     ('portals_upcall', "Set location of portals upcall script.", PARAM),
177     ('ptldebug', "Set the portals debug level",  PARAM),
178     ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log",  PARAM),
179
180     # network 
181     ('nettype', "Specify the network type. This can be tcp/elan/gm.", PARAM),
182     ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
183     ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"),
184     ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
185     ('irq_affinity', "Optional argument.", PARAM, 0),
186     ('hostaddr', "", PARAM,""),
187     ('cluster_id', "Specify the cluster ID", PARAM, "0"),
188
189     # routes
190     ('route', "Add a new route for the cluster.", PARAM),
191     ('router', "Optional flag to mark a node as router."),
192     ('gw', "Specify the nid of the gateway for a route.", PARAM),
193     ('gateway_cluster_id', "", PARAM, "0"),
194     ('target_cluster_id', "", PARAM, "0"),
195     ('lo', "For a range route, this is the low value nid.", PARAM),
196     ('hi', "For a range route, this is a hi value nid.", PARAM,""),
197
198     # servers: mds and ost
199     ('mds', "Specify MDS name.", PARAM,""),
200     ('ost', "Specify the OST name.", PARAM,""),
201     ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
202     ('failover', "Enable failover support on OSTs or MDS?"),
203     ('group', "", PARAM),
204     ('dev', "Path of the device on local system.", PARAM,""),
205     ('backdev', "Path of the device for backing storage on local system.", PARAM,""),
206     ('size', "Specify the size of the device if needed.", PARAM,"0"),
207     ('journal_size', "Specify new journal size for underlying file system.", PARAM,"0"),
208     ('inode_size', "Specify new inode size for underlying file system.", PARAM,"0"),
209     ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
210     ('backfstype', "Optional argument to specify the backing filesystem type.", PARAM, "ext3"),
211     ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
212     ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""),
213     ('ostuuid', "", PARAM,""),
214     ('nspath', "Local mount point of server namespace.", PARAM,""),
215     ('format', ""),
216
217     # clients: mountpoint and echo
218     ('echo_client', "", PARAM),
219     ('path', "Specify the mountpoint for Lustre.", PARAM),
220     ('filesystem', "Lustre filesystem name", PARAM,""),
221
222     # lov
223     ('lov', "Specify LOV name.", PARAM,""),
224     ('stripe_sz', "Specify the stripe size in bytes.", PARAM),
225     ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0),
226     ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
227
228     # cobd
229     
230     ('real_obd', "Specify the real device for the cache obd system.", PARAM),
231     ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
232     ('cobd', "Specify COBD name", PARAM),
233
234     ('mgmt', "Specify management/monitoring service name.", PARAM, ""),
235
236     # lmv
237     ('lmv', "Specify LMV name.", PARAM,""),
238     ]
239
240 def error(*args):
241     msg = string.join(map(str,args))
242     raise OptionError("Error: " +  msg)
243
244 def panic(cmd, msg):
245     print "! " + cmd
246     print msg
247     sys.exit(1)
248
249     
250 def warning(*args):
251     msg = string.join(map(str,args))
252     print "Warning: ", msg
253     
254 #
255 # manage names and uuids
256 # need to initialize this by walking tree to ensure
257 # no duplicate names or uuids are created.
258 # this are just place holders for now.
259 # consider changing this to be like OBD-dev-host
260 def new_name(base):
261     ctr = 2
262     ret = base
263     while names.has_key(ret):
264         ret = "%s_%d" % (base, ctr)
265         ctr = 1 + ctr
266     names[ret] = 1
267     return ret
268
269 def new_uuid(name):
270     ctr = 2
271     ret = "%s_UUID" % (name)
272     if len(ret) > UUID_MAX_LENGTH:
273         ret = ret[-UUID_MAX_LENGTH:]
274     while uuids.has_key(ret):
275         ret = "%s_UUID_%d" % (name, ctr)
276         ctr = 1 + ctr
277         if len(ret) > UUID_MAX_LENGTH:
278             ret = ret[-UUID_MAX_LENGTH:]
279     uuids[ret] = 1
280     return ret
281
282
283 ldlm_name = 'ldlm'
284 ldlm_uuid = 'ldlm_UUID'
285
286 def new_lustre(dom):
287     """Create a new empty lustre document"""
288     # adding ldlm here is a bit of a hack, but one is enough.
289     str = """<lustre version="%s">
290     <ldlm name="%s" uuid="%s"/>
291     </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
292     return dom.parseString(str)
293
294 names = {}
295 uuids = {}
296
297 def init_names(doc):
298     """initialize auto-name generation tables"""
299     global names, uuids
300     # get all elements that contain a name attribute
301     for n in doc.childNodes:
302         if n.nodeType == n.ELEMENT_NODE:
303             if getName(n):
304                 names[getName(n)] = 1
305                 uuids[getUUID(n)] = 1
306             init_names(n)
307
308 def get_format_flag(options):
309     if options.format:
310         return 'yes'
311     return 'no'
312
313 ############################################################
314 # Build config objects using DOM
315 #
316 class GenConfig:
317     doc = None
318     dom = None
319     def __init__(self, doc):
320         self.doc = doc
321
322     def ref(self, type, uuid):
323         """ generate <[type]_ref uuidref="[uuid]"/> """
324         tag = "%s_ref" % (type)
325         ref = self.doc.createElement(tag)
326         ref.setAttribute("uuidref", uuid)
327         return ref
328     
329     def newService(self, tag, name, uuid):
330         """ create a new  service elmement, which requires name and uuid attributes """
331         new = self.doc.createElement(tag)
332         new.setAttribute("uuid", uuid);
333         new.setAttribute("name", name);
334         return new
335     
336     def addText(self, node, str):
337         txt = self.doc.createTextNode(str)
338         node.appendChild(txt)
339
340     def addElement(self, node, tag, str=None):
341         """ create a new element and add it as a child to node. If str is passed,
342             a text node is created for the new element"""
343         new = self.doc.createElement(tag)
344         if str:
345             self.addText(new, str)
346         node.appendChild(new)
347         return new
348
349     def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
350                 port=0, tcpbuf=0, irq_aff=0):
351         """create <network> node"""
352         network = self.newService("network", name, uuid)
353         network.setAttribute("nettype", net);
354         self.addElement(network, "nid", nid)
355         self.addElement(network, "clusterid", cluster_id)
356         if hostaddr:
357             self.addElement(network, "hostaddr", hostaddr)
358         if port:
359             self.addElement(network, "port", "%d" %(port))
360         if tcpbuf:
361             self.addElement(network, "sendmem", "%d" %(tcpbuf))
362             self.addElement(network, "recvmem", "%d" %(tcpbuf))
363         if irq_aff:
364             self.addElement(network, "irqaffinity", "%d" %(irq_aff))
365             
366         return network
367
368     def routetbl(self, name, uuid):
369         """create <routetbl> node"""
370         rtbl = self.newService("routetbl", name, uuid)
371         return rtbl
372         
373     def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
374         """ create one entry for the route table """
375         ref = self.doc.createElement('route')
376         ref.setAttribute("type", gw_net_type)
377         ref.setAttribute("gw", gw)
378         ref.setAttribute("gwclusterid", gw_cluster_id)
379         ref.setAttribute("tgtclusterid", tgt_cluster_id)
380         ref.setAttribute("lo", lo)
381         if hi:
382             ref.setAttribute("hi", hi)
383         return ref
384     
385     def profile(self, name, uuid):
386         """ create a host """
387         profile = self.newService("profile", name, uuid)
388         return profile
389
390     def node(self, name, uuid, prof_uuid):
391         """ create a host """
392         node = self.newService("node", name, uuid)
393         node.appendChild(self.ref("profile", prof_uuid))
394         return node
395
396     def ldlm(self, name, uuid):
397         """ create a ldlm """
398         ldlm = self.newService("ldlm", name, uuid)
399         return ldlm
400
401     def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
402             node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", 
403             mkfsoptions="", mountfsoptions="", backfstype="", backdevname=""):
404         osd = self.newService("osd", name, uuid)
405         osd.setAttribute('osdtype', osdtype)
406         osd.appendChild(self.ref("target", ost_uuid))
407         osd.appendChild(self.ref("node", node_uuid))
408         if fstype:
409             self.addElement(osd, "fstype", fstype)
410         if backfstype:
411             self.addElement(osd, "backfstype", backfstype)
412         if backdevname:
413             self.addElement(osd, "backdevpath", backdevname)
414         if devname:
415             dev = self.addElement(osd, "devpath", devname)
416             self.addElement(osd, "autoformat", format)
417             if dev_size:
418                 self.addElement(osd, "devsize", "%s" % (dev_size))
419             if journal_size:
420                 self.addElement(osd, "journalsize", "%s" % (journal_size))
421             if inode_size:
422                 self.addElement(osd, "inodesize", "%s" % (inode_size))
423             if mkfsoptions:
424                 self.addElement(osd, "mkfsoptions", mkfsoptions)
425             if mountfsoptions:
426                 self.addElement(osd, "mountfsoptions", mountfsoptions)
427         if nspath:
428             self.addElement(osd, "nspath", nspath)
429         return osd
430
431     def cobd(self, name, uuid, real_uuid, cache_uuid):
432         cobd = self.newService("cobd", name, uuid)
433         cobd.appendChild(self.ref("realobd",real_uuid))
434         cobd.appendChild(self.ref("cacheobd",cache_uuid))
435         return cobd
436
437     def ost(self, name, uuid, osd_uuid, group=""):
438         ost = self.newService("ost", name, uuid)
439         ost.appendChild(self.ref("active", osd_uuid))
440         if group:
441             self.addElement(ost, "group", group)
442         return ost
443
444     def oss(self, name, uuid):
445         oss = self.newService("oss", name, uuid)
446         return oss
447
448     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
449         lov = self.newService("lov", name, uuid)
450         lov.appendChild(self.ref("mds", mds_uuid))
451         lov.setAttribute("stripesize", str(stripe_sz))
452         lov.setAttribute("stripecount", str(stripe_cnt))
453         lov.setAttribute("stripepattern", str(pattern))
454         return lov
455
456     def lovconfig(self, name, uuid, lov_uuid):
457         lovconfig = self.newService("lovconfig", name, uuid)
458         lovconfig.appendChild(self.ref("lov", lov_uuid))
459         return lovconfig
460
461     def lmv(self, name, uuid):
462         lmv = self.newService("lmv", name, uuid)
463         return lmv
464
465     def mds(self, name, uuid, mdd_uuid, group="", lmv=""):
466         mds = self.newService("mds", name, uuid)
467         mds.appendChild(self.ref("active",mdd_uuid))
468         if group:
469             self.addElement(mds, "group", group)
470         return mds
471
472     def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
473                mds_uuid, dev_size=0, journal_size=0, inode_size=256,
474                nspath="", mkfsoptions="", mountfsoptions="", backfstype="", 
475                backdevname="", lmv_uuid=""):
476         mdd = self.newService("mdsdev", name, uuid)
477         self.addElement(mdd, "fstype", fstype)
478         if backfstype:
479                 self.addElement(mdd, "backfstype", backfstype)
480         dev = self.addElement(mdd, "devpath", devname)
481         if backdevname:
482             self.addElement(mdd, "backdevpath", backdevname)
483         self.addElement(mdd, "autoformat", format)
484         if dev_size:
485                 self.addElement(mdd, "devsize", "%s" % (dev_size))
486         if journal_size:
487             self.addElement(mdd, "journalsize", "%s" % (journal_size))
488         if inode_size:
489             self.addElement(mdd, "inodesize", "%s" % (inode_size))
490         if nspath:
491             self.addElement(mdd, "nspath", nspath)
492         if mkfsoptions:
493             self.addElement(mdd, "mkfsoptions", mkfsoptions)
494         if mountfsoptions:
495             self.addElement(mdd, "mountfsoptions", mountfsoptions)
496
497         mdd.appendChild(self.ref("node", node_uuid))
498         mdd.appendChild(self.ref("target", mds_uuid))
499         if lmv_uuid:
500             mdd.appendChild(self.ref("lmv", lmv_uuid))
501             self.addElement(mdd, "lmv", lmv_uuid)
502
503         return mdd
504
505     def mgmt(self, mgmt_name, mgmt_uuid, node_uuid):
506         mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid)
507         mgmt.appendChild(self.ref("node", node_uuid))
508         # Placeholder until mgmt-service failover.
509         mgmt.appendChild(self.ref("active", mgmt_uuid))
510         return mgmt
511
512     def mountpoint(self, name, uuid, fs_uuid, path):
513         mtpt = self.newService("mountpoint", name, uuid)
514         mtpt.appendChild(self.ref("filesystem", fs_uuid))
515         self.addElement(mtpt, "path", path)
516         return mtpt
517
518     def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid):
519         fs = self.newService("filesystem", name, uuid)
520         fs.appendChild(self.ref("mds", mds_uuid))
521         fs.appendChild(self.ref("obd", obd_uuid))
522         if mgmt_uuid:
523             fs.appendChild(self.ref("mgmt", mgmt_uuid))
524         return fs
525         
526     def echo_client(self, name, uuid, osc_uuid):
527         ec = self.newService("echoclient", name, uuid)
528         ec.appendChild(self.ref("obd", osc_uuid))
529         return ec
530
531 ############################################################
532 # Utilities to query a DOM tree
533 # Using this functions we can treat use config information
534 # directly as a database.
535 def getName(n):
536     return n.getAttribute('name')
537
538 def getUUID(node):
539     return node.getAttribute('uuid')
540
541
542 def findByName(lustre, name, tag = ""):
543     for n in lustre.childNodes:
544         if n.nodeType == n.ELEMENT_NODE:
545             if tag and n.nodeName != tag:
546                 continue
547             if getName(n) == name:
548                 return n
549             else:
550                 n = findByName(n, name)
551                 if n: return n
552     return None
553
554
555 def lookup(node, uuid):
556     for n in node.childNodes:
557         if n.nodeType == n.ELEMENT_NODE:
558             if getUUID(n) == uuid:
559                 return n
560             else:
561                 n = lookup(n, uuid)
562                 if n: return n
563     return None
564
565
566 def name2uuid(lustre, name, tag="",  fatal=1):
567     ret = findByName(lustre, name, tag)
568     if not ret:
569         if fatal:
570             error('name2uuid:', '"'+name+'"', tag, 'element not found.')
571         else:
572             return ""
573     return getUUID(ret)
574     
575 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
576     for n in lustre.childNodes:
577         if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
578             if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
579                 return getUUID(n)
580     return None
581
582 # XXX: assumes only one network element per node. will fix this
583 # as soon as support for routers is added
584 def get_net_uuid(lustre, node_name):
585     """ get a network uuid for a node_name """
586     node = findByName(lustre, node_name, "node")
587     if not node:
588         error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
589     net = node.getElementsByTagName('network')
590     if net:
591         return getUUID(net[0])
592     return None
593
594
595 def lov_add_obd(gen, lov, osc_uuid):
596     lov.appendChild(gen.ref("obd", osc_uuid))
597                             
598 def lmv_add_obd(gen, lmv, mdc_uuid):
599     lmv.appendChild(gen.ref("mds", mdc_uuid))
600                             
601 def ref_exists(profile, uuid):
602     elist = profile.childNodes
603     for e in elist:
604         if e.nodeType == e.ELEMENT_NODE:
605             ref = e.getAttribute('uuidref')
606             if ref == uuid:
607                 return 1
608     return 0
609         
610 # ensure that uuid is not already in the profile
611 # return true if uuid is added
612 def node_add_profile(gen, node, ref, uuid):
613     refname = "%s_ref" % "profile"
614     ret = node.getElementsByTagName(refname)
615     if not ret:
616         error('node has no profile ref:', node)
617     prof_uuid = ret[0].getAttribute('uuidref')
618     profile = lookup(node.parentNode, prof_uuid)
619     if not profile:
620         error("no profile found:", prof_uuid)
621     if ref_exists(profile, uuid):
622         return 0
623     profile.appendChild(gen.ref(ref, uuid))
624     return 1
625     
626 def get_attr(dom_node, attr, default=""):
627     v = dom_node.getAttribute(attr)
628     if v:
629         return v
630     return default
631
632 ############################################################
633 # Top level commands
634 #
635 def set_node_options(gen, node, options):
636     if options.router:
637         node.setAttribute('router', '1')
638     if options.timeout:
639         gen.addElement(node, "timeout", get_option(options, 'timeout'))
640     if options.upcall:
641         default_upcall =  get_option(options, 'upcall')
642     else:
643         default_upcall = ''
644     if default_upcall or options.lustre_upcall:
645         if options.lustre_upcall:
646             gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
647         else: 
648             gen.addElement(node, 'lustreUpcall', default_upcall)
649     if default_upcall or options.portals_upcall:
650         if options.portals_upcall:
651             gen.addElement(node, 'portalsUpcall', options.portals_upcall)
652         else:
653             gen.addElement(node, 'portalsUpcall', default_upcall)
654     if options.ptldebug:
655         gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
656     if options.subsystem:
657         gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
658     return node
659
660 def do_add_node(gen, lustre,  options, node_name):
661     uuid = new_uuid(node_name)
662     prof_name = new_name("PROFILE_" + node_name)
663     prof_uuid = new_uuid(prof_name)
664     profile = gen.profile(prof_name, prof_uuid)
665     node = gen.node(node_name, uuid, prof_uuid)
666     lustre.appendChild(node)
667     lustre.appendChild(profile)
668
669     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
670     set_node_options(gen, node, options)
671     return node
672
673     
674 def add_node(gen, lustre, options):
675     """ create a node with a network config """
676
677     node_name = get_option(options, 'node')
678     ret = findByName(lustre, node_name, "node")
679     if ret:
680         print "Node:", node_name, "exists."
681         return
682     do_add_node(gen, lustre, options, node_name)
683
684
685 def add_net(gen, lustre, options):
686     """ create a node with a network config """
687
688     node_name = get_option(options, 'node')
689     nid = get_option(options, 'nid')
690     cluster_id = get_option(options, 'cluster_id')
691     hostaddr = get_option(options, 'hostaddr')
692     net_type = get_option(options, 'nettype')
693
694     if net_type in ('tcp',):
695         port = get_option_int(options, 'port')
696         tcpbuf = get_option_int(options, 'tcpbuf')
697         irq_aff = get_option_int(options, 'irq_affinity')
698     elif net_type in ('elan', 'gm'):
699         port = 0
700         tcpbuf = 0
701         irq_aff = 0
702     else:
703         print "Unknown net_type: ", net_type
704         sys.exit(2)
705
706     ret = findByName(lustre, node_name, "node")
707     if not ret:
708         node = do_add_node(gen, lustre, options, node_name)
709     else:
710         node = ret
711         set_node_options(gen, node, options)
712
713     net_name = new_name('NET_'+ node_name +'_'+ net_type)
714     net_uuid = new_uuid(net_name)
715     node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
716                                  hostaddr, port, tcpbuf, irq_aff))
717     node_add_profile(gen, node, "network", net_uuid)
718
719
720 def add_route(gen, lustre, options):
721     """ create a node with a network config """
722
723     node_name = get_option(options, 'node')
724     gw_net_type = get_option(options, 'nettype')
725     gw = get_option(options, 'gw')
726     gw_cluster_id = get_option(options, 'gateway_cluster_id')
727     tgt_cluster_id = get_option(options, 'target_cluster_id')
728     lo = get_option(options, 'lo')
729     hi = get_option(options, 'hi')
730     if not hi:
731         hi = lo
732
733     node = findByName(lustre, node_name, "node")
734     if not node:
735         error (node_name, " not found.")
736     
737     rlist = node.getElementsByTagName('routetbl')
738     if len(rlist) > 0:
739         rtbl = rlist[0]
740     else:
741         rtbl_name = new_name("RTBL_" + node_name)
742         rtbl_uuid = new_uuid(rtbl_name)
743         rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
744         node.appendChild(rtbl)
745         node_add_profile(gen, node, "routetbl", rtbl_uuid)
746     rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
747                                lo, hi))
748
749
750 def add_mds(gen, lustre, options):
751     node_name = get_option(options, 'node')
752     mds_name = get_option(options, 'mds')
753     lmv_name = get_option(options, 'lmv')
754     mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
755     mdd_uuid = new_uuid(mdd_name)
756
757     lmv_uuid = ""
758     if lmv_name:
759         lmv = findByName(lustre, lmv_name, "lmv")
760         if not lmv:
761             error('add_mds:', '"' + lmv_name + '"', "lmv element not found.")
762         lmv_uuid = name2uuid(lustre, lmv_name, fatal=0)
763
764     mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
765     if not mds_uuid:
766         mds_uuid = new_uuid(mds_name)
767         mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
768         lustre.appendChild(mds)
769         if lmv_name:
770             lmv_add_obd(gen, lmv, mds_uuid)
771     else:
772         mds = lookup(lustre, mds_uuid)
773
774     if options.failover:
775         mds.setAttribute('failover', "1")
776
777     devname = get_option(options, 'dev')
778     backdevname = get_option(options, 'backdev')
779     size = get_option(options, 'size')
780     fstype = get_option(options, 'fstype')
781     backfstype = get_option(options, 'backfstype')
782     journal_size = get_option(options, 'journal_size')
783     inode_size = get_option(options, 'inode_size')
784     nspath = get_option(options, 'nspath')
785     mkfsoptions = get_option(options, 'mkfsoptions')
786     mountfsoptions = get_option(options, 'mountfsoptions')
787
788     node_uuid = name2uuid(lustre, node_name, 'node')
789
790     node = findByName(lustre, node_name, "node")
791     node_add_profile(gen, node, "mdsdev", mdd_uuid)
792     net_uuid = get_net_uuid(lustre, node_name)
793     if not net_uuid:
794         error("NODE: ", node_name, "not found")
795
796     if lmv_name:
797         mds.appendChild(gen.ref("lmv", lmv_uuid))
798
799     mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
800                      get_format_flag(options), node_uuid, mds_uuid,
801                      size, journal_size, inode_size, nspath, mkfsoptions, 
802                      mountfsoptions, backfstype, backdevname, lmv_uuid)
803     lustre.appendChild(mdd)
804                    
805
806 def add_mgmt(gen, lustre, options):
807     node_name = get_option(options, 'node')
808     node_uuid = name2uuid(lustre, node_name, 'node')
809     mgmt_name = get_option(options, 'mgmt')
810     if not mgmt_name:
811         mgmt_name = new_name('MGMT_' + node_name)
812     mgmt_uuid = name2uuid(lustre, mgmt_name, 'mgmt', fatal=0)
813     if not mgmt_uuid:
814         mgmt_uuid = new_uuid(mgmt_name)
815         mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid)
816         lustre.appendChild(mgmt)
817     else:
818         mgmt = lookup(lustre, mgmt_uuid)
819
820     node = findByName(lustre, node_name, "node")
821     node_add_profile(gen, node, 'mgmt', mgmt_uuid)
822
823 def add_ost(gen, lustre, options):
824     node_name = get_option(options, 'node')
825     lovname = get_option(options, 'lov')
826     osdtype = get_option(options, 'osdtype')
827
828     node_uuid = name2uuid(lustre, node_name, 'node')
829
830     if osdtype == 'obdecho':
831         fstype = ''
832         backfstype = ''
833         devname = ''
834         backdevname = ''
835         size = 0
836         journal_size = ''
837         inode_size = ''
838         mkfsoptions = ''
839         mountfsoptions = ''
840     else:
841         devname = get_option(options, 'dev') # can be unset for bluearcs
842         backdevname = get_option(options, 'backdev')
843         size = get_option(options, 'size')
844         fstype = get_option(options, 'fstype')
845         backfstype = get_option(options, 'backfstype')
846         journal_size = get_option(options, 'journal_size')
847         inode_size = get_option(options, 'inode_size')
848         mkfsoptions = get_option(options, 'mkfsoptions')
849         mountfsoptions = get_option(options, 'mountfsoptions')
850         
851     nspath = get_option(options, 'nspath')
852
853     ostname = get_option(options, 'ost')
854     if not ostname:
855         ostname = new_name('OST_'+ node_name)
856
857     osdname = new_name("OSD_" + ostname + "_" + node_name)
858     osd_uuid = new_uuid(osdname)
859
860     ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
861     if not ost_uuid:
862         ost_uuid = get_option(options, 'ostuuid')
863         if ost_uuid:
864             if lookup(lustre, ost_uuid):
865                 error("Duplicate OST UUID:", ost_uuid)
866         else:
867             ost_uuid = new_uuid(ostname)
868
869         ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
870         lustre.appendChild(ost)
871         if lovname:
872             lov = findByName(lustre, lovname, "lov")
873             if not lov:
874                 error('add_ost:', '"'+lovname+'"', "lov element not found.")
875             lov_add_obd(gen, lov, ost_uuid)
876     else:
877         ost = lookup(lustre, ost_uuid)
878
879     if options.failover:
880         ost.setAttribute('failover', "1")
881     
882
883     osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
884                   get_format_flag(options), ost_uuid, node_uuid, size,
885                   journal_size, inode_size, nspath, mkfsoptions, 
886                   mountfsoptions, backfstype, backdevname)
887
888     node = findByName(lustre, node_name, "node")
889
890 ##     if node_add_profile(gen, node, 'oss', oss_uuid):
891 ##         ossname = 'OSS'
892 ##         oss_uuid = new_uuid(ossname)
893 ##         oss = gen.oss(ossname, oss_uuid)
894 ##         lustre.appendChild(oss)
895
896     node_add_profile(gen, node, 'osd', osd_uuid)
897     lustre.appendChild(osd)
898
899                    
900 def add_cobd(gen, lustre, options):
901     node_name = get_option(options, 'node')
902     name = get_option(options, 'cobd')
903     uuid = new_uuid(name)
904
905     real_name = get_option(options, 'real_obd')
906     cache_name = get_option(options, 'cache_obd')
907     
908     real_uuid = name2uuid(lustre, real_name, tag='lov', fatal=0)
909     cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0)
910
911     if real_uuid:
912         node = lookup(lustre, real_uuid)
913         rets = node.getElementsByTagName('obd_ref')
914         for ret in rets:
915             ost_uuid = ret.getAttribute('uuidref')
916             ost_node = lookup(lustre, ost_uuid)
917             ret = ost_node.getElementsByTagName('active_ref')
918             if ret:
919                 osd_uuid = ret[0].getAttribute('uuidref')
920                 osd_node = lookup(lustre, osd_uuid)
921                 gen.addElement(osd_node, 'cachetype', 'master')
922
923     if cache_uuid:
924         node = lookup(lustre, cache_uuid)
925         rets = node.getElementsByTagName('obd_ref')
926         for ret in rets:
927             ost_uuid = ret.getAttribute('uuidref')
928             ost_node = lookup(lustre, ost_uuid)
929             ret = ost_node.getElementsByTagName('active_ref')
930             if ret:
931                 osd_uuid = ret[0].getAttribute('uuidref')
932                 osd_node = lookup(lustre, osd_uuid)
933                 gen.addElement(osd_node, 'cachetype', 'cache')
934
935     if not real_uuid or not cache_uuid:
936         real_uuid = name2uuid(lustre,real_name, tag='mds')
937         cache_uuid = name2uuid(lustre,cache_name, tag='mds')
938         if real_uuid:
939             mds_node = lookup(lustre, real_uuid)
940             ret = mds_node.getElementsByTagName('active_ref')
941             if ret:
942                 mdsdev_uuid = ret[0].getAttribute('uuidref')
943                 mdsdev_node = lookup(lustre, mdsdev_uuid)
944                 gen.addElement(mdsdev_node, 'cachetype', 'master')
945         if cache_uuid:
946             mds_node = lookup(lustre, cache_uuid)
947             ret = mds_node.getElementsByTagName('active_ref')
948             if ret:
949                 mdsdev_uuid = ret[0].getAttribute('uuidref')
950                 mdsdev_node = lookup(lustre, mdsdev_uuid)
951                 gen.addElement(mdsdev_node, 'cachetype', 'cache')
952
953     node = findByName(lustre, node_name, "node")
954     cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
955     lustre.appendChild(cobd)
956
957
958 def add_echo_client(gen, lustre, options):
959     """ add an echo client to the profile for this node. """
960     node_name = get_option(options, 'node')
961     lov_name = get_option(options, 'ost')
962
963     node = findByName(lustre, node_name, 'node')
964
965     echoname = new_name('ECHO_'+ node_name)
966     echo_uuid = new_uuid(echoname)
967     node_add_profile(gen, node, 'echoclient', echo_uuid)
968
969     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
970     if not lov_uuid:
971         lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
972
973     echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
974     lustre.appendChild(echo)
975
976
977 def add_lov(gen, lustre, options):
978     """ create a lov """
979
980     lmv_name = get_option(options, 'lmv')
981     lov_orig = get_option(options, 'lov')
982     name = new_name(lov_orig)
983     if name != lov_orig:
984         warning("name:", lov_orig, "already used. using:", name)
985
986     mds_name = get_option(options, 'mds')
987     if not mds_name:
988         if not lmv_name:
989             error("LOV: MDS or LMV must be specified.");
990
991     stripe_sz = get_option_int(options, 'stripe_sz')
992     stripe_cnt = get_option_int(options, 'stripe_cnt')
993     pattern = get_option_int(options, 'stripe_pattern')
994     uuid = new_uuid(name)
995
996     ret = findByName(lustre, name, "lov")
997     if ret:
998         error("LOV: ", name, " already exists.")
999
1000     if not mds_name:
1001         mds_uuid = name2uuid(lustre, lmv_name, 'lmv')
1002     else:
1003         mds_uuid = name2uuid(lustre, mds_name, 'mds')
1004
1005     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1006     lustre.appendChild(lov)
1007     
1008     # add an lovconfig entry to the active mdsdev profile
1009     lovconfig_name = new_name('LVCFG_' + name)
1010     lovconfig_uuid = new_uuid(lovconfig_name)
1011     if mds_name:
1012         mds = findByName(lustre, mds_name, "mds")
1013         mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1014     if lmv_name:
1015         lmv = findByName(lustre, lmv_name, "lmv")
1016         lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1017     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1018     lustre.appendChild(lovconfig)
1019
1020 def add_default_lov(gen, lustre, mds_name, lov_name):
1021     """ create a default lov """
1022                                                                                                                                                
1023     stripe_sz = DEFAULT_STRIPE_SZ
1024     stripe_cnt = DEFAULT_STRIPE_CNT
1025     pattern = DEFAULT_STRIPE_PATTERN
1026     uuid = new_uuid(lov_name)
1027                                                                                                                                                
1028     ret = findByName(lustre, lov_name, "lov")
1029     if ret:
1030         error("LOV: ", lov_name, " already exists.")
1031                                                                                                                                                
1032     mds_uuid = name2uuid(lustre, mds_name, 'mds')
1033     lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1034     lustre.appendChild(lov)
1035                                                                                                                                                
1036     # add an lovconfig entry to the active mdsdev profile
1037     lovconfig_name = new_name('LVCFG_' + lov_name)
1038     lovconfig_uuid = new_uuid(lovconfig_name)
1039     mds = findByName(lustre, mds_name)
1040     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1041     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1042     lustre.appendChild(lovconfig)
1043
1044 def add_lmv(gen, lustre, options):
1045     """ create a lmv """
1046
1047     lmv_orig = get_option(options, 'lmv')
1048     name = new_name(lmv_orig)
1049     if name != lmv_orig:
1050         warning("name:", lmv_orig, "already used. using:", name)
1051
1052     uuid = new_uuid(name)
1053     ret = findByName(lustre, name, "lmv")
1054     if ret:
1055         error("LMV: ", name, " already exists.")
1056
1057     lmv = gen.lmv(name, uuid)
1058     lustre.appendChild(lmv)
1059
1060 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
1061     fs_name = new_name("FS_fsname")
1062     fs_uuid = new_uuid(fs_name)
1063     cobd = lookup(lustre, mds_uuid)
1064     #SHOULD appendChild filesystem to real mds not cobd
1065     ret = cobd.getElementsByTagName("cacheobd_ref")
1066     if ret:
1067         cacheobd_uuid = ret[0].getAttribute('uuidref') 
1068         cacheobd = lookup(lustre, cacheobd_uuid)
1069         cacheobd.appendChild(gen.ref("filesystem", fs_uuid))
1070         ret = cobd.getElementsByTagName("realobd_ref")
1071         if ret:
1072             realobd_uuid = ret[0].getAttribute('uuidref')
1073             realobd = lookup(lustre, realobd_uuid)
1074             realobd.appendChild(gen.ref("filesystem", fs_uuid))
1075     else:
1076         cobd.appendChild(gen.ref("filesystem", fs_uuid))
1077     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid)
1078     lustre.appendChild(fs)
1079     return fs_uuid
1080
1081 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
1082     mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0)
1083     if not mds_uuid:
1084         mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0)
1085     if not mds_uuid:
1086         mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=1) 
1087     obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
1088     if obd_uuid == '':
1089         obd_uuid = name2uuid(lustre, obd_name, tag='cobd')
1090     if mgmt_name:
1091         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
1092     else:
1093         mgmt_uuid = ''
1094     fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
1095     if not fs_uuid:
1096         fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid)
1097     return fs_uuid
1098     
1099 def add_mtpt(gen, lustre, options):
1100     """ create mtpt on a node """
1101     node_name = get_option(options, 'node')
1102
1103     path = get_option(options, 'path')
1104     fs_name = get_option(options, 'filesystem')
1105
1106     lov_name = get_option(options, 'lov')
1107     ost_name = get_option(options, 'ost')
1108     mds_name = get_option(options, 'mds')
1109     if mds_name == '':
1110         mds_name = get_option(options, 'lmv')
1111         if mds_name == '':
1112             error("--add mtpt requires either --mds or --lmv.")
1113     if lov_name == '':
1114         if ost_name == '':
1115             error("--add mtpt requires --lov lov_name or --ost ost_name")
1116         else:
1117             warning("use default value for lov, due no --lov lov_name provided")
1118             lov_name = new_name("lov_default")
1119             add_default_lov(gen, lustre, mds_name, lov_name)
1120             ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1121             if not ost_uuid:
1122                 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1123             lov = findByName(lustre, lov_name, "lov")
1124             lov_add_obd(gen, lov, ost_uuid)
1125
1126     if fs_name == '':
1127         mgmt_name = get_option(options, 'mgmt')
1128         fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name)
1129     else:
1130         fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1131
1132     name = new_name('MNT_'+ node_name)
1133
1134     ret = findByName(lustre, name, "mountpoint")
1135     if ret:
1136         # this can't happen, because new_name creates unique names
1137         error("MOUNTPOINT: ", name, " already exists.")
1138
1139     uuid = new_uuid(name)
1140     mtpt = gen.mountpoint(name, uuid, fs_uuid, path)
1141     node = findByName(lustre, node_name, "node")
1142     if not node:
1143         error('node:',  node_name, "not found.")
1144     node_add_profile(gen, node, "mountpoint", uuid)
1145     lustre.appendChild(mtpt)
1146
1147 ############################################################
1148 # Command line processing
1149 #
1150 class OptionError (exceptions.Exception):
1151     def __init__(self, args):
1152         self.args = args
1153
1154 def get_option(options, tag):
1155     """Look for tag in options hash and return the value if set. If not
1156     set, then if return default it is set, otherwise exception."""
1157     if options.__getattr__(tag) != None:
1158         return options.__getattr__(tag)
1159     else:
1160         raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1161
1162 def get_option_int(options, tag):
1163     """Return an integer option.  Raise exception if the value is not an int"""
1164     val = get_option(options, tag)
1165     try:
1166         n = int(val)
1167     except ValueError:
1168         raise OptionError("--%s <num> (value must be integer)" % (tag))        
1169     return n
1170
1171 # simple class for profiling
1172 import time
1173 class chrono:
1174     def __init__(self):
1175         self._start = 0
1176     def start(self):
1177         self._stop = 0
1178         self._start = time.time()
1179     def stop(self, msg=''):
1180         self._stop = time.time()
1181         if msg:
1182             self.display(msg)
1183     def dur(self):
1184         return self._stop - self._start
1185     def display(self, msg):
1186         d = self.dur()
1187         str = '%s: %g secs' % (msg, d)
1188         print str
1189
1190 #################################################################
1191 # function cmdlinesplit used to split cmd line from batch file
1192 #
1193 def cmdlinesplit(cmdline):
1194                                                                                                                                                
1195     double_quote  = re.compile(r'"(([^"\\]|\\.)*)"')
1196     single_quote  = re.compile(r"'(.*?)'")
1197     escaped = re.compile(r'\\(.)')
1198     esc_quote = re.compile(r'\\([\\"])')
1199     outside = re.compile(r"""([^\s\\'"]+)""")
1200                                                                                                                                                
1201     arg_list = []
1202     i = 0; arg = None
1203     while i < len(cmdline):
1204         c = cmdline[i]
1205         if c == '"':
1206             match = double_quote.match(cmdline, i)
1207             if not match:
1208                 print "Unmatched double quote:", cmdline
1209                 sys.exit(1)
1210             i = match.end()
1211             if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1212             else:           arg = arg + esc_quote.sub(r'\1', match.group(1))
1213                                                                                                                                                
1214         elif c == "'":
1215             match = single_quote.match(cmdline, i)
1216             if not match:
1217                 print "Unmatched single quote:", cmdline
1218                 sys.exit(1)
1219             i = match.end()
1220             if arg is None: arg = match.group(1)
1221             else:           arg = arg + match.group(1)
1222                                                                                                                                                
1223         elif c == "\\":
1224             match = escaped.match(cmdline, i)
1225             if not match:
1226                 print "Unmatched backslash", cmdline
1227                 sys.exit(1)
1228             i = match.end()
1229             if arg is None: arg = match.group(1)
1230             else:           arg = arg + match.group(1)
1231                                                                                                                                                
1232         elif c in string.whitespace:
1233             if arg != None:
1234                 arg_list.append(str(arg))
1235             arg = None
1236             while i < len(cmdline) and cmdline[i] in string.whitespace:
1237                 i = i + 1
1238         else:
1239             match = outside.match(cmdline, i)
1240             assert match
1241             i = match.end()
1242             if arg is None: arg = match.group()
1243             else:           arg = arg + match.group()
1244                                                                                                                                                
1245     if arg != None: arg_list.append(str(arg))
1246                                                                                                                                                
1247     return arg_list
1248
1249 ############################################################
1250 # Main
1251 #
1252
1253 def add(devtype, gen, lustre, options):
1254     if devtype == 'net':
1255         add_net(gen, lustre, options)
1256     elif devtype == 'mtpt':
1257         add_mtpt(gen, lustre, options)
1258     elif devtype == 'mds':
1259         add_mds(gen, lustre, options)
1260     elif devtype == 'ost':
1261         add_ost(gen, lustre, options)
1262     elif devtype == 'lov':
1263         add_lov(gen, lustre, options)
1264     elif devtype == 'route':
1265         add_route(gen, lustre, options)
1266     elif devtype == 'node':
1267         add_node(gen, lustre, options)
1268     elif devtype == 'echo_client':
1269         add_echo_client(gen, lustre, options)
1270     elif devtype == 'cobd':
1271         add_cobd(gen, lustre, options)
1272     elif devtype == 'mgmt':
1273         add_mgmt(gen, lustre, options)
1274     elif devtype == 'lmv':
1275         add_lmv(gen, lustre, options)
1276     else:
1277         error("unknown device type:", devtype)
1278     
1279 def do_command(gen, lustre, options, args):
1280     if options.add:
1281         add(options.add, gen, lustre, options)
1282     else:
1283         error("Missing command")
1284
1285 def main():
1286     cl = Lustre.Options("lmc", "", lmc_options)
1287     try:
1288         options, args = cl.parse(sys.argv[1:])
1289     except Lustre.OptionError, e:
1290         panic("lmc", e)
1291
1292     if len(args) > 0:
1293         panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1294
1295     if options.reference:
1296         reference()
1297         sys.exit(0)
1298
1299     outFile = '-'
1300
1301     if options.merge:
1302         outFile = options.merge
1303         if os.access(outFile, os.R_OK):
1304             doc = xml.dom.minidom.parse(outFile)
1305         else:
1306             doc = new_lustre(xml.dom.minidom)
1307     elif options.input:
1308         doc = xml.dom.minidom.parse(options.input)
1309     else:
1310         doc = new_lustre(xml.dom.minidom)
1311
1312     if options.output:
1313         outFile = options.output
1314
1315     lustre = doc.documentElement
1316     init_names(lustre)
1317     if lustre.tagName != "lustre":
1318         print "Existing config not valid."
1319         sys.exit(1)
1320
1321     gen = GenConfig(doc)
1322
1323     if options.batch:
1324         fp = open(options.batch)
1325         batchCommands = fp.readlines()
1326         fp.close()
1327         for cmd in batchCommands:
1328             try:
1329                 options, args = cl.parse(cmdlinesplit(cmd))
1330                 if options.merge or options.input or options.output:
1331                         print "The batchfile should not contain --merge, --input or --output."
1332                         sys.exit(1)
1333                 do_command(gen, lustre, options, args)
1334             except OptionError, e:
1335                 panic(cmd, e)
1336             except Lustre.OptionError, e:
1337                 panic(cmd, e)
1338     else:
1339         try:
1340             do_command(gen, lustre, options, args)
1341         except OptionError, e:
1342             panic(string.join(sys.argv),e)
1343         except Lustre.OptionError, e:
1344             panic("lmc", e)
1345
1346     if outFile == '-':
1347         printDoc(doc)
1348     else:
1349         printDoc(doc, open(outFile,"w"))
1350
1351 if __name__ == "__main__":
1352     main()