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