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