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