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