Whamcloud - gitweb
* support for toenal
[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 configurtion data  manager
23  
24  Basic plan for lmc usage:
25 # create nodes
26 ./lmc --output config.xml --node server --net server1 tcp 
27 ./lmc --merge config.xml  --node client --net client1 tcp
28 ./lmc --merge config.xml  --node client --route gw lo [hi]
29 ./lmc --merge config.xml --router --node gw1 --net gw1 tcp
30 ./lmc --merge config.xml --node gw1 --net 1 elan
31
32 ./lmc --merge config.xml --route elan 1 1 100
33 ./lmc --merge config.xml --route tcp gw1 ba1
34
35
36
37 # configure server
38 ./lmc --merge config.xml  --node server --mds mds1 /tmp/mds1 50000
39
40 # create lov
41 ./lmc --merge config.xml  --lov lov1 mds1 65536 0 0
42 ./lmc --merge config.xml  --node server --lov lov1 --ost /tmp/ost1 100000
43 ./lmc --merge config.xml  --node server --lov lov1 --ost /tmp/ost2 100000
44
45 # create client config
46 ./lmc --merge config.xml  --node client --mtpt /mnt/lustre mds1 lov1
47
48 """
49
50 import sys, os, getopt, string
51 import xml.dom.minidom
52 from xml.dom.ext import PrettyPrint
53
54
55 DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use?
56
57 def usage():
58     print """usage: lmc [--node --ost | --mtpt | --lov] args
59 Commands:
60 --node node_name 
61    Node_name by itself it will create a new node. If the --router
62    option is used when creating a new node, then that node will also
63    be configured as a router. When used with other commands it
64    specifies the node to modify.
65
66 --net hostname nettype [port, recv_buf, send_buf]
67    Nettype is either tcp, toe, elan, or gm.
68    Requires --node
69
70 --route net gw lo [hi]
71    This command is used to create  routes.  NET is the
72    network type this route will be used on.  The GW is an address of
73    one of the local interfaces. LO and HI represent a range of
74    addresses that can be reached through the gateway. If HI is not
75    set, then a route to the specific host in LO is created.
76
77 --mds device [size]
78    Create a MDS using the device
79    Requires --node 
80
81 --lov lov_name [mds_name stripe_sz sub_stripe_count pattern]
82    Creates a logical volume
83    When used with other commands, it specifics the lov to modify
84
85 --ost device [size]
86    Creates an OBD/OST/OSC configuration triplet for a new device.
87    When used on "host", the device will be initialized and the OST
88    will be enabled. On client nodes, the OSC will be avaiable.
89    Requires --node
90    Optional --obduuid Specifies the UUID used for the obd. 
91    If --lov lov_name is used, this device is added to lov. 
92
93 --mtpt /mnt/point mds_name lov_name|osc_name 
94    Creates a client mount point.
95    Requires --node
96
97 Options:
98 --merge="xml file"  Add the new objects to an existing file
99 --format            Format the partitions if unformated
100                     NB: The autoformat option has been disabled until a safe
101                     method is implemented to determine if a block device has a
102                     filesystem.
103 --reformat          Reformat partitions (this should be an lconf arg,
104                     I think)
105 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
106                     This is only useful for the --ost command.
107                     The device parameters are ignored for the obdecho type.
108 """
109     sys.exit(1)
110
111 def error(*args):
112     msg = string.join(map(str,args))
113     print "Error: ", msg
114     sys.exit(1)
115     
116 def warning(*args):
117     msg = string.join(map(str,args))
118     print "Warning: ", msg
119     
120 #
121 # manage names and uuids
122 # need to initialize this by walking tree to ensure
123 # no duplicate names or uuids are created.
124 # this are just place holders for now.
125 # consider changing this to be like OBD-dev-host
126 def new_name(base):
127     ctr = 2
128     ret = base
129     while names.has_key(ret):
130         ret = "%s_%d" % (base, ctr)
131         ctr = 1 + ctr
132     names[ret] = 1
133     return ret
134
135 def new_uuid(name):
136     return "%s_UUID" % (name)
137
138 ldlm_name = 'ldlm'
139 ldlm_uuid = 'ldlm_UUID'
140 def new_lustre(dom):
141     """Create a new empty lustre document"""
142     # adding ldlm here is a bit of a hack, but one is enough.
143     str = """<lustre>
144     <ldlm name="%s" uuid="%s"/>
145     </lustre>""" % (ldlm_name, ldlm_uuid)
146     return dom.parseString(str)
147
148 names = {}
149 uuids = {}
150
151 def init_names(doc):
152     """initialize auto-name generation tables"""
153     global names, uuids
154     # get all elements that contain a name attribute
155     for n in doc.childNodes:
156         if n.nodeType == n.ELEMENT_NODE:
157             if getName(n):
158                 names[getName(n)] = 1
159                 uuids[getUUID(n)] = 1
160             init_names(n)
161
162 def get_format_flag(options):
163     if options.has_key('format'):
164         if options['format']:
165             return 'yes'
166     return 'no'
167
168 ############################################################
169 # Build config objects using DOM
170 #
171 class GenConfig:
172     doc = None
173     dom = None
174     def __init__(self, doc):
175         self.doc = doc
176
177     def ref(self, type, uuid):
178         """ generate <[type]_ref uuidref="[uuid]"/> """
179         tag = "%s_ref" % (type)
180         ref = self.doc.createElement(tag)
181         ref.setAttribute("uuidref", uuid)
182         return ref
183     
184     def newService(self, tag, name, uuid):
185         """ create a new  service elmement, which requires name and uuid attributes """
186         new = self.doc.createElement(tag)
187         new.setAttribute("name", name);
188         new.setAttribute("uuid", uuid);
189         return new
190     
191     def addText(self, node, str):
192         txt = self.doc.createTextNode(str)
193         node.appendChild(txt)
194
195     def addElement(self, node, tag, str=None):
196         """ create a new element and add it as a child to node. If str is passed,
197             a text node is created for the new element"""
198         new = self.doc.createElement(tag)
199         if str:
200             self.addText(new, str)
201         node.appendChild(new)
202         return new
203
204     def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
205         """create <network> node"""
206         network = self.newService("network", name, uuid)
207         network.setAttribute("type", net);
208         self.addElement(network, "server", hostname)
209         if port:
210             self.addElement(network, "port", "%d" %(port))
211         if tcpbuf:
212             self.addElement(network, "send_mem", "%d" %(tcpbuf))
213             self.addElement(network, "recv_mem", "%d" %(tcpbuf))
214             
215         return network
216
217     def route(self, net_type, gw, lo, hi):
218         """ create one entry for the route table """
219         ref = self.doc.createElement('route')
220         ref.setAttribute("type", net_type)
221         ref.setAttribute("gw", gw)
222         ref.setAttribute("lo", lo)
223         if hi:
224             ref.setAttribute("hi", hi)
225         return ref
226     
227     def node(self, name, uuid):
228         """ create a host """
229         node = self.newService("node", name, uuid)
230         self.addElement(node, 'profile')
231         return node
232
233     def ldlm(self, name, uuid):
234         """ create a ldlm """
235         ldlm = self.newService("ldlm", name, uuid)
236         return ldlm
237
238     def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
239         obd = self.newService("obd", name, uuid)
240         obd.setAttribute('type', obdtype)
241         if fs:
242             self.addElement(obd, "fstype", fs)
243         if devname:
244             dev = self.addElement(obd, "device", devname)
245             if (dev_size):
246                 dev.setAttribute("size", "%s" % (dev_size))
247             self.addElement(obd, "autoformat", format)
248         return obd
249
250     def osc(self, name, uuid, obd_uuid, net_uuid):
251         osc = self.newService("osc", name, uuid)
252         osc.appendChild(self.ref("ost", net_uuid))
253         osc.appendChild(self.ref("obd", obd_uuid))
254         return osc
255
256     def ost(self, name, uuid, obd_uuid, net_uuid):
257         ost = self.newService("ost", name, uuid)
258         ost.appendChild(self.ref("network", net_uuid))
259         ost.appendChild(self.ref("obd", obd_uuid))
260         return ost
261
262     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_count, pattern):
263         lov = self.newService("lov", name, uuid)
264         lov.appendChild(self.ref("mds", mds_uuid))
265         devs = self.addElement(lov, "devices" )
266         devs.setAttribute("stripesize", stripe_sz)
267         devs.setAttribute("stripecount", stripe_count)
268         devs.setAttribute("pattern", pattern)
269         return lov
270
271     def lovconfig(self, name, uuid, lov_uuid):
272         lovconfig = self.newService("lovconfig", name, uuid)
273         lovconfig.appendChild(self.ref("lov", lov_uuid))
274         return lovconfig
275
276     def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
277             failover_uuid = "", dev_size=0 ):
278         mds = self.newService("mds", name, uuid)
279         self.addElement(mds, "fstype", fs)
280         dev = self.addElement(mds, "device", devname)
281         if dev_size:
282             dev.setAttribute("size", "%s" % (dev_size))
283         self.addElement(mds, "autoformat", format)
284         mds.appendChild(self.ref("network", net_uuid))
285         mds.appendChild(self.ref("node", node_uuid))
286         if failover_uuid:
287             mds.appendChild(self.ref("failover", failover_uuid))
288         return mds
289
290     def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
291         mtpt = self.newService("mountpoint", name, uuid)
292         mtpt.appendChild(self.ref("mds", mds_uuid))
293         mtpt.appendChild(self.ref("osc", osc_uuid))
294         self.addElement(mtpt, "path", path)
295         return mtpt
296
297 ############################################################
298 # Utilities to query a DOM tree
299 # Using this functions we can treat use config information
300 # directly as a database.
301 def getName(n):
302     return n.getAttribute('name')
303
304 def getUUID(node):
305     return node.getAttribute('uuid')
306
307
308 def findByName(lustre, name, tag = ""):
309     for n in lustre.childNodes:
310         if n.nodeType == n.ELEMENT_NODE:
311             if tag and n.nodeName != tag:
312                 continue
313             if getName(n) == name:
314                 return n
315             else:
316                 n = findByName(n, name)
317                 if n: return n
318     return None
319
320
321 def lookup(node, uuid):
322     for n in node.childNodes:
323         if n.nodeType == n.ELEMENT_NODE:
324             if getUUID(n) == uuid:
325                 return n
326             else:
327                 n = lookup(n, uuid)
328                 if n: return n
329     return None
330             
331
332 def mds2node(lustre, mds_name):
333     """ Find the node a MDS is configured on """
334     mds = findByName(lustre, mds_name, 'mds')
335     ref = mds.getElementsByTagName('node_ref')
336     if not ref:
337         error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
338     node_uuid = ref[0].getAttribute('uuidref')
339     node = lookup(lustre, node_uuid)
340     if not node:
341         error('mds2node:', "no node found for :", '"'+mds_name+'"')
342     return node
343
344
345 def name2uuid(lustre, name, tag="",  fatal=1):
346     ret = findByName(lustre, name, tag)
347     if not ret:
348         if fatal:
349             error('name2uuid:', '"'+name+'"', tag, 'element not found.')
350         else:
351             return ""
352     return getUUID(ret)
353     
354
355 # XXX: assumes only one network element per node. will fix this
356 # as soon as support for routers is added
357 def get_net_uuid(lustre, node_name):
358     """ get a network uuid for a node_name """
359     node = findByName(lustre, node_name, "node")
360     if not node:
361         error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
362     net = node.getElementsByTagName('network')
363     if net:
364         return getUUID(net[0])
365     return None
366
367
368 def lov_add_osc(gen, lov, osc_uuid):
369     devs = lov.getElementsByTagName('devices')
370     if len(devs) == 1:
371         devs[0].appendChild(gen.ref("osc", osc_uuid))
372     else:
373         error("No devices element found for LOV:", lov)
374
375                             
376 def node_add_profile(gen, node, ref, uuid):
377     ret = node.getElementsByTagName('profile')
378     if not ret:
379         error('node has no profile:', node)
380     ret[0].appendChild(gen.ref(ref, uuid))
381     
382 def get_attr(dom_node, attr, default=""):
383     v = dom_node.getAttribute(attr)
384     if v:
385         return v
386     return default
387
388 ############################################################
389 # Top level commands
390 #
391 def do_add_node(gen, lustre,  options, node_name):
392     uuid = new_uuid(node_name)
393     node = gen.node(node_name, uuid)
394     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
395     if options.has_key('router'):
396         node.setAttribute('router', '1')
397     lustre.appendChild(node)
398     return node
399
400     
401 def add_node(gen, lustre, options, args):
402     """ create a node with a network config """
403     if len(args) > 1:
404         usage()
405
406     node_name = options['node']
407
408     ret = findByName(lustre, node_name, "node")
409     if ret:
410         print "Node:", node_name, "exists."
411         return
412     do_add_node(gen, lustre, options, node_name)
413
414
415 def add_net(gen, lustre, options, args):
416     """ create a node with a network config """
417     if len(args) < 2:
418         usage()
419
420     node_name = options['node']
421     nid = args[0]
422     net_type = args[1]
423     port = 0
424     tcpbuf = 0
425
426     if net_type in ('tcp', 'toe'):
427         if len(args) > 2:
428             port = int(args[2])
429         else:
430             port = DEFAULT_PORT
431         if options.has_key('tcpbuf'):
432             tcpbuf = int(options['tcpbuf'])
433     elif net_type in ('elan', 'gm'):
434         port = 0
435     else:
436         print "Unknown net_type: ", net_type
437         sys.exit(2)
438
439     ret = findByName(lustre, node_name, "node")
440     if not ret:
441         node = do_add_node(gen, lustre, options, node_name)
442     else:
443         node = ret
444     net_name = new_name('NET_'+ node_name +'_'+ net_type)
445     net_uuid = new_uuid(net_name)
446     node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
447     node_add_profile(gen, node, "network", net_uuid)
448
449
450 def add_route(gen, lustre, options, args):
451     """ create a node with a network config """
452     if len(args) < 3:
453         usage()
454
455     node_name = options['node']
456     net_type= args[0]
457     gw = args[1]
458     lo = args[2]
459     hi = ''
460
461     if len(args) > 3:
462         hi = args[3]
463
464     node = findByName(lustre, node_name, "node")
465     if not node:
466         error (node_name, " not found.")
467     
468     netlist = node.getElementsByTagName('network')
469     net = netlist[0]
470     rlist = net.getElementsByTagName('route_tbl')
471     if len(rlist) > 0:
472         rtbl = rlist[0]
473     else:
474         rtbl = gen.addElement(net, 'route_tbl')
475     rtbl.appendChild(gen.route(net_type, gw, lo, hi))
476
477
478 def add_mds(gen, lustre, options, args):
479     if len(args) < 1:
480         usage()
481
482     if options.has_key('node'):
483         node_name = options['node']
484     else:
485         error("--mds requires a --node argument")
486
487     mds_name = new_name(options['mds'])
488     if mds_name != options['mds']:
489         warning("name:", options['mds'], "already used. using:", mds_name)
490     devname = args[0]
491     if len(args) > 1:
492         size = args[1]
493     else:
494         size = 0
495
496     mds_uuid = new_uuid(mds_name)
497
498     node_uuid = name2uuid(lustre, node_name, 'node')
499
500     node = findByName(lustre, node_name, "node")
501     node_add_profile(gen, node, "mds", mds_uuid)
502     net_uuid = get_net_uuid(lustre, node_name)
503     if not net_uuid:
504         error("NODE: ", node_name, "not found")
505
506
507     mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
508                   net_uuid, node_uuid, dev_size=size)
509     lustre.appendChild(mds)
510                    
511
512 def add_ost(gen, lustre, options, args):
513     lovname = ''
514     obdtype = 'obdfilter'
515     devname = ''
516     size = 0
517     fstype = 'extN'
518     
519     if options.has_key('node'):
520         node_name = options['node']
521     else:
522         error("--ost requires a --node argument")
523
524     if options.has_key('lov'):
525         lovname = options['lov']
526
527     if options.has_key('obdtype'):
528         obdtype = options['obdtype']
529     if obdtype == 'obdecho':
530         fstype = ''
531     else:
532         if len(args) < 1:
533             usage()
534         devname = args[0]
535         if len(args) > 1:
536             size = args[1]
537         
538     obdname = new_name('OBD_'+ node_name)
539     oscname = new_name('OSC_'+ node_name)
540     ostname = new_name('OST_'+ node_name)
541     if options.has_key('obduuid'):
542         obd_uuid = options['obduuid']
543         obd = lookup(lustre, obd_uuid)
544         if obd:
545             error("Duplicate OBD UUID:", obd_uuid)
546     else:
547         obd_uuid = new_uuid(obdname)
548     ost_uuid = new_uuid(ostname)
549     osc_uuid = new_uuid(oscname)
550
551     net_uuid = get_net_uuid(lustre, node_name)
552     if not net_uuid:
553         error("NODE: ", node_name, "not found")
554     
555     obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
556     ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
557     osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
558     
559     if lovname:
560         lov = findByName(lustre, lovname, "lov")
561         if not lov:
562             error('add_ost:', '"'+lovname+'"', "lov element not found.")
563         lov_add_osc(gen, lov, osc_uuid)
564
565     node = findByName(lustre, node_name, "node")
566     node_add_profile(gen, node, 'obd', obd_uuid)
567     node_add_profile(gen, node, 'ost', ost_uuid)
568
569     lustre.appendChild(obd)
570     lustre.appendChild(osc)
571     lustre.appendChild(ost)
572
573                    
574 # this is generally only used by llecho.sh
575 def add_osc(gen, lustre, options, args):
576     """ add the osc to the profile for this node. """
577     if len(args) < 1:
578         usage()
579     osc_name = args[0]
580     if options.has_key('node'):
581         node_name = options['node']
582     else:
583         error("--osc requires a --node argument")
584     osc_uuid = name2uuid(lustre, osc_name) # either 'osc' or 'lov'
585     node = findByName(lustre, node_name, "node")
586     node_add_profile(gen, node, 'osc', osc_uuid)
587
588
589 def add_lov(gen, lustre, options, args):
590     """ create a lov """
591     if len(args) < 4:
592         usage()
593
594     name = new_name(options['lov'])
595     if name != options['lov']:
596         warning("name:", options['lov'], "already used. using:", name)
597
598     mds_name = args[0]
599     stripe_sz = args[1]
600     stripe_count = args[2]
601     pattern = args[3]
602     uuid = new_uuid(name)
603
604     ret = findByName(lustre, name, "lov")
605     if ret:
606         error("LOV: ", name, " already exists.")
607
608     mds_uuid = name2uuid(lustre, mds_name, 'mds')
609     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
610     lustre.appendChild(lov)
611     
612     # add an lovconfig entry to the mds profile
613     lovconfig_name = new_name('LVCFG_' + name)
614     lovconfig_uuid = new_uuid(lovconfig_name)
615     node = mds2node(lustre, mds_name)
616     node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
617     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
618     lustre.appendChild(lovconfig)
619
620
621
622 def add_mtpt(gen, lustre, options, args):
623     """ create mtpt on a node """
624     if len(args) < 3:
625         usage()
626
627     if options.has_key('node'):
628         node_name = options['node']
629     else:
630         error("--mtpt requires a --node argument")
631
632     path = args[0]
633     mds_name = args[1]
634     lov_name = args[2]
635
636     name = new_name('MNT_'+ node_name)
637
638     ret = findByName(lustre, name, "mountpoint")
639     if ret:
640         error("MOUNTPOINT: ", name, " already exists.")
641
642     mds_uuid = name2uuid(lustre, mds_name, tag='mds')
643     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
644     if not lov_uuid:
645         lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
646
647     uuid = new_uuid(name)
648     mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
649     node = findByName(lustre, node_name, "node")
650     if not node:
651             error('node:',  node_name, "not found.")
652     node_add_profile(gen, node, "mountpoint", uuid)
653     lustre.appendChild(mtpt)
654
655
656 ############################################################
657 # Command line processing
658 #
659 def parse_cmdline(argv):
660     short_opts = "ho:i:m:"
661     long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
662                  "route", "router", "merge=", "format", "reformat", "output=",
663                  "obdtype=", "obduuid=", "in=", "help", "batch="]
664     opts = []
665     args = []
666     options = {}
667     try:
668         opts, args = getopt.getopt(argv, short_opts, long_opts)
669     except getopt.error:
670         print "invalid opt"
671         usage()
672
673     for o, a in opts:
674         # Commands to create new devices
675         if o == "--ost":
676             options['ost'] = 1
677         if o == "--osc":
678             options['osc'] = 1
679         if o == "--mds":
680             options['mds'] = a
681         if o == "--net":
682             options['net'] = 1
683         if o == "--mtpt":
684             options['mtpt'] = 1
685         if o == "--node":
686             options['node'] = a
687         if o == "--route":
688             options['route'] = 1
689         if o == "--router":
690             options['router'] = 1
691         if o == "--lov":
692             options['lov'] = a
693
694         # Options for commands
695         if o == "--obdtype":
696             options['obdtype'] = a
697         if o == "--obduuid":
698             options['obduuid'] = a
699         if o == "--tcpbuf":
700             options['tcpbuf'] = a
701
702         # lmc options
703         if o in ("-h", "--help"):
704             usage()
705         if o in ("-o", "--output"):
706             options['output'] = a
707         if o in ("-m", "--merge"):
708             options['merge'] = a
709         if o == "--format":
710             options['format'] = 1
711         if o  == "--reformat":
712             options['reformat'] = 1
713         if o  == "--batch":
714             options['batch'] = a
715         if o  in ("--in" , "-i"):
716             options['in'] = a
717             
718     return options, args
719
720
721 # simple class for profiling
722 import time
723 class chrono:
724     def __init__(self):
725         self._start = 0
726     def start(self):
727         self._stop = 0
728         self._start = time.time()
729     def stop(self, msg=''):
730         self._stop = time.time()
731         if msg:
732             self.display(msg)
733     def dur(self):
734         return self._stop - self._start
735     def display(self, msg):
736         d = self.dur()
737         str = '%s: %g secs' % (msg, d)
738         print str
739
740 ############################################################
741 # Main
742 #
743 def do_command(gen, lustre, options, args):
744     if options.has_key('ost'):
745         add_ost(gen, lustre, options, args)
746     elif options.has_key('osc'):
747         add_osc(gen, lustre, options, args)
748     elif options.has_key('mtpt'):
749         add_mtpt(gen, lustre, options, args)
750     elif options.has_key('mds'):
751         add_mds(gen, lustre, options, args)
752     elif options.has_key('net'):
753         add_net(gen, lustre, options, args)
754     elif options.has_key('lov'):
755         add_lov(gen, lustre, options, args)
756     elif options.has_key('route'):
757         add_route(gen, lustre, options, args)
758     elif options.has_key('node'):
759         add_node(gen, lustre, options, args)
760     else:
761         print "Missing command"
762         usage()
763
764 def main():
765     options, args = parse_cmdline(sys.argv[1:])
766     outFile = '-'
767
768     if options.has_key('merge'):
769         outFile = options['merge']
770         if os.access(outFile, os.R_OK):
771             doc = xml.dom.minidom.parse(outFile)
772         else:
773             doc = new_lustre(xml.dom.minidom)
774     elif options.has_key('in'):
775         doc = xml.dom.minidom.parse(options['in'])
776     else:
777         doc = new_lustre(xml.dom.minidom)
778
779     if options.has_key('output'):
780         outFile = options['output']
781
782     lustre = doc.documentElement
783     init_names(lustre)
784     if lustre.tagName != "lustre":
785         print "Existing config not valid."
786         sys.exit(1)
787
788     gen = GenConfig(doc)
789
790     if options.has_key('batch'):
791         fp = open(options['batch'])
792         batchCommands = fp.readlines()
793         fp.close()
794         for cmd in batchCommands:
795             options, args = parse_cmdline(string.split(cmd))
796             do_command(gen, lustre, options, args)
797     else:
798         do_command(gen, lustre, options, args)
799
800     if outFile == '-':
801         PrettyPrint(doc)
802     else:
803         PrettyPrint(doc, open(outFile,"w"))
804
805 if __name__ == "__main__":
806     main()
807
808