Whamcloud - gitweb
- fix child wait
[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, time
30 import re, exceptions
31 import xml.dom.minidom
32
33 # Global parameters
34 TCP_ACCEPTOR = ''
35
36 #
37 # Maximum number of devices to search for.
38 # (the /dev/loop* nodes need to be created beforehand)
39 MAX_LOOP_DEVICES = 256
40
41
42 def usage():
43     print """usage: lconf config.xml
44
45 config.xml          Lustre configuration in xml format.
46 --get <url>         URL to fetch a config file
47 -v | --verbose      Print system commands as they are run
48 -d | --debug        Print system commands, but does not run them
49 --node <nodename>   Load config for <nodename>
50 --cleanup           Cleans up config. (Shutdown)
51 -h | --help         Print this help 
52 --gdb               Create a gdb script to load the modules. Prints message
53                     after creating script and sleeps for 5 seconds.
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 """
63     sys.exit()
64
65 # ============================================================
66 # Config parameters, encapsulated in a class
67 class Config:
68     def __init__(self):
69         # flags
70         self._noexec = 0
71         self._verbose = 0
72         self._reformat = 0
73         self._cleanup = 0
74         self._gdb = 0
75         self._nomod = 0
76         self._nosetup = 0
77         # parameters
78         self._modules = None
79         self._node = None
80         self._url = None
81         self._gdb_script = '/tmp/ogdb'
82         self._debug_path = '/tmp/lustre-log'
83         self._src_dir = None
84
85     def verbose(self, flag = None):
86         if flag: self._verbose = flag
87         return self._verbose
88
89     def noexec(self, flag = None):
90         if flag: self._noexec = flag
91         return self._noexec
92
93     def reformat(self, flag = None):
94         if flag: self._reformat = flag
95         return self._reformat
96
97     def cleanup(self, flag = None):
98         if flag: self._cleanup = flag
99         return self._cleanup
100
101     def gdb(self, flag = None):
102         if flag: self._gdb = flag
103         return self._gdb
104
105     def nomod(self, flag = None):
106         if flag: self._nomod = flag
107         return self._nomod
108
109     def nosetup(self, flag = None):
110         if flag: self._nosetup = flag
111         return self._nosetup
112
113     def node(self, val = None):
114         if val: self._node = val
115         return self._node
116
117     def url(self, val = None):
118         if val: self._url = val
119         return self._url
120
121     def gdb_script(self):
122         if os.path.isdir('/r'):
123             return '/r' + self._gdb_script
124         else:
125             return self._gdb_script
126
127     def debug_path(self):
128         if os.path.isdir('/r'):
129             return '/r' + self._debug_path
130         else:
131             return self._debug_path
132
133     def src_dir(self, val = None):
134         if val: self._url = val
135         return self._url
136
137 config = Config()
138
139 # ============================================================ 
140 # debugging and error funcs
141
142 def fixme(msg = "this feature"):
143     raise RuntimeError, msg + ' not implmemented yet.'
144
145 def panic(*args):
146     msg = string.join(map(str,args))
147     print msg
148     if not config.noexec():
149         raise RuntimeError, msg
150
151 def log(*args):
152     msg = string.join(map(str,args))
153     print msg
154
155 def logall(msgs):
156     for s in msgs:
157         print string.strip(s)
158
159 def debug(*args):
160     if config.verbose():
161         msg = string.join(map(str,args))
162         print msg
163
164 # ============================================================
165 # locally defined exceptions
166 class CommandError (exceptions.Exception):
167     def __init__(self, args=None):
168         self.args = args
169
170 # ============================================================
171 # handle lctl interface
172 class LCTLInterface:
173     """
174     Manage communication with lctl
175     """
176
177     def __init__(self, cmd):
178         """
179         Initialize close by finding the lctl binary.
180         """
181         self.lctl = find_prog(cmd)
182         if not self.lctl:
183             if config.noexec():
184                 debug('! lctl not found')
185                 self.lctl = 'lctl'
186             else:
187                 raise CommandError,  "unable to find lctl binary."
188             
189     def run(self, cmds):
190         """
191         run lctl
192         the cmds are written to stdin of lctl
193         lctl doesn't return errors when run in script mode, so
194         stderr is checked
195         should modify command line to accept multiple commands, or
196         create complex command line options
197         """
198         debug("+", self.lctl, cmds)
199         if config.noexec(): return (0, [])
200         p = popen2.Popen3(self.lctl, 1)
201         p.tochild.write(cmds + "\n")
202         p.tochild.close()
203         ret = p.wait()
204         out = p.fromchild.readlines()
205         for l in out:
206             debug('lctl:',string.strip(l))
207         err = p.childerr.readlines()
208         if ret or len(err):
209             log (self.lctl, "error:", ret)
210             logall(err)
211             raise CommandError, err
212         return ret, out
213
214     def network(self, net, nid):
215         """ initialized network and add "self" """
216         # Idea: "mynid" could be used for all network types to add "self," and then
217         # this special case would be gone and the "self" hack would be hidden.
218         if net  == 'tcp':
219             cmds =  """
220   network %s
221   mynid %s
222   add_uuid self %s
223   quit""" % (net, nid, nid)
224         else:
225             cmds =  """
226   network %s
227   add_uuid self %s
228   quit""" % (net, nid)
229             
230         self.run(cmds)
231
232     # create a new connection 
233     def connect(self, net, nid, port, servuuid, send_buf, read_buf):
234         # XXX: buf size params not used yet
235         cmds =  """
236   network %s
237   connect %s %d
238   add_uuid %s %s
239   quit""" % (net, nid, port,  servuuid, nid)
240         self.run(cmds)
241                 
242     # create a new connection 
243     def add_route(self, net, to, via):
244         cmds =  """
245         """ 
246         #self.run(cmds)
247
248     # disconnect one connection
249     def disconnect(self, net, nid, port, servuuid):
250         cmds =  """
251   network %s
252   disconnect %s 
253   del_uuid %s
254   quit""" % (net, nid, servuuid)
255         self.run(cmds)
256
257     # disconnect all connections
258     def disconnectAll(self, net):
259         cmds =  """
260   network %s
261   disconnect
262   del_uuid self
263   quit""" % (net)
264         self.run(cmds)
265
266     # create a new device with lctl
267     def newdev(self, attach, setup = ""):
268         cmds = """
269   newdev
270   attach %s
271   setup %s
272   quit""" % (attach, setup)
273         self.run(cmds)
274
275     # cleanup a device
276     def cleanup(self, name, uuid):
277         cmds = """
278   device $%s
279   cleanup
280   detach
281   quit""" % (name)
282         self.run(cmds)
283
284     # create an lov
285     def lovconfig(self, uuid, mdsuuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist):
286         cmds = """
287   device $%s
288   probe
289   lovconfig %s %d %d %d %s %s
290   quit""" % (mdsuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
291         self.run(cmds)
292
293 # ============================================================
294 # Various system-level functions
295 # (ideally moved to their own module)
296
297 # Run a command and return the output and status.
298 # stderr is sent to /dev/null, could use popen3 to
299 # save it if necessary
300 def run(*args):
301     cmd = string.join(map(str,args))
302     debug ("+", cmd)
303     if config.noexec(): return (0, [])
304     f = os.popen(cmd + ' 2>&1')
305     out = f.readlines()
306     ret = f.close()
307     if ret:
308         ret = ret >> 8
309     else:
310         ret = 0
311     return (ret, out)
312
313 # Run a command in the background.
314 def run_daemon(*args):
315     cmd = string.join(map(str,args))
316     debug ("+", cmd)
317     if config.noexec(): return 0
318     f = os.popen(cmd + ' 2>&1')
319     ret = f.close()
320     if ret:
321         ret = ret >> 8
322     else:
323         ret = 0
324     return ret
325
326 # Determine full path to use for an external command
327 # searches dirname(argv[0]) first, then PATH
328 def find_prog(cmd):
329     syspath = string.split(os.environ['PATH'], ':')
330     cmdpath = os.path.dirname(sys.argv[0])
331     syspath.insert(0, cmdpath);
332     syspath.insert(0, os.path.join(cmdpath, '../../portals/linux/utils/'))
333     for d in syspath:
334         prog = os.path.join(d,cmd)
335         if os.access(prog, os.X_OK):
336             return prog
337     return ''
338
339 # Recursively look for file starting at base dir
340 def do_find_file(base, mod):
341     fullname = os.path.join(base, mod)
342     if os.access(fullname, os.R_OK):
343         return fullname
344     for d in os.listdir(base):
345         dir = os.path.join(base,d)
346         if os.path.isdir(dir):
347             module = do_find_file(dir, mod)
348             if module:
349                 return module
350
351 def find_module(src_dir, modname):
352     mod = '%s.o' % (modname)
353     search = (src_dir + "/lustre", src_dir + "/portals/linux")
354     for d in search:
355         try:
356             module = do_find_file(d, mod)
357             if module:
358                 return module
359         except OSError:
360             pass
361     return None
362
363 # is the path a block device?
364 def is_block(path):
365     s = ()
366     try:
367         s =  os.stat(path)
368     except OSError:
369         return 0
370     return stat.S_ISBLK(s[stat.ST_MODE])
371
372 # build fs according to type
373 # fixme: dangerous
374 def mkfs(fstype, dev):
375     if(fstype in ('ext3', 'extN')):
376         mkfs = 'mkfs.ext2 -j -b 4096'
377     else:
378         print 'unsupported fs type: ', fstype
379     if not is_block(dev):
380         force = '-F'
381     else:
382         force = ''
383     (ret, out) = run (mkfs, force, dev)
384     if ret:
385         panic("Unable to build fs:", dev)
386     # enable hash tree indexing on fs
387     if fstype == 'extN':
388         htree = 'echo "feature FEATURE_C5" | debugfs -w'
389         (ret, out) = run (htree, dev)
390         if ret:
391             panic("Unable to enable htree:", dev)
392
393 # some systems use /dev/loopN, some /dev/loop/N
394 def loop_base():
395     import re
396     loop = '/dev/loop'
397     if not os.access(loop + str(0), os.R_OK):
398         loop = loop + '/'
399         if not os.access(loop + str(0), os.R_OK):
400             panic ("can't access loop devices")
401     return loop
402     
403 # find loop device assigned to thefile
404 def find_loop(file):
405     loop = loop_base()
406     for n in xrange(0, MAX_LOOP_DEVICES):
407         dev = loop + str(n)
408         if os.access(dev, os.R_OK):
409             (stat, out) = run('losetup', dev)
410             if (out and stat == 0):
411                 m = re.search(r'\((.*)\)', out[0])
412                 if m and file == m.group(1):
413                     return dev
414         else:
415             break
416     return ''
417
418 # create file if necessary and assign the first free loop device
419 def init_loop(file, size, fstype):
420     dev = find_loop(file)
421     if dev:
422         print 'WARNING file:', file, 'already mapped to', dev
423         return dev
424     if not os.access(file, os.R_OK | os.W_OK):
425         run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,  file))
426     loop = loop_base()
427     # find next free loop
428     for n in xrange(0, MAX_LOOP_DEVICES):
429         dev = loop + str(n)
430         if os.access(dev, os.R_OK):
431             (stat, out) = run('losetup', dev)
432             if (stat):
433                 run('losetup', dev, file)
434                 return dev
435         else:
436             print "out of loop devices"
437             return ''
438     print "out of loop devices"
439     return ''
440
441 # undo loop assignment
442 def clean_loop(file):
443     dev = find_loop(file)
444     if dev:
445         ret, out = run('losetup -d', dev)
446         if ret:
447             log('unable to clean loop device:', dev, 'for file:', file)
448             logall(out)
449
450 # initialize a block device if needed
451 def block_dev(dev, size, fstype, format):
452     if config.noexec(): return dev
453     if not is_block(dev):
454         dev = init_loop(dev, size, fstype)
455     if (format == 'yes'):
456         mkfs(fstype, dev)
457     return dev
458
459 # ============================================================
460 # Classes to prepare and cleanup the various objects
461 #
462 class Module:
463     """ Base class for the rest of the modules. The default cleanup method is
464     defined here, as well as some utilitiy funcs.
465     """
466     def __init__(self, tag_name, node):
467         self.dom_node = node
468         self.tag_name = tag_name
469         self.name = node.getAttribute('name')
470         self.uuid = node.getAttribute('uuid')
471         self.kmodule_list = []
472         
473     def info(self, *args):
474         msg = string.join(map(str,args))
475         print self.tag_name + ":", self.name, self.uuid, msg
476
477     def cleanup(self):
478         """ default cleanup, used for most modules """
479         self.info()
480         try:
481             lctl.cleanup(self.name, self.uuid)
482         except CommandError:
483             print "cleanup failed: ", self.name
484
485     def add_module(self, modname):
486         """Append a module to list of modules to load."""
487         self.kmodule_list.append(modname)
488
489     def mod_loaded(self, modname):
490         """Check if a module is already loaded. Look in /proc/modules for it."""
491         fp = open('/proc/modules')
492         lines = fp.readlines()
493         fp.close()
494         # please forgive my tired fingers for this one
495         ret = filter(lambda word, mod=modname: word == mod,
496                      map(lambda line: string.split(line)[0], lines))
497         return ret
498
499     def load_module(self):
500         """Load all the modules in the list in the order they appear."""
501         for mod in self.kmodule_list:
502             #  (rc, out) = run ('/sbin/lsmod | grep -s', mod)
503             if self.mod_loaded(mod) and not config.noexec():
504                 continue
505             if config.src_dir():
506                 module = find_module(config.src_dir(), mod)
507                 if not module:
508                     panic('module not found:', mod)
509                 (rc, out)  = run('/sbin/insmod', module)
510                 if rc:
511                     raise CommandError, "insmod failed: %s" %(module)
512             else:
513                 (rc, out) = run('/sbin/modprobe', mod)
514                 if rc:
515                     raise CommandError, "modprobe failed: %s" %(module)
516             
517     def cleanup_module(self):
518         """Unload the modules in the list in reverse order."""
519         rev = self.kmodule_list
520         rev.reverse()
521         for mod in rev:
522             debug('rmmod', mod)
523             if config.noexec():
524                 continue
525             run('/sbin/rmmod', mod)
526         
527
528 class Network(Module):
529     def __init__(self,node):
530         Module.__init__(self, 'NETWORK', node)
531         self.net_type = node.getAttribute('type')
532         self.nid = getText(node, 'server', "")
533         self.port = int(getText(node, 'port', 0))
534         self.send_buf = int(getText(node, 'send_buf', 0))
535         self.read_buf = int(getText(node, 'read_buf', 0))
536
537         self.add_module('portals')
538         if self.net_type == 'tcp':
539             self.add_module('ksocknal')
540         if self.net_type == 'elan':
541             self.add_module('kqswnal')
542         if self.net_type == 'gm':
543             self.add_module('kgmnal')
544         self.add_module('obdclass')
545         self.add_module('ptlrpc')
546
547     def prepare(self):
548         self.info(self.net_type, self.nid, self.port)
549         if self.net_type == 'tcp':
550             ret = run_daemon(TCP_ACCEPTOR, self.port)
551             if ret:
552                 print "error:", ret
553                 raise CommandError, "cannot run acceptor"
554         lctl.network(self.net_type, self.nid)
555         lctl.newdev(attach = "ptlrpc RPCDEV")
556
557     def cleanup(self):
558         self.info(self.net_type, self.nid, self.port)
559         try:
560             lctl.cleanup("RPCDEV", "")
561             lctl.disconnectAll(self.net_type)
562         except CommandError:
563             print "cleanup failed: ", self.name
564         if self.net_type == 'tcp':
565             # yikes, this ugly! need to save pid in /var/something
566             run("killall acceptor")
567
568 class LDLM(Module):
569     def __init__(self,node):
570         Module.__init__(self, 'LDLM', node)
571         self.add_module('ldlm')
572         self.add_module('extN') # yuck, fix dupe handling and move this
573     def prepare(self):
574         self.info()
575         lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
576                     setup ="")
577
578 class LOV(Module):
579     def __init__(self,node):
580         Module.__init__(self, 'LOV', node)
581         devs = node.getElementsByTagName('devices')[0]
582         self.stripe_sz = int(devs.getAttribute('stripesize'))
583         self.stripe_off = int(devs.getAttribute('stripeoffset'))
584         self.pattern = int(devs.getAttribute('pattern'))
585         mdsref =  node.getElementsByTagName('mds_ref')[0]
586         self.mdsuuid = mdsref.getAttribute('uuidref')
587         mds= lookup(node.parentNode, self.mdsuuid)
588         self.mdsname = getName(mds)
589         devlist = ""
590         stripe_cnt = 0
591         for child in devs.childNodes:
592             if child.nodeName == 'osc_ref':
593                 devlist = devlist +  child.getAttribute('uuidref') + " "
594                 stripe_cnt = stripe_cnt + 1
595         self.devlist = devlist
596         self.stripe_cnt = stripe_cnt
597         self.add_module('osc')
598         self.add_module('lov')
599
600     def prepare(self):
601         self.info(self.mdsuuid, self.stripe_cnt, self.stripe_sz, self.stripe_off, self.pattern,
602         self.devlist, self.mdsname)
603         lctl.lovconfig(self.uuid, self.mdsname, self.stripe_cnt,
604                        self.stripe_sz, self.stripe_off, self.pattern,
605                        self.devlist)
606
607     def cleanup(self):
608         pass
609
610 class MDS(Module):
611     def __init__(self,node):
612         Module.__init__(self, 'MDS', node)
613         self.devname, self.size = getDevice(node)
614         self.fstype = getText(node, 'fstype')
615         self.format = getText(node, 'autoformat', "no")
616         self.add_module('mds')
617         self.add_module('mds_%s' % (self.fstype))
618             
619     def prepare(self):
620         self.info(self.devname, self.fstype, self.format)
621         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
622         lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
623                     setup ="%s %s" %(blkdev, self.fstype))
624     def cleanup(self):
625         Module.cleanup(self)
626         clean_loop(self.devname)
627
628 class MDC(Module):
629     def __init__(self,node):
630         Module.__init__(self, 'MDC', node)
631         ref = node.getElementsByTagName('mds_ref')[0]
632         self.mds_uuid = ref.getAttribute('uuidref')
633         self.add_module('mdc')
634
635     def prepare(self):
636         self.info(self.mds_uuid)
637         mds = lookup(self.dom_node.parentNode, self.mds_uuid)
638         if mds == None:
639             panic(self.mdsuuid, "not found.")
640         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
641         srv = Network(net)
642         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
643         lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
644                         setup ="%s %s" %(self.mds_uuid, srv.uuid))
645             
646     def cleanup(self):
647         self.info(self.mds_uuid)
648         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
649         srv = Network(net)
650         try:
651             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
652             lctl.cleanup(self.name, self.uuid)
653         except CommandError:
654             print "cleanup failed: ", self.name
655
656 class OBD(Module):
657     def __init__(self, node):
658         Module.__init__(self, 'OBD', node)
659         self.obdtype = node.getAttribute('type')
660         self.devname, self.size = getDevice(node)
661         self.fstype = getText(node, 'fstype')
662         self.format = getText(node, 'autoformat', 'yes')
663         self.add_module(self.obdtype)
664
665     # need to check /proc/mounts and /etc/mtab before
666     # formatting anything.
667     # FIXME: check if device is already formatted.
668     def prepare(self):
669         self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
670         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
671         lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
672                     setup ="%s %s" %(blkdev, self.fstype))
673     def cleanup(self):
674         Module.cleanup(self)
675         clean_loop(self.devname)
676
677 class OST(Module):
678     def __init__(self,node):
679         Module.__init__(self, 'OST', node)
680         ref = node.getElementsByTagName('obd_ref')[0]
681         self.obd_uuid = ref.getAttribute('uuidref')
682         self.add_module('ost')
683
684     def prepare(self):
685         self.info(self.obd_uuid)
686         lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
687                     setup ="%s" % (self.obd_uuid))
688
689 class OSC(Module):
690     def __init__(self,node):
691         Module.__init__(self, 'OSC', node)
692         ref = node.getElementsByTagName('obd_ref')[0]
693         self.obd_uuid = ref.getAttribute('uuidref')
694         ref = node.getElementsByTagName('ost_ref')[0]
695         self.ost_uuid = ref.getAttribute('uuidref')
696         self.add_module('osc')
697
698     def prepare(self):
699         self.info(self.obd_uuid, self.ost_uuid)
700         net = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
701         srv = Network(net)
702         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
703         lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
704                     setup ="%s %s" %(self.obd_uuid, srv.uuid))
705
706     def cleanup(self):
707         self.info(self.obd_uuid, self.ost_uuid)
708         net_uuid = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
709         srv = Network(net_uuid)
710         try:
711             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
712             lctl.cleanup(self.name, self.uuid)
713         except CommandError:
714             print "cleanup failed: ", self.name
715
716 class Mountpoint(Module):
717     def __init__(self,node):
718         Module.__init__(self, 'MTPT', node)
719         self.path = getText(node, 'path')
720         ref = node.getElementsByTagName('mdc_ref')[0]
721         self.mdc_uuid = ref.getAttribute('uuidref')
722         ref = node.getElementsByTagName('osc_ref')[0]
723         self.lov_uuid = ref.getAttribute('uuidref')
724         self.add_module('osc')
725         self.add_module('llite')
726
727     def prepare(self):
728         l = lookup(self.dom_node.parentNode, self.lov_uuid)
729         if l.nodeName == 'lov':
730             lov = LOV(l)
731             for osc_uuid in string.split(lov.devlist):
732                 osc = lookup(self.dom_node.parentNode, osc_uuid)
733                 if osc:
734                     n = OSC(osc)
735                     n.prepare()
736                 else:
737                     panic('osc not found:', osc_uuid)
738             lctl.newdev(attach="lov %s %s" % (lov.name, lov.uuid),
739                         setup ="%s" % (self.mdc_uuid))
740         else:
741             osc = OSC(l)
742             osc.prepare()
743             
744         self.info(self.path, self.mdc_uuid,self.lov_uuid)
745         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
746               (self.lov_uuid, self.mdc_uuid, self.path)
747         run("mkdir", self.path)
748         ret, val = run(cmd)
749         if ret:
750             panic("mount failed:", self.path)
751     def cleanup(self):
752         self.info(self.path, self.mdc_uuid,self.lov_uuid)
753         run("umount", self.path)
754         l = lookup(self.dom_node.parentNode, self.lov_uuid)
755         if l.nodeName == 'lov':
756             lov = LOV(l)
757             for osc_uuid in string.split(lov.devlist):
758                 osc = lookup(self.dom_node.parentNode, osc_uuid)
759                 if osc:
760                     n = OSC(osc)
761                     n.cleanup()
762                 else:
763                     panic('osc not found:', osc_uuid)
764             lov.cleanup()
765         else:
766             osc = OSC(l)
767             osc.cleanup()
768             
769 # ============================================================
770 # XML processing and query
771 # TODO: Change query funcs to use XPath, which is muc cleaner
772
773 def getDevice(obd):
774     dev = obd.getElementsByTagName('device')[0]
775     dev.normalize();
776     try:
777         size = int(dev.getAttribute('size'))
778     except ValueError:
779         size = 0
780     return dev.firstChild.data, size
781
782 # Get the text content from the first matching child
783 def getText(node, tag, default=""):
784     list = node.getElementsByTagName(tag)
785     if len(list) > 0:
786         node = list[0]
787         node.normalize()
788         return node.firstChild.data
789     else:
790         return default
791
792 def get_ost_net(node, uuid):
793     ost = lookup(node, uuid)
794     list = ost.getElementsByTagName('network_ref')
795     if list:
796         uuid = list[0].getAttribute('uuidref')
797     else:
798         return None
799     return lookup(node, uuid)
800     
801 def lookup(node, uuid):
802     for n in node.childNodes:
803         if n.nodeType == n.ELEMENT_NODE:
804             if getUUID(n) == uuid:
805                 return n
806             else:
807                 n = lookup(n, uuid)
808                 if n: return n
809     return None
810             
811 # Get name attribute of node
812 def getName(node):
813     return node.getAttribute('name')
814
815 def getRef(node):
816     return node.getAttribute('uuidref')
817
818 # Get name attribute of node
819 def getUUID(node):
820     return node.getAttribute('uuid')
821
822 # the tag name is the service type
823 # fixme: this should do some checks to make sure the node is a service
824 def getServiceType(node):
825     return node.nodeName
826
827 #
828 # determine what "level" a particular node is at.
829 # the order of iniitailization is based on level.  objects
830 # are assigned a level based on type:
831 #  net,devices,ldlm:1, obd, mdd:2  mds,ost:3 osc,mdc:4 mounts:5
832 def getServiceLevel(node):
833     type = getServiceType(node)
834     if type in ('network',):
835         return 1
836     if type in ('device', 'ldlm'):
837         return 2
838     elif type in ('obd', 'mdd'):
839         return 3
840     elif type in ('mds','ost'):
841         return 4
842     elif type in ('mdc','osc'):
843         return 5
844     elif type in ('lov',):
845         return 6
846     elif type in ('mountpoint',):
847         return 7
848     return 0
849
850 #
851 # return list of services in a profile. list is a list of tuples
852 # [(level, node),]
853 def getServices(lustreNode, profileNode):
854     list = []
855     for n in profileNode.childNodes:
856         if n.nodeType == n.ELEMENT_NODE:
857             servNode = lookup(lustreNode, getRef(n))
858             if not servNode:
859                 print n
860                 panic('service not found: ' + getRef(n))
861             level = getServiceLevel(servNode)
862             list.append((level, servNode))
863     list.sort()
864     return list
865
866 def getByName(lustreNode, tag, name):
867     ndList = lustreNode.getElementsByTagName(tag)
868     for nd in ndList:
869         if getName(nd) == name:
870             return nd
871     return None
872     
873
874 # ============================================================
875 # lconf level logic
876 # Start a service.
877 def startService(node, clean_flag, module_flag):
878     type = getServiceType(node)
879     debug('Service:', type, getName(node), getUUID(node))
880     # there must be a more dynamic way of doing this...
881     n = None
882     if type == 'ldlm':
883         n = LDLM(node)
884     elif type == 'lov':
885         n = LOV(node)
886     elif type == 'network':
887         n = Network(node)
888     elif type == 'obd':
889         n = OBD(node)
890     elif type == 'ost':
891         n = OST(node)
892     elif type == 'mds':
893         n = MDS(node)
894     elif type == 'osc':
895         n = OSC(node)
896     elif type == 'mdc':
897         n = MDC(node)
898     elif type == 'mountpoint':
899         n = Mountpoint(node)
900     else:
901         panic ("unknown service type:", type)
902
903     if module_flag:
904         if config.nomod():
905             return
906         if clean_flag:
907             n.cleanup_module()
908         else:
909             n.load_module()
910     else:
911         if config.nosetup():
912             return
913         if clean_flag:
914             n.cleanup()
915         else:
916             n.prepare()
917
918 #
919 # Prepare the system to run lustre using a particular profile
920 # in a the configuration. 
921 #  * load & the modules
922 #  * setup networking for the current node
923 #  * make sure partitions are in place and prepared
924 #  * initialize devices with lctl
925 # Levels is important, and needs to be enforced.
926 def startProfile(lustreNode, profileNode, clean_flag, module_flag):
927     if not profileNode:
928         panic("profile:", profile, "not found.")
929     services = getServices(lustreNode, profileNode)
930     if clean_flag:
931         services.reverse()
932     for s in services:
933         startService(s[1], clean_flag, module_flag)
934
935 #
936 # Load profile for 
937 def doHost(lustreNode, hosts, clean_flag):
938     node = None
939     for h in hosts:
940         node = getByName(lustreNode, 'node', h)
941         if node:
942             break
943
944     if not node:
945         print 'No host entry found.'
946         return
947
948     # Two step process: (1) load modules, (2) setup lustre
949     # if not cleaning, load modules first.
950     module_flag = not clean_flag
951     reflist = node.getElementsByTagName('profile')
952     for profile in reflist:
953             startProfile(lustreNode,  profile, clean_flag, module_flag)
954
955     if not clean_flag:
956         setDebugPath()
957         script = config.gdb_script()
958         run(lctl.lctl, ' modules >', script)
959         if config.gdb():
960             # dump /tmp/ogdb and sleep/pause here
961             log ("The GDB module script is in", script)
962             time.sleep(5)
963             
964     module_flag = not module_flag
965     for profile in reflist:
966             startProfile(lustreNode,  profile, clean_flag, module_flag)
967
968 # Command line processing
969 #
970 def parse_cmdline(argv):
971     short_opts = "hdv"
972     long_opts = ["ldap", "reformat", "lustre=", "verbose", "gdb",
973                  "portals=", "makeldiff", "cleanup",
974                  "help", "debug", "node=", "get=", "nomod", "nosetup"]
975     opts = []
976     args = []
977     try:
978         opts, args = getopt.getopt(argv, short_opts, long_opts)
979     except getopt.GetoptError:
980         print "invalid opt"
981         usage()
982
983     for o, a in opts:
984         if o in ("-h", "--help"):
985             usage()
986         if o == "--cleanup":
987             config.cleanup(1)
988         if o in ("-v", "--verbose"):
989             config.verbose(1)
990         if o in ("-d", "--debug"):
991             config.noexec(1)
992             config.verbose(1)
993         if o == "--portals":
994             config.portals =  a
995         if o == "--lustre":
996             config.lustre  = a
997         if o  == "--reformat":
998             config.reformat(1)
999         if o  == "--node":
1000             config.node(a)
1001         if o  == "--get":
1002             config.url(a)
1003         if o  == "--gdb":
1004             config.gdb(1)
1005         if o  == "--nomod":
1006             config.nomod(1)
1007         if o  == "--nosetup":
1008             config.nosetup(1)
1009     return args
1010
1011 def fetch(url):
1012     import urllib
1013     data = ""
1014     try:
1015         s = urllib.urlopen(url)
1016         data = s.read()
1017     except:
1018         usage()
1019     return data
1020
1021 def setupModulePath(cmd):
1022     base = os.path.dirname(cmd)
1023     if os.access(base+"/Makefile", os.R_OK):
1024         config.src_dir(base + "/../../")
1025
1026 def setDebugPath():
1027     debug("debug path: ", config.debug_path())
1028     if config.noexec():
1029         return
1030     fp = open('/proc/sys/portals/debug_path', 'w')
1031     fp.write(config.debug_path())
1032     fp.close()
1033              
1034     
1035 def makeDevices():
1036     if not os.access('/dev/portals', os.R_OK):
1037         run('mknod /dev/portals c 10 240')
1038     if not os.access('/dev/obd', os.R_OK):
1039         run('mknod /dev/obd c 10 241')
1040
1041 # Initialize or shutdown lustre according to a configuration file
1042 #   * prepare the system for lustre
1043 #   * configure devices with lctl
1044 # Shutdown does steps in reverse
1045 #
1046 def main():
1047     global TCP_ACCEPTOR, lctl
1048     args = parse_cmdline(sys.argv[1:])
1049     if len(args) > 0:
1050         if not os.access(args[0], os.R_OK | os.W_OK):
1051             print 'File not found:', args[0]
1052             sys.exit(1)
1053         dom = xml.dom.minidom.parse(args[0])
1054     elif config.url():
1055         xmldata = fetch(config.url())
1056         dom = xml.dom.minidom.parseString(xmldata)
1057     else:
1058         usage()
1059
1060     node_list = []
1061     if config.node():
1062         node_list.append(config.node())
1063     else:
1064         host = socket.gethostname()
1065         if len(host) > 0:
1066             node_list.append(host)
1067         node_list.append('localhost')
1068     print "configuring for host: ", node_list
1069
1070     TCP_ACCEPTOR = find_prog('acceptor')
1071     if not TCP_ACCEPTOR:
1072         if config.noexec():
1073             TCP_ACCEPTOR = 'acceptor'
1074             debug('! acceptor not found')
1075         else:
1076             panic('acceptor not found')
1077
1078     lctl = LCTLInterface('lctl')
1079
1080     setupModulePath(sys.argv[0])
1081     makeDevices()
1082     doHost(dom.documentElement, node_list, config.cleanup())
1083
1084 if __name__ == "__main__":
1085     try:
1086         main()
1087     except RuntimeError:
1088         pass
1089     except CommandError:
1090         print 'FIXME: insert exception data here'
1091