Whamcloud - gitweb
- the xml format used by lconf has changed considerably
[fs/lustre-release.git] / lustre / utils / lconf
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002 Cluster File Systems, Inc.
4 #   Author: Robert Read <rread@clusterfs.com>
5
6 #   This file is part of Lustre, http://www.lustre.org.
7 #
8 #   Lustre is free software; you can redistribute it and/or
9 #   modify it under the terms of version 2 of the GNU General Public
10 #   License as published by the Free Software Foundation.
11 #
12 #   Lustre is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with Lustre; if not, write to the Free Software
19 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #
21 # lconf - lustre configuration tool
22 #
23 # lconf is the main driver script for starting and stopping
24 # lustre filesystem services.
25 #
26 # Based in part on the XML obdctl modifications done by Brian Behlendorf 
27
28 import sys, getopt
29 import string, os, stat, popen2, socket
30 import re, exceptions
31 import xml.dom.minidom
32 from xml.xpath import Evaluate
33
34 # Global parameters
35 TCP_ACCEPTOR = '../..//portals/linux/utils/acceptor'
36 options = {}
37
38 #
39 # Maximum number of devices to search for.
40 # (the /dev/loop* nodes need to be created beforehand)
41 MAX_LOOP_DEVICES = 256
42
43
44 def usage():
45     print """usage: lconf config.xml
46
47 config.xml          Lustre configuration in xml format.
48 --get <url>         URL to fetch a config file
49 -v | --verbose      Print system commands as they are run
50 -d | --debug        Print system commands, but does not run them
51 --host <hostname>   Load config for <hostname>
52 --cleanup           Cleans up config. (Shutdown)
53 -h | --help         Print this help 
54 """
55     TODO = """
56 --ldap server       LDAP server with lustre config database
57 --reformat          Reformat all devices (will confirm)
58 --lustre="src dir"  Base directory of lustre sources. Used to search
59                     for modules.
60 --portals=src       Portals source 
61 --makeldiff         Translate xml source to LDIFF 
62 --iam myname        ??
63 """
64     sys.exit()
65
66 # ============================================================ 
67 # debugging and error funcs
68
69 def fixme(msg = "this feature"):
70     raise RuntimeError, msg + ' not implmemented yet.'
71
72 def panic(*args):
73     msg = string.join(map(str,args))
74     print msg
75     raise RuntimeError, msg
76
77 def log(*args):
78     msg = string.join(map(str,args))
79     print msg
80
81 def logall(msgs):
82     for s in msgs:
83         print string.strip(s)
84
85 def debug(*args):
86     msg = string.join(map(str,args))
87     if isverbose(): print msg
88
89 def isverbose():
90     return options.has_key('verbose') and  options['verbose'] == 1
91
92 def isnotouch():
93     return options.has_key('debug') and options['debug'] == 1
94
95 # ============================================================
96 # locally defined exceptions
97 class CommandError (exceptions.Exception):
98     def __init__(self, args=None):
99         self.args = args
100
101 # ============================================================
102 # handle lctl interface
103 class LCTLInterface:
104     """
105     Manage communication with lctl
106     """
107
108     def __init__(self, cmd):
109         """
110         Initialize close by finding the lctl binary.
111         """
112         syspath = string.split(os.environ['PATH'], ':')
113         syspath.insert(0, "../utils");
114         self.lctlcmd = None
115         for d in syspath:
116             lctl = os.path.join(d,cmd)
117             if os.access(lctl, os.X_OK):
118                 self.lctl = lctl
119                 break
120         if not self.lctl:
121             raise RuntimeError,  "unable to find lctl binary."
122             
123     def run(self, cmds):
124         """
125         run lctl
126         the cmds are written to stdin of lctl
127         lctl doesn't return errors when run in script mode, so
128         stderr is checked
129         should modify command line to accept multiple commands, or
130         create complex command line options
131         """
132         debug("+", self.lctl, cmds)
133         if isnotouch(): return ([], 0)
134         p = popen2.Popen3(self.lctl, 1)
135         p.tochild.write(cmds + "\n")
136         p.tochild.close()
137         out = p.fromchild.readlines()
138         ret = p.poll()
139         err = p.childerr.readlines()
140         if ret or len(err):
141             log (self.lctl, "error:", ret)
142             logall(err)
143             raise CommandError, err
144         return ret, out
145
146     def network(self, net, nid):
147         """ initialized network and add "self" """
148         # Idea: "mynid" could be used for all network types to add "self," and then
149         # this special case would be gone and the "self" hack would be hidden.
150         if net  == 'tcp':
151             cmds =  """
152   network %s
153   mynid %s
154   add_uuid self %s
155   quit""" % (net, nid, nid)
156         else:
157             cmds =  """
158   network %s
159   add_uuid self %s
160   quit""" % (net, nid)
161             
162         self.run(cmds)
163
164     # create a new connection 
165     def connect(self, net, nid, port, servuuid, send_buf, read_buf):
166         # XXX: buf size params not used yet
167         cmds =  """
168   network %s
169   connect %s %d
170   add_uuid %s %s
171   quit""" % (net, nid, port,  servuuid, nid)
172         self.run(cmds)
173                 
174     # create a new connection 
175     def add_route(self, net, to, via):
176         cmds =  """
177         """ 
178         #self.run(cmds)
179
180     # create a new device with lctl
181     def disconnect(self, net, nid, port, servuuid):
182         cmds =  """
183   network %s
184   disconnect %s 
185   quit""" % (net, nid)
186         self.run(cmds)
187
188     # create a new device with lctl
189     def newdev(self, attach, setup = ""):
190         cmds = """
191   newdev
192   attach %s
193   setup %s
194   quit""" % (attach, setup)
195         self.run(cmds)
196
197     # cleanup a device
198     def cleanup(self, name, uuid):
199         cmds = """
200   device $%s
201   cleanup
202   detach
203   quit""" % (name)
204         self.run(cmds)
205
206     # create an lov
207     def lovconfig(self, uuid, mdcuuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist):
208         cmds = """
209   device $%s
210   probe
211   lovconfig %s %d %d %d %s %s
212   quit""" % (mdcuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
213         self.run(cmds)
214
215 # ============================================================
216 # Various system-level functions
217 # (ideally moved to their own module)
218
219 # Run a command and return the output and status.
220 # stderr is sent to /dev/null, could use popen3 to
221 # save it if necessary
222 def run(*args):
223     cmd = string.join(map(str,args))
224     debug ("+", cmd)
225     if isnotouch(): return (0, [])
226     f = os.popen(cmd + ' 2>&1')
227     out = f.readlines()
228     ret = f.close()
229     if ret:
230         ret = ret >> 8
231     else:
232         ret = 0
233     return (ret, out)
234
235 # Run a command in the background.
236 def run_daemon(*args):
237     cmd = string.join(map(str,args))
238     debug ("+", cmd)
239     if isnotouch(): return 0
240     f = os.popen(cmd + ' 2>&1')
241     ret = f.close()
242     if ret:
243         ret = ret >> 8
244     else:
245         ret = 0
246     return ret
247
248
249 # is the path a block device?
250 def is_block(path):
251     s = ()
252     try:
253         s =  os.stat(path)
254     except OSError:
255         return 0
256     return stat.S_ISBLK(s[stat.ST_MODE])
257
258 # build fs according to type
259 # fixme: dangerous
260 def mkfs(fstype, dev):
261     if(fstype in ('ext3', 'extN')):
262         mkfs = 'mkfs.ext2 -j -b 4096'
263     else:
264         print 'unsupported fs type: ', fstype
265     if not is_block(dev):
266         force = '-F'
267     else:
268         force = ''
269     (ret, out) = run (mkfs, force, dev)
270     if ret:
271         panic("Unable to build fs:", dev)
272
273 # some systems use /dev/loopN, some /dev/loop/N
274 def loop_base():
275     import re
276     loop = '/dev/loop'
277     if not os.access(loop + str(0), os.R_OK):
278         loop = loop + '/'
279         if not os.access(loop + str(0), os.R_OK):
280             panic ("can't access loop devices")
281     return loop
282     
283 # find loop device assigned to thefile
284 def find_loop(file):
285     loop = loop_base()
286     for n in xrange(0, MAX_LOOP_DEVICES):
287         dev = loop + str(n)
288         if os.access(dev, os.R_OK):
289             (stat, out) = run('losetup', dev)
290             if (out and stat == 0):
291                 m = re.search(r'\((.*)\)', out[0])
292                 if m and file == m.group(1):
293                     return dev
294         else:
295             break
296     return ''
297
298 # create file if necessary and assign the first free loop device
299 def init_loop(file, size, fstype):
300     dev = find_loop(file)
301     if dev:
302         print 'WARNING file:', file, 'already mapped to', dev
303         return dev
304     if not os.access(file, os.R_OK | os.W_OK):
305         run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,  file))
306     loop = loop_base()
307     # find next free loop
308     for n in xrange(0, MAX_LOOP_DEVICES):
309         dev = loop + str(n)
310         if os.access(dev, os.R_OK):
311             (stat, out) = run('losetup', dev)
312             if (stat):
313                 run('losetup', dev, file)
314                 return dev
315         else:
316             print "out of loop devices"
317             return ''
318     print "out of loop devices"
319     return ''
320
321 # undo loop assignment
322 def clean_loop(file):
323     dev = find_loop(file)
324     if dev:
325         ret, out = run('losetup -d', dev)
326         if ret:
327             log('unable to clean loop device:', dev, 'for file:', file)
328             logall(out)
329
330 # initialize a block device if needed
331 def block_dev(dev, size, fstype, format):
332     if isnotouch(): return dev
333     if not is_block(dev):
334         dev = init_loop(dev, size, fstype)
335     if (format == 'yes'):
336         mkfs(fstype, dev)
337     return dev
338
339 # ============================================================
340 # Classes to prepare and cleanup the various objects
341 #
342 class Module:
343     """ Base class for the rest of the modules. The default cleanup method is
344     defined here, as well as some utilitiy funcs.
345     """
346     def __init__(self, tag_name, node):
347         self.dom_node = node
348         self.tag_name = tag_name
349         self.name = node.getAttribute('name')
350         self.uuid = node.getAttribute('uuid')
351
352     def info(self, *args):
353         msg = string.join(map(str,args))
354         print self.tag_name + ":", self.name, self.uuid, msg
355
356     def cleanup(self):
357         """ default cleanup, used for most modules """
358         self.info()
359         try:
360             lctl.cleanup(self.name, self.uuid)
361         except CommandError:
362             print "cleanup failed: ", self.name
363
364 class Network(Module):
365     def __init__(self,node):
366         Module.__init__(self, 'NETWORK', node)
367         self.net_type = node.getAttribute('type')
368         self.nid = getText(node, 'server', "")
369         self.port = int(getText(node, 'port', 0))
370         self.send_buf = int(getText(node, 'send_buf', 0))
371         self.read_buf = int(getText(node, 'read_buf', 0))
372
373     def prepare(self):
374         self.info(self.net_type, self.nid, self.port)
375         if type == 'tcp':
376             ret = run_daemon(TCP_ACCEPTOR, port)
377             if ret:
378                 print "error:", ret
379                 raise CommandError, "cannot run acceptor"
380         lctl.network(self.net_type, self.nid)
381         lctl.newdev(attach = "ptlrpc RPCDEV")
382
383     def cleanup(self):
384         self.info(self.net_type, self.nid, self.port)
385         try:
386             lctl.cleanup("RPCDEV", "")
387         except CommandError:
388             print "cleanup failed: ", self.name
389         if type == 'tcp':
390             # yikes, this ugly! need to save pid in /var/something
391             run("killall acceptor")
392
393 class LDLM(Module):
394     def __init__(self,node):
395         Module.__init__(self, 'LDLM', node)
396     def prepare(self):
397         self.info()
398         lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
399                     setup ="")
400
401 class LOV(Module):
402     def __init__(self,node):
403         Module.__init__(self, 'LOV', node)
404         devs = node.getElementsByTagName('devices')[0]
405         self.stripe_sz = int(devs.getAttribute('stripesize'))
406         self.stripe_off = int(devs.getAttribute('stripeoffset'))
407         self.pattern = int(devs.getAttribute('pattern'))
408         mdcref =  node.getElementsByTagName('mdc_ref')[0]
409         self.mdcuuid = mdcref.getAttribute('uuidref')
410         mdc= lookup(node.parentNode, self.mdcuuid)
411         mdsref =  mdc.getElementsByTagName('mds_ref')[0]
412         self.mdsuuid = mdsref.getAttribute('uuidref')
413         mds= lookup(node.parentNode, self.mdsuuid)
414         self.mdsname = getName(mds)
415         devlist = ""
416         stripe_cnt = 0
417         for child in devs.childNodes:
418             if child.nodeName == 'osc_ref':
419                 devlist = devlist +  child.getAttribute('uuidref') + " "
420                 strip_cnt = stripe_cnt + 1
421         self.devlist = devlist
422         self.stripe_cnt = strip_cnt
423
424     def prepare(self):
425         for osc_uuid in string.split(self.devlist):
426             osc = lookup(self.dom_node.parentNode, osc_uuid)
427             if osc:
428                 n = OSC(osc)
429                 n.prepare()
430             else:
431                 panic('osc not found:', osc_uuid)
432         self.info(self.mdcuuid, self.stripe_cnt, self.stripe_sz, self.stripe_off, self.pattern,
433         self.devlist, self.mdsname)
434         lctl.lovconfig(self.uuid, self.mdsname, self.stripe_cnt,
435                        self.stripe_sz, self.stripe_off, self.pattern,
436                        self.devlist)
437         lctl.newdev(attach="lov %s %s" % (self.name, self.uuid),
438                     setup ="%s" % (self.mdcuuid))
439
440 class MDS(Module):
441     def __init__(self,node):
442         Module.__init__(self, 'MDS', node)
443         self.devname, self.size = getDevice(node)
444         self.fstype = getText(node, 'fstype')
445         self.format = getText(node, 'autoformat', "no")
446
447     def prepare(self):
448         self.info(self.devname, self.fstype, self.format)
449         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
450         lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
451                     setup ="%s %s" %(blkdev, self.fstype))
452     def cleanup(self):
453         Module.cleanup(self)
454         clean_loop(self.devname)
455
456 class MDC(Module):
457     def __init__(self,node):
458         Module.__init__(self, 'MDC', node)
459         ref = node.getElementsByTagName('mds_ref')[0]
460         self.mds_uuid = ref.getAttribute('uuidref')
461
462     def prepare(self):
463         self.info(self.mds_uuid)
464         mds = lookup(self.dom_node.parentNode, self.mds_uuid)
465         if mds == None:
466             panic(self.mdsuuid, "not found.")
467         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
468         srv = Network(net)
469         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
470         lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
471                         setup ="%s %s" %(self.mds_uuid, srv.uuid))
472             
473     def cleanup(self):
474         self.info(self.mds_uuid)
475         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
476         srv = Network(net)
477         try:
478             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
479             lctl.cleanup(self.name, self.uuid)
480         except CommandError:
481             print "cleanup failed: ", self.name
482
483 class OBD(Module):
484     def __init__(self, node):
485         Module.__init__(self, 'OBD', node)
486         self.obdtype = node.getAttribute('type')
487         self.devname, self.size = getDevice(node)
488         self.fstype = getText(node, 'fstype')
489         self.format = getText(node, 'autoformat', 'yes')
490
491     # need to check /proc/mounts and /etc/mtab before
492     # formatting anything.
493     # FIXME: check if device is already formatted.
494     def prepare(self):
495         self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
496         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
497         lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
498                     setup ="%s %s" %(blkdev, self.fstype))
499     def cleanup(self):
500         Module.cleanup(self)
501         clean_loop(self.devname)
502
503 class OST(Module):
504     def __init__(self,node):
505         Module.__init__(self, 'OST', node)
506         ref = node.getElementsByTagName('obd_ref')[0]
507         self.obd_uuid = ref.getAttribute('uuidref')
508
509     def prepare(self):
510         self.info(self.obd_uuid)
511         lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
512                     setup ="%s" % (self.obd_uuid))
513
514 class OSC(Module):
515     def __init__(self,node):
516         Module.__init__(self, 'OSC', node)
517         ref = node.getElementsByTagName('obd_ref')[0]
518         self.obd_uuid = ref.getAttribute('uuidref')
519         ref = node.getElementsByTagName('ost_ref')[0]
520         self.ost_uuid = ref.getAttribute('uuidref')
521
522     def prepare(self):
523         self.info(self.obd_uuid, self.ost_uuid)
524         net = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
525         srv = Network(net)
526         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
527         lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
528                     setup ="%s %s" %(self.obd_uuid, self.ost_uuid))
529
530     def cleanup(self):
531         self.info(self.obd_uuid, self.ost_uuid)
532         net_uuid = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
533         srv = Network(net)
534         try:
535             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
536             lctl.cleanup(self.name, self.uuid)
537         except CommandError:
538             print "cleanup failed: ", self.name
539
540 class Mountpoint(Module):
541     def __init__(self,node):
542         Module.__init__(self, 'MTPT', node)
543         self.path = getText(node, 'path')
544         ref = node.getElementsByTagName('mdc_ref')[0]
545         self.mdc_uuid = ref.getAttribute('uuidref')
546         ref = node.getElementsByTagName('osc_ref')[0]
547         self.lov_uuid = ref.getAttribute('uuidref')
548
549     def prepare(self):
550         self.info(self.path, self.mdc_uuid,self.lov_uuid)
551         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
552               (self.lov_uuid, self.mdc_uuid, self.path)
553         run("mkdir", self.path)
554         ret, val = run(cmd)
555         if ret:
556             panic("mount failed:", self.path)
557     def cleanup(self):
558         self.info(self.path, self.mdc_uuid,self.lov_uuid)
559         run("umount", self.path)
560
561 # ============================================================
562 # XML processing and query
563 # TODO: Change query funcs to use XPath, which is muc cleaner
564
565 def getDevice(obd):
566     dev = obd.getElementsByTagName('device')[0]
567     dev.normalize();
568     try:
569         size = int(dev.getAttribute('size'))
570     except ValueError:
571         size = 0
572     return dev.firstChild.data, size
573
574 # Get the text content from the first matching child
575 def getText(node, tag, default=""):
576     list = node.getElementsByTagName(tag)
577     if len(list) > 0:
578         node = list[0]
579         node.normalize()
580         return node.firstChild.data
581     else:
582         return default
583
584 def get_ost_net(node, uuid):
585     path = '//*[@uuid="%s"]/network_ref' % (uuid)
586     ret = Evaluate(path, node)
587     if ret:
588         uuid = ret[0].getAttribute('uuidref')
589     else:
590         return None
591     net = lookup(node, uuid)
592     return net
593     
594
595 # Recusively search from node for a uuid
596 def lookup(node, uuid):
597     path = '//*[@uuid="%s"]' % (uuid)
598     ret = Evaluate(path, node)
599     if ret:
600         return ret[0]
601     else:
602         return None
603     
604 # would like to do performance test on these two lookups
605 #def lookup(node, uuid):
606 #    for n in node.childNodes:
607 #        if n.nodeType == n.ELEMENT_NODE:
608 #            if getUUID(n) == uuid:
609 #                return n
610 #            else:
611 #                n = lookup(n, uuid)
612 #                if n: return n
613 #    return None
614
615 # Get name attribute of node
616 def getName(node):
617     return node.getAttribute('name')
618
619 def getRef(node):
620     return node.getAttribute('uuidref')
621
622 # Get name attribute of node
623 def getUUID(node):
624     return node.getAttribute('uuid')
625
626 # the tag name is the service type
627 # fixme: this should do some checks to make sure the node is a service
628 def getServiceType(node):
629     return node.nodeName
630
631 #
632 # determine what "level" a particular node is at.
633 # the order of iniitailization is based on level.  objects
634 # are assigned a level based on type:
635 #  net,devices,ldlm:1, obd, mdd:2  mds,ost:3 osc,mdc:4 mounts:5
636 def getServiceLevel(node):
637     type = getServiceType(node)
638     if type in ('network',):
639         return 1
640     if type in ('device', 'ldlm'):
641         return 2
642     elif type in ('obd', 'mdd'):
643         return 3
644     elif type in ('mds','ost'):
645         return 4
646     elif type in ('mdc','osc'):
647         return 5
648     elif type in ('lov',):
649         return 6
650     elif type in ('mountpoint',):
651         return 7
652     return 0
653
654 #
655 # return list of services in a profile. list is a list of tuples
656 # [(level, node),]
657 def getServices(lustreNode, profileNode):
658     list = []
659     for n in profileNode.childNodes:
660         if n.nodeType == n.ELEMENT_NODE:
661             servNode = lookup(lustreNode, getRef(n))
662             if not servNode:
663                 print n
664                 panic('service not found: ' + getRef(n))
665             level = getServiceLevel(servNode)
666             list.append((level, servNode))
667     list.sort()
668     return list
669
670 def getByName(lustreNode, tag, name):
671     ndList = lustreNode.getElementsByTagName(tag)
672     for nd in ndList:
673         if getName(nd) == name:
674             return nd
675     return None
676     
677
678 # ============================================================
679 # lconf level logic
680 # Start a service.
681 def startService(node, cleanFlag):
682     type = getServiceType(node)
683     debug('Starting service:', type, getName(node), getUUID(node))
684     # there must be a more dynamic way of doing this...
685     n = None
686     if type == 'ldlm':
687         n = LDLM(node)
688     elif type == 'lov':
689         n = LOV(node)
690     elif type == 'network':
691         n = Network(node)
692     elif type == 'obd':
693         n = OBD(node)
694     elif type == 'ost':
695         n = OST(node)
696     elif type == 'mds':
697         n = MDS(node)
698     elif type == 'osc':
699         n = OSC(node)
700     elif type == 'mdc':
701         n = MDC(node)
702     elif type == 'mountpoint':
703         n = Mountpoint(node)
704     else:
705         panic ("unknown service type:", type)
706
707     if cleanFlag:
708         n.cleanup()
709     else:
710         n.prepare()
711
712 #
713 # Prepare the system to run lustre using a particular profile
714 # in a the configuration. 
715 #  * load & the modules
716 #  * setup networking for the current node
717 #  * make sure partitions are in place and prepared
718 #  * initialize devices with lctl
719 # Levels is important, and needs to be enforced.
720 def startProfile(lustreNode, profileNode, cleanFlag):
721     if not profileNode:
722         panic("profile:", profile, "not found.")
723     services = getServices(lustreNode, profileNode)
724     if cleanFlag:
725         services.reverse()
726     for s in services:
727         startService(s[1], cleanFlag)
728
729 #
730 # Load profile for 
731 def doHost(lustreNode, hosts, cleanFlag):
732     node = None
733     for h in hosts:
734         node = getByName(lustreNode, 'node', h)
735         if node:
736             break
737
738     if not node:
739         print 'No host entry found.'
740         return
741
742     reflist = node.getElementsByTagName('profile')
743     for profile in reflist:
744             startProfile(lustreNode,  profile, cleanFlag)
745
746 # Command line processing
747 #
748 def parse_cmdline(argv):
749     short_opts = "hdv"
750     long_opts = ["ldap", "reformat", "lustre=", "verbose",
751                  "portals=", "makeldiff", "cleanup", "iam=",
752                  "help", "debug", "host=", "get="]
753     opts = []
754     args = []
755     global options
756     try:
757         opts, args = getopt.getopt(argv, short_opts, long_opts)
758     except getopt.GetoptError:
759         print "invalid opt"
760         usage()
761
762     for o, a in opts:
763         if o in ("-h", "--help"):
764             usage()
765         if o == "--cleanup":
766             options['cleanup'] = 1
767         if o in ("-v", "--verbose"):
768             options['verbose'] = 1
769         if o in ("-d", "--debug"):
770             options['debug'] = 1
771             options['verbose'] = 1
772         if o == "--portals":
773             options['portals'] = a
774         if o == "--lustre":
775             options['lustre'] = a
776         if o  == "--reformat":
777             options['reformat'] = 1
778         if o  == "--host":
779             options['hostname'] = [a]
780         if o  == "--get":
781             options['url'] = a
782     return args
783
784 def fetch(url):
785     import urllib
786     data = ""
787     try:
788         s = urllib.urlopen(url)
789         data = s.read()
790     except:
791         usage()
792     return data
793
794 # Initialize or shutdown lustre according to a configuration file
795 #   * prepare the system for lustre
796 #   * configure devices with lctl
797 # Shutdown does steps in reverse
798 #
799 lctl = LCTLInterface('lctl')
800 def main():
801     global options
802     args = parse_cmdline(sys.argv[1:])
803     if len(args) > 0:
804         dom = xml.dom.minidom.parse(args[0])
805     elif options.has_key('url'):
806         xmldata = fetch(options['url'])
807         dom = xml.dom.minidom.parseString(xmldata)
808     else:
809         usage()
810
811     if not options.has_key('hostname'):
812         options['hostname'] = []
813         host = socket.gethostname()
814         if len(host) > 0:
815             options['hostname'].append(host)
816         options['hostname'].append('localhost')
817     print "configuring for host: ", options['hostname']
818     doHost(dom.documentElement, options['hostname'], options.has_key('cleanup') )
819
820 if __name__ == "__main__":
821     main()