Whamcloud - gitweb
land b_md onto HEAD. the highlights:
[fs/lustre-release.git] / lustre / utils / lconf.in
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002 Cluster File Systems, Inc.
4 #   Author: Robert Read <rread@clusterfs.com>
5 #   This file is part of Lustre, http://www.lustre.org.
6 #
7 #   Lustre is free software; you can redistribute it and/or
8 #   modify it under the terms of version 2 of the GNU General Public
9 #   License as published by the Free Software Foundation.
10 #
11 #   Lustre is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with Lustre; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #
20 # lconf - lustre configuration tool
21 #
22 # lconf is the main driver script for starting and stopping
23 # lustre filesystem services.
24 #
25 # Based in part on the XML obdctl modifications done by Brian Behlendorf 
26
27 import sys, getopt
28 import string, os, stat, popen2, socket, time, random
29 import re, exceptions
30 import xml.dom.minidom
31
32 # Global parameters
33 TCP_ACCEPTOR = ''
34 MAXTCPBUF = 1048576
35 DEFAULT_TCPBUF = 1048576
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 PORTALS_DIR = '@PORTALSLOC@'
41
42 first_cleanup_error = 0
43 def cleanup_error(rc):
44     global first_cleanup_error
45     if not first_cleanup_error:
46         first_cleanup_error = rc
47
48
49 def usage():
50     print """usage: lconf config.xml
51
52 config.xml          Lustre configuration in xml format.
53 --get <url>         URL to fetch a config file
54 --node <nodename>   Load config for <nodename>
55 -d | --cleanup      Cleans up config. (Shutdown)
56 -f | --force        Forced unmounting and/or obd detach during cleanup
57 -v | --verbose      Print system commands as they are run
58 -h | --help         Print this help 
59 --gdb               Prints message after creating gdb module script
60                     and sleeps for 5 seconds.
61 -n | --noexec       Prints the commands and steps that will be run for a
62                     config without executing them. This can used to check if a
63                     config file is doing what it should be doing. (Implies -v)
64 --nomod             Skip load/unload module step.
65 --nosetup           Skip device setup/cleanup step.
66 --reformat          Reformat all devices (without question)
67 --dump <file>       Dump the kernel debug log before portals is unloaded
68 --minlevel <num>    Specify the minimum level of services to configure/cleanup (default 0)
69 --maxlevel <num>    Specify the maximum level of services to configure/cleanup (default 100)
70                     Levels are aproximatly like:
71                             10 - network
72                             20 - device, ldlm
73                             30 - obd, mdd
74                             40 - mds, ost
75                             50 - mdc, osc
76                             60 - lov, lovconfig
77                             70 - mountpoint, echo_client
78 """
79     TODO = """
80 --ldap server       LDAP server with lustre config database
81 --makeldiff         Translate xml source to LDIFF 
82 This are perhaps not needed:
83 --lustre="src dir"  Base directory of lustre sources. Used to search
84                     for modules.
85 --portals=src       Portals source 
86 """
87     sys.exit()
88
89 # ============================================================
90 # Config parameters, encapsulated in a class
91 class Config:
92     def __init__(self):
93         # flags
94         self._noexec = 0
95         self._verbose = 0
96         self._reformat = 0
97         self._cleanup = 0
98         self._gdb = 0
99         self._nomod = 0
100         self._nosetup = 0
101         self._force = 0
102         # parameters
103         self._modules = None
104         self._node = None
105         self._url = None
106         self._gdb_script = '/tmp/ogdb'
107         self._debug_path = '/tmp/lustre-log'
108         self._dump_file = None
109         self._src_dir = None
110         self._minlevel = 0
111         self._maxlevel = 100
112
113     def verbose(self, flag = None):
114         if flag: self._verbose = flag
115         return self._verbose
116
117     def noexec(self, flag = None):
118         if flag: self._noexec = flag
119         return self._noexec
120
121     def reformat(self, flag = None):
122         if flag: self._reformat = flag
123         return self._reformat
124
125     def cleanup(self, flag = None):
126         if flag: self._cleanup = flag
127         return self._cleanup
128
129     def gdb(self, flag = None):
130         if flag: self._gdb = flag
131         return self._gdb
132
133     def nomod(self, flag = None):
134         if flag: self._nomod = flag
135         return self._nomod
136
137     def nosetup(self, flag = None):
138         if flag: self._nosetup = flag
139         return self._nosetup
140
141     def force(self, flag = None):
142         if flag: self._force = flag
143         return self._force
144
145     def node(self, val = None):
146         if val: self._node = val
147         return self._node
148
149     def url(self, val = None):
150         if val: self._url = val
151         return self._url
152
153     def gdb_script(self):
154         if os.path.isdir('/r'):
155             return '/r' + self._gdb_script
156         else:
157             return self._gdb_script
158
159     def debug_path(self):
160         if os.path.isdir('/r'):
161             return '/r' + self._debug_path
162         else:
163             return self._debug_path
164
165     def src_dir(self, val = None):
166         if val: self._src_dir = val
167         return self._src_dir
168
169     def dump_file(self, val = None):
170         if val: self._dump_file = val
171         return self._dump_file
172
173     def minlevel(self, val = None):
174         if val: self._minlevel = int(val)
175         return self._minlevel
176
177     def maxlevel(self, val = None):
178         if val: self._maxlevel = int(val)
179         return self._maxlevel
180
181
182
183 config = Config()
184
185 # ============================================================ 
186 # debugging and error funcs
187
188 def fixme(msg = "this feature"):
189     raise LconfError, msg + ' not implmemented yet.'
190
191 def panic(*args):
192     msg = string.join(map(str,args))
193     if not config.noexec():
194         raise LconfError(msg)
195     else:
196         print "! " + msg
197
198 def log(*args):
199     msg = string.join(map(str,args))
200     print msg
201
202 def logall(msgs):
203     for s in msgs:
204         print string.strip(s)
205
206 def debug(*args):
207     if config.verbose():
208         msg = string.join(map(str,args))
209         print msg
210
211 # ============================================================
212 # locally defined exceptions
213 class CommandError (exceptions.Exception):
214     def __init__(self, cmd_name, cmd_err, rc=None):
215         self.cmd_name = cmd_name
216         self.cmd_err = cmd_err
217         self.rc = rc
218
219     def dump(self):
220         import types
221         if type(self.cmd_err) == types.StringType:
222             if self.rc:
223                 print "! %s (%d): %s" % (self.cmd_name, self.rc, self.cmd_err)
224             else:
225                 print "! %s: %s" % (self.cmd_name, self.cmd_err)
226         elif type(self.cmd_err) == types.ListType:
227             if self.rc:
228                 print "! %s (error %d):" % (self.cmd_name, self.rc)
229             else:
230                 print "! %s:" % (self.cmd_name)
231             for s in self.cmd_err:
232                 print "> %s" %(string.strip(s))
233         else:
234             print self.cmd_err
235
236 class LconfError (exceptions.Exception):
237     def __init__(self, args):
238         self.args = args
239
240
241 # ============================================================
242 # handle lctl interface
243 class LCTLInterface:
244     """
245     Manage communication with lctl
246     """
247
248     def __init__(self, cmd):
249         """
250         Initialize close by finding the lctl binary.
251         """
252         self.lctl = find_prog(cmd)
253         if not self.lctl:
254             if config.noexec():
255                 debug('! lctl not found')
256                 self.lctl = 'lctl'
257             else:
258                 raise CommandError('lctl', "unable to find lctl binary.")
259
260     def run(self, cmds):
261         """
262         run lctl
263         the cmds are written to stdin of lctl
264         lctl doesn't return errors when run in script mode, so
265         stderr is checked
266         should modify command line to accept multiple commands, or
267         create complex command line options
268         """
269         debug("+", self.lctl, cmds)
270         if config.noexec(): return (0, [])
271         p = popen2.Popen3(self.lctl, 1)
272         p.tochild.write(cmds + "\n")
273         p.tochild.close()
274         out = p.fromchild.readlines()
275         err = p.childerr.readlines()
276         ret = p.wait()
277         if os.WIFEXITED(ret):
278             rc = os.WEXITSTATUS(ret)
279         else:
280             rc = 0
281         if rc or len(err):
282             raise CommandError(self.lctl, err, rc)
283         return rc, out
284
285     def runcmd(self, *args):
286         """
287         run lctl using the command line
288         """
289         cmd = string.join(map(str,args))
290         debug("+", self.lctl, cmd)
291         rc, out = run(self.lctl, cmd)
292         if rc:
293             raise CommandError(self.lctl, out, rc)
294         return rc, out
295
296             
297     def network(self, net, nid):
298         """ initialized network and add "self" """
299         # Idea: "mynid" could be used for all network types to add "self," and then
300         # this special case would be gone and the "self" hack would be hidden.
301         if net  in ('tcp', 'toe'):
302             cmds =  """
303   network %s
304   mynid %s
305   add_uuid self %s
306   quit""" % (net, nid, nid)
307         else:
308             cmds =  """
309   network %s
310   add_uuid self %s
311   quit""" % (net, nid)
312             
313         self.run(cmds)
314
315     # create a new connection
316     def connect(self, net, nid, port, servuuid, send_mem, recv_mem):
317         if net  in ('tcp', 'toe'):
318             cmds =  """
319   network %s
320   add_uuid %s %s
321   send_mem %d
322   recv_mem %d
323   connect %s %d
324   quit""" % (net, servuuid, nid, send_mem, recv_mem, nid, port,  )
325         else:
326             cmds =  """
327   network %s
328   add_uuid %s %s
329   connect %s %d
330   quit""" % (net, servuuid, nid, nid, port,  )
331             
332         self.run(cmds)
333                 
334     # add a route to a range
335     def add_route(self, net, gw, lo, hi):
336         cmds =  """
337   network %s
338   add_route %s %s %s
339   quit  """ % (net, gw, lo, hi)
340         self.run(cmds)
341
342                 
343     def del_route(self, net, gw, lo, hi):
344         cmds =  """
345   ignore_errors
346   network %s
347   del_route %s
348   quit  """ % (net, lo)
349         self.run(cmds)
350
351     # add a route to a host
352     def add_route_host(self, net, uuid, gw, tgt):
353         cmds =  """
354   network %s
355   add_uuid %s %s
356   add_route %s %s
357   quit """ % (net, uuid, tgt, gw, tgt)
358         self.run(cmds)
359
360     # add a route to a range
361     def del_route_host(self, net, uuid, gw, tgt):
362         cmds =  """
363   ignore_errors
364   network %s
365   del_uuid %s
366   del_route %s
367   quit  """ % (net, uuid, tgt)
368         self.run(cmds)
369
370     # disconnect one connection
371     def disconnect(self, net, nid, port, servuuid):
372         cmds =  """
373   ignore_errors
374   network %s
375   disconnect %s 
376   del_uuid %s
377   quit""" % (net, nid, servuuid)
378         self.run(cmds)
379
380     # disconnect all
381     def disconnectAll(self, net):
382         cmds =  """
383   ignore_errors
384   network %s
385   del_uuid self
386   disconnect
387   quit""" % (net)
388         self.run(cmds)
389
390     # create a new device with lctl
391     def newdev(self, attach, setup = ""):
392         cmds = """
393   newdev
394   attach %s
395   setup %s
396   quit""" % (attach, setup)
397         self.run(cmds)
398
399     # cleanup a device
400     def cleanup(self, name, uuid):
401         cmds = """
402   ignore_errors
403   device $%s
404   cleanup
405   detach %s
406   quit""" % (name, ('', 'force')[config.force()])
407         self.run(cmds)
408
409     # create an lov
410     def lov_setconfig(self, uuid, mdsuuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist):
411         cmds = """
412   device $%s
413   probe
414   lov_setconfig %s %d %d %d %s %s
415   quit""" % (mdsuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
416         self.run(cmds)
417
418     # dump the log file
419     def dump(self, dump_file):
420         cmds = """
421   debug_kernel %s 1
422   quit""" % (dump_file)
423         self.run(cmds)
424
425     # get list of devices
426     def device_list(self):
427         rc, out = self.runcmd('device_list')
428         return out
429
430     # get lustre version
431     def lustre_version(self):
432         rc, out = self.runcmd('version')
433         return out
434
435 # ============================================================
436 # Various system-level functions
437 # (ideally moved to their own module)
438
439 # Run a command and return the output and status.
440 # stderr is sent to /dev/null, could use popen3 to
441 # save it if necessary
442 def run(*args):
443     cmd = string.join(map(str,args))
444     debug ("+", cmd)
445     if config.noexec(): return (0, [])
446     f = os.popen(cmd + ' 2>&1')
447     out = f.readlines()
448     ret = f.close()
449     if ret:
450         ret = ret >> 8
451     else:
452         ret = 0
453     return (ret, out)
454
455 # Run a command in the background.
456 def run_daemon(*args):
457     cmd = string.join(map(str,args))
458     debug ("+", cmd)
459     if config.noexec(): return 0
460     f = os.popen(cmd + ' 2>&1')
461     ret = f.close()
462     if ret:
463         ret = ret >> 8
464     else:
465         ret = 0
466     return ret
467
468 # Determine full path to use for an external command
469 # searches dirname(argv[0]) first, then PATH
470 def find_prog(cmd):
471     syspath = string.split(os.environ['PATH'], ':')
472     cmdpath = os.path.dirname(sys.argv[0])
473     syspath.insert(0, cmdpath);
474     syspath.insert(0, os.path.join(cmdpath, PORTALS_DIR+'/linux/utils/'))
475     for d in syspath:
476         prog = os.path.join(d,cmd)
477         debug(prog)
478         if os.access(prog, os.X_OK):
479             return prog
480     return ''
481
482 # Recursively look for file starting at base dir
483 def do_find_file(base, mod):
484     fullname = os.path.join(base, mod)
485     if os.access(fullname, os.R_OK):
486         return fullname
487     for d in os.listdir(base):
488         dir = os.path.join(base,d)
489         if os.path.isdir(dir):
490             module = do_find_file(dir, mod)
491             if module:
492                 return module
493
494 def find_module(dev_dir, modname):
495     mod = '%s.o' % (modname)
496
497     module = dev_dir +'/'+ mod
498     try: 
499        if os.access(module, os.R_OK):
500             return module
501     except OSError:
502         pass
503     return None
504
505 # is the path a block device?
506 def is_block(path):
507     s = ()
508     try:
509         s =  os.stat(path)
510     except OSError:
511         return 0
512     return stat.S_ISBLK(s[stat.ST_MODE])
513
514 # build fs according to type
515 # fixme: dangerous
516 def mkfs(fstype, dev):
517     if(fstype in ('ext3', 'extN')):
518         mkfs = 'mkfs.ext2 -j -b 4096'
519     elif (fstype == 'reiserfs'):
520         mkfs = 'mkfs.reiserfs -f'
521     else:
522         print 'unsupported fs type: ', fstype
523     if not is_block(dev):
524         if(fstype in ('ext3', 'extN')):
525             force = '-F'
526         elif (fstype == 'reiserfs'):
527             force = ''
528         else:
529             print 'unsupported fs type: ', fstype
530     else:
531         force = ''
532     (ret, out) = run (mkfs, force, dev)
533     if ret:
534         panic("Unable to build fs:", dev)
535     # enable hash tree indexing on fsswe
536     # FIXME: this check can probably go away on 2.5
537     if fstype == 'extN':
538         htree = 'echo "feature FEATURE_C5" | debugfs -w'
539         (ret, out) = run (htree, dev)
540         if ret:
541             panic("Unable to enable htree:", dev)
542
543 # some systems use /dev/loopN, some /dev/loop/N
544 def loop_base():
545     import re
546     loop = '/dev/loop'
547     if not os.access(loop + str(0), os.R_OK):
548         loop = loop + '/'
549         if not os.access(loop + str(0), os.R_OK):
550             panic ("can't access loop devices")
551     return loop
552     
553 # find loop device assigned to thefile
554 def find_loop(file):
555     loop = loop_base()
556     for n in xrange(0, MAX_LOOP_DEVICES):
557         dev = loop + str(n)
558         if os.access(dev, os.R_OK):
559             (stat, out) = run('losetup', dev)
560             if (out and stat == 0):
561                 m = re.search(r'\((.*)\)', out[0])
562                 if m and file == m.group(1):
563                     return dev
564         else:
565             break
566     return ''
567
568 # create file if necessary and assign the first free loop device
569 def init_loop(file, size, fstype):
570     dev = find_loop(file)
571     if dev:
572         print 'WARNING file:', file, 'already mapped to', dev
573         return dev
574     if config.reformat()  or not os.access(file, os.R_OK | os.W_OK):
575         run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,  file))
576     loop = loop_base()
577     # find next free loop
578     for n in xrange(0, MAX_LOOP_DEVICES):
579         dev = loop + str(n)
580         if os.access(dev, os.R_OK):
581             (stat, out) = run('losetup', dev)
582             if (stat):
583                 run('losetup', dev, file)
584                 return dev
585         else:
586             print "out of loop devices"
587             return ''
588     print "out of loop devices"
589     return ''
590
591 # undo loop assignment
592 def clean_loop(file):
593     dev = find_loop(file)
594     if dev:
595         ret, out = run('losetup -d', dev)
596         if ret:
597             log('unable to clean loop device:', dev, 'for file:', file)
598             logall(out)
599
600 # determine if dev is formatted as a <fstype> filesystem
601 def need_format(fstype, dev):
602     # FIXME don't know how to implement this    
603     return 0
604
605 # initialize a block device if needed
606 def block_dev(dev, size, fstype, format):
607     if config.noexec(): return dev
608     if not is_block(dev):
609         dev = init_loop(dev, size, fstype)
610     if config.reformat() or (need_format(fstype, dev) and format == 'yes'):
611         mkfs(fstype, dev)
612
613 #    else:
614 #        panic("device:", dev,
615 #              "not prepared, and autoformat is not set.\n",
616 #              "Rerun with --reformat option to format ALL filesystems")
617         
618     return dev
619
620 def if2addr(iface):
621     """lookup IP address for an interface"""
622     rc, out = run("/sbin/ifconfig", iface)
623     if rc or not out:
624        return None
625     addr = string.split(out[1])[1]
626     ip = string.split(addr, ':')[1]
627     return ip
628
629 def get_local_address(net_type, wildcard):
630     """Return the local address for the network type."""
631     local = ""
632     if net_type in ('tcp', 'toe'):
633         if  ':' in wildcard:
634             iface, star = string.split(wildcard, ':')
635             local = if2addr(iface)
636             if not local:
637                 panic ("unable to determine ip for:", wildcard)
638         else:
639             host = socket.gethostname()
640             local = socket.gethostbyname(host)
641     elif net_type == 'elan':
642         # awk '/NodeId/ { print $2 }' '/proc/elan/device0/position'
643         try:
644             fp = open('/proc/elan/device0/position', 'r')
645             lines = fp.readlines()
646             fp.close()
647             for l in lines:
648                 a = string.split(l)
649                 if a[0] == 'NodeId':
650                     local = a[1]
651                     break
652         except IOError, e:
653             log(e)
654     elif net_type == 'gm':
655         fixme("automatic local address for GM")
656     return local
657         
658
659 def is_prepared(uuid):
660     """Return true if a device exists for the uuid"""
661     # expect this format:
662     # 1 UP ldlm ldlm ldlm_UUID 2
663     try:
664         out = lctl.device_list()
665         for s in out:
666             if uuid == string.split(s)[4]:
667                 return 1
668     except CommandError, e:
669         e.dump()
670     return 0
671     
672
673 # ============================================================
674 # Classes to prepare and cleanup the various objects
675 #
676 class Module:
677     """ Base class for the rest of the modules. The default cleanup method is
678     defined here, as well as some utilitiy funcs.
679     """
680     def __init__(self, module_name, dom_node):
681         self.dom_node = dom_node
682         self.module_name = module_name
683         self.name = get_attr(dom_node, 'name')
684         self.uuid = get_attr(dom_node, 'uuid')
685         self.kmodule_list = []
686         self._server = None
687         self._connected = 0
688         
689     def info(self, *args):
690         msg = string.join(map(str,args))
691         print self.module_name + ":", self.name, self.uuid, msg
692
693
694     def lookup_server(self, srv_uuid):
695         """ Lookup a server's network information """
696         net = get_ost_net(self.dom_node.parentNode, srv_uuid)
697         if not net:
698             panic ("Unable to find a server for:", srv_uuid)
699         self._server = Network(net)
700
701     def get_server(self):
702         return self._server
703
704     def cleanup(self):
705         """ default cleanup, used for most modules """
706         self.info()
707         srv = self.get_server()
708         if srv and local_net(srv):
709             try:
710                 lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
711             except CommandError, e:
712                 log(self.module_name, "disconnect failed: ", self.name)
713                 e.dump()
714                 cleanup_error(e.rc)
715         try:
716             lctl.cleanup(self.name, self.uuid)
717         except CommandError, e:
718             log(self.module_name, "cleanup failed: ", self.name)
719             e.dump()
720             cleanup_error(e.rc)
721
722     def add_module(self, dev_dir, modname):
723         """Append a module to list of modules to load."""
724         self.kmodule_list.append((dev_dir, modname))
725
726     def mod_loaded(self, modname):
727         """Check if a module is already loaded. Look in /proc/modules for it."""
728         fp = open('/proc/modules')
729         lines = fp.readlines()
730         fp.close()
731         # please forgive my tired fingers for this one
732         ret = filter(lambda word, mod=modname: word == mod,
733                      map(lambda line: string.split(line)[0], lines))
734         return ret
735
736     def load_module(self):
737         """Load all the modules in the list in the order they appear."""
738         for dev_dir, mod in self.kmodule_list:
739             #  (rc, out) = run ('/sbin/lsmod | grep -s', mod)
740             if self.mod_loaded(mod) and not config.noexec():
741                 continue
742             log ('loading module:', mod)
743             if config.src_dir():
744                 module = find_module(dev_dir,  mod)
745                 if not module:
746                     panic('module not found:', mod)
747                 (rc, out)  = run('/sbin/insmod', module)
748                 if rc:
749                     raise CommandError('insmod', out, rc)
750             else:
751                 (rc, out) = run('/sbin/modprobe', mod)
752                 if rc:
753                     raise CommandError('modprobe', out, rc)
754             
755     def cleanup_module(self):
756         """Unload the modules in the list in reverse order."""
757         rev = self.kmodule_list
758         rev.reverse()
759         for dev_dir, mod in rev:
760             if not self.mod_loaded(mod):
761                 continue
762             # debug hack
763             if mod == 'portals' and config.dump_file():
764                 lctl.dump(config.dump_file())
765             log('unloading module:', mod)
766             if config.noexec():
767                 continue
768             (rc, out) = run('/sbin/rmmod', mod)
769             if rc:
770                 log('! unable to unload module:', mod)
771                 logall(out)
772         
773
774 class Network(Module):
775     def __init__(self,dom_node):
776         Module.__init__(self, 'NETWORK', dom_node)
777         self.net_type = get_attr(dom_node,'type')
778         self.nid = get_text(dom_node, 'server', '*')
779         self.port = get_text_int(dom_node, 'port', 0)
780         self.send_mem = get_text_int(dom_node, 'send_mem', DEFAULT_TCPBUF)
781         self.recv_mem = get_text_int(dom_node, 'recv_mem', DEFAULT_TCPBUF)
782         if '*' in self.nid:
783             self.nid = get_local_address(self.net_type, self.nid)
784             if not self.nid:
785                 panic("unable to set nid for", self.net_type, self.nid)
786             debug("nid:", self.nid)
787
788         self.add_module(PORTALS_DIR+"/linux/oslib", 'portals')
789         if node_needs_router():
790             self.add_module(PORTALS_DIR+"/linux/router", 'kptlrouter')
791         if self.net_type == 'tcp':
792             self.add_module(PORTALS_DIR+"/linux/socknal", 'ksocknal')
793         if self.net_type == 'toe':
794             self.add_module(PORTALS_DIR+"/linux/toenal", 'ktoenal')
795         if self.net_type == 'elan':
796             self.add_module(PORTALS_DIR+"/linux/rqswnal", 'kqswnal')
797         if self.net_type == 'gm':
798             self.add_module(PORTALS_DIR+"/linux/gmnal", 'kgmnal')
799         self.add_module(config.src_dir()+'obdclass', 'obdclass')
800         self.add_module(config.src_dir()+'ptlrpc', 'ptlrpc')
801
802     def prepare(self):
803         self.info(self.net_type, self.nid, self.port)
804         if self.net_type in ('tcp', 'toe'):
805             nal_id = '' # default is socknal
806             if self.net_type == 'toe':
807                 nal_id = '-N 4'
808             ret, out = run(TCP_ACCEPTOR, '-s', self.send_mem, '-r', self.recv_mem, nal_id, self.port)
809             if ret:
810                 raise CommandError(TCP_ACCEPTOR, out, ret)
811         ret = self.dom_node.getElementsByTagName('route_tbl')
812         for a in ret:
813             for r in a.getElementsByTagName('route'):
814                 net_type = get_attr(r, 'type')
815                 gw = get_attr(r, 'gw')
816                 lo = get_attr(r, 'lo')
817                 hi = get_attr(r,'hi', '')
818                 lctl.add_route(net_type, gw, lo, hi)
819                 if net_type in ('tcp', 'toe') and net_type == self.net_type and hi == '':
820                     srv = nid2server(self.dom_node.parentNode.parentNode, lo)
821                     if not srv:
822                         panic("no server for nid", lo)
823                     else:
824                         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
825
826             
827         lctl.network(self.net_type, self.nid)
828         lctl.newdev(attach = "ptlrpc RPCDEV RPCDEV_UUID")
829
830     def cleanup(self):
831         self.info(self.net_type, self.nid, self.port)
832         ret = self.dom_node.getElementsByTagName('route_tbl')
833         for a in ret:
834             for r in a.getElementsByTagName('route'):
835                 lo = get_attr(r, 'lo')
836                 hi = get_attr(r,'hi', '')
837                 if self.net_type in ('tcp', 'toe') and hi == '':
838                     srv = nid2server(self.dom_node.parentNode.parentNode, lo)
839                     if not srv:
840                         panic("no server for nid", lo)
841                     else:
842                         try:
843                             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
844                         except CommandError, e:
845                             print "disconnect failed: ", self.name
846                             e.dump()
847                             cleanup_error(e.rc)
848                 try:
849                     lctl.del_route(self.net_type, self.nid, lo, hi)
850                 except CommandError, e:
851                     print "del_route failed: ", self.name
852                     e.dump()
853                     cleanup_error(e.rc)
854               
855         try:
856             lctl.cleanup("RPCDEV", "RPCDEV_UUID")
857         except CommandError, e:
858             print "cleanup failed: ", self.name
859             e.dump()
860             cleanup_error(e.rc)
861         try:
862             lctl.disconnectAll(self.net_type)
863         except CommandError, e:
864             print "disconnectAll failed: ", self.name
865             e.dump()
866             cleanup_error(e.rc)
867         if self.net_type in ('tcp', 'toe'):
868             # yikes, this ugly! need to save pid in /var/something
869             run("killall acceptor")
870
871 class LDLM(Module):
872     def __init__(self,dom_node):
873         Module.__init__(self, 'LDLM', dom_node)
874         self.add_module(config.src_dir()+'ldlm', 'ldlm') 
875     def prepare(self):
876         if is_prepared(self.uuid):
877             return
878         self.info()
879         lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
880                     setup ="")
881
882 class LOV(Module):
883     def __init__(self,dom_node):
884         Module.__init__(self, 'LOV', dom_node)
885         self.mds_uuid = get_first_ref(dom_node, 'mds')
886         mds= lookup(dom_node.parentNode, self.mds_uuid)
887         self.mds_name = getName(mds)
888         devs = dom_node.getElementsByTagName('devices')
889         if len(devs) > 0:
890             dev_node = devs[0]
891             self.stripe_sz = get_attr_int(dev_node, 'stripesize', 65536)
892             self.stripe_off = get_attr_int(dev_node, 'stripeoffset', 0)
893             self.pattern = get_attr_int(dev_node, 'pattern', 0)
894             self.devlist = get_all_refs(dev_node, 'osc')
895             self.stripe_cnt = get_attr_int(dev_node, 'stripecount', len(self.devlist))
896         self.add_module(config.src_dir()+'mdc', 'mdc')
897         self.add_module(config.src_dir()+'lov', 'lov')
898
899     def prepare(self):
900         if is_prepared(self.uuid):
901             return
902         for osc_uuid in self.devlist:
903             osc = lookup(self.dom_node.parentNode, osc_uuid)
904             if osc:
905                 n = OSC(osc)
906                 try:
907                     # Ignore connection failures, because the LOV will DTRT with
908                     # an unconnected OSC.
909                     n.prepare(ignore_connect_failure=1)
910                 except CommandError:
911                     print "Error preparing OSC %s (inactive)\n" % osc_uuid
912             else:
913                 panic('osc not found:', osc_uuid)
914         mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
915         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
916                   self.stripe_off, self.pattern, self.devlist, self.mds_name)
917         lctl.newdev(attach="lov %s %s" % (self.name, self.uuid),
918                     setup ="%s" % (mdc_uuid))
919
920     def cleanup(self):
921         if not is_prepared(self.uuid):
922             return
923         for osc_uuid in self.devlist:
924             osc = lookup(self.dom_node.parentNode, osc_uuid)
925             if osc:
926                 n = OSC(osc)
927                 n.cleanup()
928             else:
929                 panic('osc not found:', osc_uuid)
930         Module.cleanup(self)
931         cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
932
933
934     def load_module(self):
935         for osc_uuid in self.devlist:
936             osc = lookup(self.dom_node.parentNode, osc_uuid)
937             if osc:
938                 n = OSC(osc)
939                 n.load_module()
940                 break
941             else:
942                 panic('osc not found:', osc_uuid)
943         Module.load_module(self)
944
945
946     def cleanup_module(self):
947         Module.cleanup_module(self)
948         for osc_uuid in self.devlist:
949             osc = lookup(self.dom_node.parentNode, osc_uuid)
950             if osc:
951                 n = OSC(osc)
952                 n.cleanup_module()
953                 break
954             else:
955                 panic('osc not found:', osc_uuid)
956
957 class LOVConfig(Module):
958     def __init__(self,dom_node):
959         Module.__init__(self, 'LOVConfig', dom_node)
960         self.lov_uuid = get_first_ref(dom_node, 'lov')
961         l = lookup(dom_node.parentNode, self.lov_uuid)
962         self.lov = LOV(l)
963         
964     def prepare(self):
965         lov = self.lov
966         self.info(lov.mds_uuid, lov.stripe_cnt, lov.stripe_sz, lov.stripe_off,
967                   lov.pattern, lov.devlist, lov.mds_name)
968         lctl.lov_setconfig(lov.uuid, lov.mds_name, lov.stripe_cnt,
969                            lov.stripe_sz, lov.stripe_off, lov.pattern,
970                            string.join(lov.devlist))
971
972     def cleanup(self):
973         #nothing to do here
974         pass
975
976
977 class MDS(Module):
978     def __init__(self,dom_node):
979         Module.__init__(self, 'MDS', dom_node)
980         self.devname, self.size = get_device(dom_node)
981         self.fstype = get_text(dom_node, 'fstype')
982         # FIXME: if fstype not set, then determine based on kernel version
983         self.format = get_text(dom_node, 'autoformat', "no")
984         if self.fstype == 'extN':
985             self.add_module(config.src_dir()+'extN', 'extN') 
986         self.add_module(config.src_dir()+'mds', 'mds')
987         self.add_module(config.src_dir()+'obdclass', 'fsfilt_%s'%(self.fstype))
988             
989     def prepare(self):
990         if is_prepared(self.uuid):
991             return
992         self.info(self.devname, self.fstype, self.format)
993         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
994         if not is_prepared('MDT_UUID'):
995             lctl.newdev(attach="mdt %s %s" % ('MDT', 'MDT_UUID'),
996                         setup ="")
997         lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
998                     setup ="%s %s" %(blkdev, self.fstype))
999     def cleanup(self):
1000         if is_prepared('MDT_UUID'):
1001             try:
1002                 lctl.cleanup("MDT", "MDT_UUID")
1003             except CommandError, e:
1004                 print "cleanup failed: ", self.name
1005                 e.dump()
1006                 cleanup_error(e.rc)
1007         if not is_prepared(self.uuid):
1008             return
1009         Module.cleanup(self)
1010         clean_loop(self.devname)
1011
1012 # Very unusual case, as there is no MDC element in the XML anymore
1013 # Builds itself from an MDS node
1014 class MDC(Module):
1015     def __init__(self,dom_node):
1016         self.mds = MDS(dom_node)
1017         self.dom_node = dom_node
1018         self.module_name = 'MDC'
1019         self.kmodule_list = []
1020         self._server = None
1021         self._connected = 0
1022
1023         host = socket.gethostname()
1024         self.name = 'MDC_%s' % (self.mds.name)
1025         self.uuid = '%s_%05x_%05x' % (self.name, int(random.random() * 1048576),
1026                                       int(random.random() * 1048576))
1027
1028         self.lookup_server(self.mds.uuid)
1029         self.add_module(config.src_dir()+'mdc', 'mdc')
1030
1031     def prepare(self):
1032         if is_prepared(self.uuid):
1033             return
1034         self.info(self.mds.uuid)
1035         srv = self.get_server()
1036         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1037         lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
1038                         setup ="%s %s" %(self.mds.uuid, srv.uuid))
1039             
1040 class OBD(Module):
1041     def __init__(self, dom_node):
1042         Module.__init__(self, 'OBD', dom_node)
1043         self.obdtype = get_attr(dom_node, 'type')
1044         self.devname, self.size = get_device(dom_node)
1045         self.fstype = get_text(dom_node, 'fstype')
1046         # FIXME: if fstype not set, then determine based on kernel version
1047         self.format = get_text(dom_node, 'autoformat', 'yes')
1048         if self.fstype == 'extN':
1049             self.add_module(config.src_dir()+'extN', 'extN') 
1050         self.add_module(config.src_dir()+'' + self.obdtype, self.obdtype)
1051         self.add_module(config.src_dir()+'obdclass' , 'fsfilt_%s' % (self.fstype))
1052
1053     # need to check /proc/mounts and /etc/mtab before
1054     # formatting anything.
1055     # FIXME: check if device is already formatted.
1056     def prepare(self):
1057         if is_prepared(self.uuid):
1058             return
1059         self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
1060         if self.obdtype == 'obdecho':
1061             blkdev = ''
1062         else:
1063             blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
1064         lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
1065                     setup ="%s %s" %(blkdev, self.fstype))
1066     def cleanup(self):
1067         if not is_prepared(self.uuid):
1068             return
1069         Module.cleanup(self)
1070         if not self.obdtype == 'obdecho':
1071             clean_loop(self.devname)
1072
1073 class OST(Module):
1074     def __init__(self,dom_node):
1075         Module.__init__(self, 'OST', dom_node)
1076         self.obd_uuid = get_first_ref(dom_node, 'obd')
1077         self.add_module(config.src_dir()+'ost', 'ost')
1078
1079     def prepare(self):
1080         if is_prepared(self.uuid):
1081             return
1082         self.info(self.obd_uuid)
1083         lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
1084                     setup ="%s" % (self.obd_uuid))
1085
1086
1087 # virtual interface for  OSC and LOV
1088 class VOSC(Module):
1089     def __init__(self,dom_node):
1090         Module.__init__(self, 'VOSC', dom_node)
1091         if dom_node.nodeName == 'lov':
1092             self.osc = LOV(dom_node)
1093         else:
1094             self.osc = OSC(dom_node)
1095     def prepare(self):
1096         self.osc.prepare()
1097     def cleanup(self):
1098         self.osc.cleanup()
1099     def load_module(self):
1100         self.osc.load_module()
1101     def cleanup_module(self):
1102         self.osc.cleanup_module()
1103         
1104
1105 class OSC(Module):
1106     def __init__(self,dom_node):
1107         Module.__init__(self, 'OSC', dom_node)
1108         self.obd_uuid = get_first_ref(dom_node, 'obd')
1109         self.ost_uuid = get_first_ref(dom_node, 'ost')
1110         self.lookup_server(self.ost_uuid)
1111         self.add_module(config.src_dir()+'osc', 'osc')
1112
1113     def prepare(self, ignore_connect_failure = 0):
1114         if is_prepared(self.uuid):
1115             return
1116         self.info(self.obd_uuid, self.ost_uuid)
1117         srv = self.get_server()
1118         try:
1119             if local_net(srv):
1120                 lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1121             else:
1122                 r =  find_route(srv)
1123                 if r:
1124                     lctl.add_route_host(r[0], srv.uuid, r[1], r[2])
1125                 else:
1126                     panic ("no route to",  srv.nid)
1127         except CommandError:
1128             if (ignore_connect_failure == 0):
1129                 pass
1130             
1131         lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
1132                     setup ="%s %s" %(self.obd_uuid, srv.uuid))
1133
1134     def cleanup(self):
1135         if not is_prepared(self.uuid):
1136             return
1137         srv = self.get_server()
1138         if local_net(srv):
1139             Module.cleanup(self)
1140         else:
1141             self.info(self.obd_uuid, self.ost_uuid)
1142             r =  find_route(srv)
1143             if r:
1144                 try:
1145                     lctl.del_route_host(r[0], srv.uuid, r[1], r[2])
1146                 except CommandError, e:
1147                     print "del_route failed: ", self.name
1148                     e.dump()
1149                     cleanup_error(e.rc)
1150             Module.cleanup(self)
1151             
1152
1153 class ECHO_CLIENT(Module):
1154     def __init__(self,dom_node):
1155         Module.__init__(self, 'ECHO_CLIENT', dom_node)
1156         self.add_module('lustre/obdecho', 'obdecho')
1157         self.lov_uuid = get_first_ref(dom_node, 'osc')
1158         l = lookup(self.dom_node.parentNode, self.lov_uuid)
1159         self.osc = VOSC(l)
1160
1161     def prepare(self):
1162         if is_prepared(self.uuid):
1163             return
1164         self.osc.prepare() # XXX This is so cheating. -p
1165         self.info(self.lov_uuid)
1166             
1167         lctl.newdev(attach="echo_client %s %s" % (self.name, self.uuid),
1168                     setup = self.lov_uuid)
1169
1170     def cleanup(self):
1171         if not is_prepared(self.uuid):
1172             return
1173         self.osc.cleanup()
1174
1175     def load_module(self):
1176         self.osc.load_module()
1177         Module.load_module(self)
1178     def cleanup_module(self):
1179         Module.cleanup_module(self)
1180         self.osc.cleanup_module()
1181
1182
1183 class Mountpoint(Module):
1184     def __init__(self,dom_node):
1185         Module.__init__(self, 'MTPT', dom_node)
1186         self.path = get_text(dom_node, 'path')
1187         self.mds_uuid = get_first_ref(dom_node, 'mds')
1188         self.lov_uuid = get_first_ref(dom_node, 'osc')
1189         self.add_module(config.src_dir()+'mdc', 'mdc')
1190         self.add_module(config.src_dir()+'llite', 'llite')
1191         l = lookup(self.dom_node.parentNode, self.lov_uuid)
1192         self.osc = VOSC(l)
1193
1194     def prepare(self):
1195         self.osc.prepare()
1196         mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
1197         self.info(self.path, self.mds_uuid, self.lov_uuid)
1198         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
1199               (self.lov_uuid, mdc_uuid, self.path)
1200         run("mkdir", self.path)
1201         ret, val = run(cmd)
1202         if ret:
1203             panic("mount failed:", self.path)
1204
1205     def cleanup(self):
1206         self.info(self.path, self.mds_uuid,self.lov_uuid)
1207         if config.force():
1208             (rc, out) = run("umount -f", self.path)
1209         else:
1210             (rc, out) = run("umount", self.path)
1211         if rc:
1212             log("umount failed, cleanup will most likely not work.")
1213         l = lookup(self.dom_node.parentNode, self.lov_uuid)
1214         self.osc.cleanup()
1215         cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
1216
1217     def load_module(self):
1218         self.osc.load_module()
1219         Module.load_module(self)
1220     def cleanup_module(self):
1221         Module.cleanup_module(self)
1222         self.osc.cleanup_module()
1223
1224
1225 # ============================================================
1226 # XML processing and query
1227 # TODO: Change query funcs to use XPath, which is muc cleaner
1228
1229 def get_device(obd):
1230     list = obd.getElementsByTagName('device')
1231     if len(list) > 0:
1232         dev = list[0]
1233         dev.normalize();
1234         size = get_attr_int(dev, 'size', 0)
1235         return dev.firstChild.data, size
1236     return '', 0
1237
1238 # Get the text content from the first matching child
1239 # If there is no content (or it is all whitespace), return
1240 # the default
1241 def get_text(dom_node, tag, default=""):
1242     list = dom_node.getElementsByTagName(tag)
1243     if len(list) > 0:
1244         dom_node = list[0]
1245         dom_node.normalize()
1246         if dom_node.firstChild:
1247             txt = string.strip(dom_node.firstChild.data)
1248             if txt:
1249                 return txt
1250     return default
1251
1252 def get_text_int(dom_node, tag, default=0):
1253     list = dom_node.getElementsByTagName(tag)
1254     n = default
1255     if len(list) > 0:
1256         dom_node = list[0]
1257         dom_node.normalize()
1258         if dom_node.firstChild:
1259             txt = string.strip(dom_node.firstChild.data)
1260             if txt:
1261                 try:
1262                     n = int(txt)
1263                 except ValueError:
1264                     panic("text value is not integer:", txt)
1265     return n
1266
1267 def get_attr(dom_node, attr, default=""):
1268     v = dom_node.getAttribute(attr)
1269     if v:
1270         return v
1271     return default
1272
1273 def get_attr_int(dom_node, attr, default=0):
1274     n = default
1275     v = dom_node.getAttribute(attr)
1276     if v:
1277         try:
1278             n = int(v)
1279         except ValueError:
1280             panic("attr value is not integer", v)
1281     return n
1282
1283 def get_first_ref(dom_node, tag):
1284     """ Get the first uuidref of the type TAG. Used one only
1285     one is expected.  Returns the uuid."""
1286     uuid = None
1287     refname = '%s_ref' % tag
1288     list = dom_node.getElementsByTagName(refname)
1289     if len(list) > 0:
1290         uuid = getRef(list[0])
1291     return uuid
1292     
1293 def get_all_refs(dom_node, tag):
1294     """ Get all the refs of type TAG.  Returns list of uuids. """
1295     uuids = []
1296     refname = '%s_ref' % tag
1297     list = dom_node.getElementsByTagName(refname)
1298     if len(list) > 0:
1299         for i in list:
1300             uuids.append(getRef(i))
1301     return uuids
1302
1303 def get_ost_net(dom_node, uuid):
1304     ost = lookup(dom_node, uuid)
1305     uuid = get_first_ref(ost, 'network')
1306     if not uuid:
1307         return None
1308     return lookup(dom_node, uuid)
1309
1310 def nid2server(dom_node, nid):
1311     netlist = dom_node.getElementsByTagName('network')
1312     for net_node in netlist:
1313         if get_text(net_node, 'server') == nid:
1314             return Network(net_node)
1315     return None
1316     
1317 def lookup(dom_node, uuid):
1318     for n in dom_node.childNodes:
1319         if n.nodeType == n.ELEMENT_NODE:
1320             if getUUID(n) == uuid:
1321                 return n
1322             else:
1323                 n = lookup(n, uuid)
1324                 if n: return n
1325     return None
1326             
1327 # Get name attribute of dom_node
1328 def getName(dom_node):
1329     return dom_node.getAttribute('name')
1330
1331 def getRef(dom_node):
1332     return dom_node.getAttribute('uuidref')
1333
1334 # Get name attribute of dom_node
1335 def getUUID(dom_node):
1336     return dom_node.getAttribute('uuid')
1337
1338 # the tag name is the service type
1339 # fixme: this should do some checks to make sure the dom_node is a service
1340 def getServiceType(dom_node):
1341     return dom_node.nodeName
1342
1343 #
1344 # determine what "level" a particular node is at.
1345 # the order of iniitailization is based on level. 
1346 def getServiceLevel(dom_node):
1347     type = getServiceType(dom_node)
1348     ret=0;
1349     if type in ('network',):
1350         ret = 10
1351     elif type in ('device', 'ldlm'):
1352         ret = 20
1353     elif type in ('obd', 'mdd'):
1354         ret = 30
1355     elif type in ('mds','ost'):
1356         ret = 40
1357     elif type in ('mdc','osc'):
1358         ret = 50
1359     elif type in ('lov', 'lovconfig'):
1360         ret = 60
1361     elif type in ('mountpoint', 'echo_client'):
1362         ret = 70
1363
1364     if ret < config.minlevel() or ret > config.maxlevel():
1365         ret = 0 
1366     return ret
1367
1368 #
1369 # return list of services in a profile. list is a list of tuples
1370 # [(level, dom_node),]
1371 def getServices(lustreNode, profileNode):
1372     list = []
1373     for n in profileNode.childNodes:
1374         if n.nodeType == n.ELEMENT_NODE:
1375             servNode = lookup(lustreNode, getRef(n))
1376             if not servNode:
1377                 print n
1378                 panic('service not found: ' + getRef(n))
1379             level = getServiceLevel(servNode)
1380             if level > 0:
1381                 list.append((level, servNode))
1382     list.sort()
1383     return list
1384
1385 def getByName(lustreNode, name, tag):
1386     ndList = lustreNode.getElementsByTagName(tag)
1387     for nd in ndList:
1388         if getName(nd) == name:
1389             return nd
1390     return None
1391     
1392
1393 ############################################################
1394 # MDC UUID hack - 
1395 # FIXME: clean this mess up!
1396 #
1397 saved_mdc = {}
1398 def prepare_mdc(dom_node, mds_uuid):
1399     global saved_mdc
1400     mds_node = lookup(dom_node, mds_uuid);
1401     if not mds_node:
1402         panic("no mds:", mds_uuid)
1403     if saved_mdc.has_key(mds_uuid):
1404         return saved_mdc[mds_uuid]
1405     mdc = MDC(mds_node)
1406     mdc.prepare()
1407     saved_mdc[mds_uuid] = mdc.uuid
1408     return mdc.uuid
1409
1410 def cleanup_mdc(dom_node, mds_uuid):
1411     global saved_mdc
1412     mds_node = lookup(dom_node, mds_uuid);
1413     if not mds_node:
1414         panic("no mds:", mds_uuid)
1415     if not saved_mdc.has_key(mds_uuid):
1416         mdc = MDC(mds_node)
1417         mdc.cleanup()
1418         saved_mdc[mds_uuid] = mdc.uuid
1419         
1420
1421 ############################################################
1422 # routing ("rooting")
1423 #
1424 routes = []
1425 local_node = []
1426 router_flag = 0
1427
1428 def init_node(dom_node):
1429     global local_node, router_flag
1430     netlist = dom_node.getElementsByTagName('network')
1431     for dom_net in netlist:
1432         type = get_attr(dom_net, 'type')
1433         gw = get_text(dom_net, 'server')
1434         local_node.append((type, gw))
1435
1436 def node_needs_router():
1437     return router_flag
1438
1439 def get_routes(type, gw, dom_net):
1440     """ Return the routes as a list of tuples of the form:
1441         [(type, gw, lo, hi),]"""
1442     res = []
1443     tbl = dom_net.getElementsByTagName('route_tbl')
1444     for t in tbl:
1445         routes = t.getElementsByTagName('route')
1446         for r in routes:
1447             lo = get_attr(r, 'lo')
1448             hi = get_attr(r, 'hi', '')
1449             res.append((type, gw, lo, hi))
1450     return res
1451     
1452
1453 def init_route_config(lustre):
1454     """ Scan the lustre config looking for routers.  Build list of
1455     routes. """
1456     global routes, router_flag
1457     routes = []
1458     list = lustre.getElementsByTagName('node')
1459     for node in list:
1460         if get_attr(node, 'router'):
1461             router_flag = 1
1462             for (local_type, local_nid) in local_node:
1463                 gw = None
1464                 netlist = node.getElementsByTagName('network')
1465                 for dom_net in netlist:
1466                     if local_type == get_attr(dom_net, 'type'):
1467                         gw = get_text(dom_net, 'server')
1468                         break
1469                 if not gw:
1470                     continue
1471                 for dom_net in netlist:
1472                     if local_type != get_attr(dom_net, 'type'):
1473                         for route in get_routes(local_type, gw, dom_net):
1474                             routes.append(route)
1475     
1476
1477 def local_net(net):
1478     global local_node
1479     for iface in local_node:
1480         if net.net_type == iface[0]:
1481             return 1
1482     return 0
1483
1484 def find_route(net):
1485     global local_node, routes
1486     frm_type = local_node[0][0]
1487     to_type = net.net_type
1488     to = net.nid
1489     debug ('looking for route to', to_type,to)
1490     for r in routes:
1491         if  r[2] == to:
1492             return r
1493     return None
1494            
1495     
1496         
1497
1498 ############################################################
1499 # lconf level logic
1500 # Start a service.
1501 def startService(dom_node, module_flag):
1502     type = getServiceType(dom_node)
1503     debug('Service:', type, getName(dom_node), getUUID(dom_node))
1504     # there must be a more dynamic way of doing this...
1505     n = None
1506     if type == 'ldlm':
1507         n = LDLM(dom_node)
1508     elif type == 'lov':
1509         n = LOV(dom_node)
1510     elif type == 'lovconfig':
1511         n = LOVConfig(dom_node)
1512     elif type == 'network':
1513         n = Network(dom_node)
1514     elif type == 'obd':
1515         n = OBD(dom_node)
1516     elif type == 'ost':
1517         n = OST(dom_node)
1518     elif type == 'mds':
1519         n = MDS(dom_node)
1520     elif type == 'osc':
1521         n = VOSC(dom_node)
1522     elif type == 'mdc':
1523         n = MDC(dom_node)
1524     elif type == 'mountpoint':
1525         n = Mountpoint(dom_node)
1526     elif type == 'echo_client':
1527         n = ECHO_CLIENT(dom_node)
1528     else:
1529         panic ("unknown service type:", type)
1530
1531     if module_flag:
1532         if config.nomod():
1533             return
1534         if config.cleanup():
1535             n.cleanup_module()
1536         else:
1537             n.load_module()
1538     else:
1539         if config.nosetup():
1540             return
1541         if config.cleanup():
1542             n.cleanup()
1543         else:
1544             n.prepare()
1545
1546 #
1547 # Prepare the system to run lustre using a particular profile
1548 # in a the configuration. 
1549 #  * load & the modules
1550 #  * setup networking for the current node
1551 #  * make sure partitions are in place and prepared
1552 #  * initialize devices with lctl
1553 # Levels is important, and needs to be enforced.
1554 def startProfile(lustreNode, profileNode, module_flag):
1555     if not profileNode:
1556         panic("profile:", profile, "not found.")
1557     services = getServices(lustreNode, profileNode)
1558     if config.cleanup():
1559         services.reverse()
1560     for s in services:
1561         startService(s[1], module_flag)
1562
1563
1564 #
1565 # Load profile for 
1566 def doHost(lustreNode, hosts):
1567     global routes
1568     dom_node = None
1569     for h in hosts:
1570         dom_node = getByName(lustreNode, h, 'node')
1571         if dom_node:
1572             break
1573     if not dom_node:
1574         print 'lconf: No host entry found in '+sys.argv[1]
1575         return
1576
1577     if not get_attr(dom_node, 'router'):
1578         init_node(dom_node)
1579         init_route_config(lustreNode)
1580     else:
1581         global router_flag 
1582         router_flag = 1
1583
1584     # Two step process: (1) load modules, (2) setup lustre
1585     # if not cleaning, load modules first.
1586     module_flag = not config.cleanup()
1587     reflist = dom_node.getElementsByTagName('profile')
1588     for profile in reflist:
1589             startProfile(lustreNode,  profile, module_flag)
1590
1591     if not config.cleanup():
1592         sys_set_debug_path()
1593         script = config.gdb_script()
1594         run(lctl.lctl, ' modules >', script)
1595         if config.gdb():
1596             # dump /tmp/ogdb and sleep/pause here
1597             log ("The GDB module script is in", script)
1598             time.sleep(5)
1599             
1600     module_flag = not module_flag
1601     for profile in reflist:
1602             startProfile(lustreNode,  profile, module_flag)
1603
1604 ############################################################
1605 # Command line processing
1606 #
1607 def parse_cmdline(argv):
1608     short_opts = "hdnvf"
1609     long_opts = ["ldap", "reformat", "lustre=", "verbose", "gdb",
1610                  "portals=", "makeldiff", "cleanup", "noexec",
1611                  "help", "node=", "nomod", "nosetup",
1612                  "dump=", "force", "minlevel=", "maxlevel="]
1613     opts = []
1614     args = []
1615     try:
1616         opts, args = getopt.getopt(argv, short_opts, long_opts)
1617     except getopt.error:
1618         print "invalid opt"
1619         usage()
1620
1621     for o, a in opts:
1622         if o in ("-h", "--help"):
1623             usage()
1624         if o in ("-d","--cleanup"):
1625             config.cleanup(1)
1626         if o in ("-v", "--verbose"):
1627             config.verbose(1)
1628         if o in ("-n", "--noexec"):
1629             config.noexec(1)
1630             config.verbose(1)
1631         if o == "--portals":
1632             config.portals = a
1633         if o == "--lustre":
1634             config.lustre = a
1635         if o == "--reformat":
1636             config.reformat(1)
1637         if o == "--node":
1638             config.node(a)
1639         if o == "--gdb":
1640             config.gdb(1)
1641         if o == "--nomod":
1642             config.nomod(1)
1643         if o == "--nosetup":
1644             config.nosetup(1)
1645         if o == "--dump":
1646             config.dump_file(a)
1647         if o in ("-f", "--force"):
1648             config.force(1)
1649         if o in ("--minlevel",):
1650                 config.minlevel(a)
1651         if o in ("--maxlevel",):
1652                 config.maxlevel(a)
1653
1654     return args
1655
1656 def fetch(url):
1657     import urllib
1658     data = ""
1659     try:
1660         s = urllib.urlopen(url)
1661         data = s.read()
1662     except:
1663         usage()
1664     return data
1665
1666 def setupModulePath(cmd):
1667     global PORTALS_DIR
1668     base = os.path.dirname(cmd)
1669     if os.access(base+"/Makefile", os.R_OK):
1670         config.src_dir(base + "/../")  
1671     if PORTALS_DIR[0] != '/':
1672         PORTALS_DIR= config.src_dir()+PORTALS_DIR
1673
1674 def sys_set_debug_path():
1675     debug("debug path: ", config.debug_path())
1676     if config.noexec():
1677         return
1678     try:
1679         fp = open('/proc/sys/portals/debug_path', 'w')
1680         fp.write(config.debug_path())
1681         fp.close()
1682     except IOError, e:
1683         print e
1684              
1685 #/proc/sys/net/core/rmem_max
1686 #/proc/sys/net/core/wmem_max
1687 def sys_set_netmem_max(path, max):
1688     debug("setting", path, "to at least", max)
1689     if config.noexec():
1690         return
1691     fp = open(path)
1692     str = fp.readline()
1693     fp.close
1694     cur = int(str)
1695     if max > cur:
1696         fp = open(path, 'w')
1697         fp.write('%d\n' %(max))
1698         fp.close()
1699     
1700     
1701 def sys_make_devices():
1702     if not os.access('/dev/portals', os.R_OK):
1703         run('mknod /dev/portals c 10 240')
1704     if not os.access('/dev/obd', os.R_OK):
1705         run('mknod /dev/obd c 10 241')
1706
1707
1708 # Add dir to the global PATH, if not already there.
1709 def add_to_path(new_dir):
1710     syspath = string.split(os.environ['PATH'], ':')
1711     if new_dir in syspath:
1712         return
1713     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
1714     
1715
1716 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
1717 # ensure basic elements are in the system path
1718 def sanitise_path():
1719     for dir in DEFAULT_PATH:
1720         add_to_path(dir)
1721
1722 # Initialize or shutdown lustre according to a configuration file
1723 #   * prepare the system for lustre
1724 #   * configure devices with lctl
1725 # Shutdown does steps in reverse
1726 #
1727 def main():
1728     global TCP_ACCEPTOR, lctl, MAXTCPBUF
1729     setupModulePath(sys.argv[0])
1730
1731     host = socket.gethostname()
1732
1733     # the PRNG is normally seeded with time(), which is not so good for starting
1734     # time-synchronized clusters
1735     input = open('/dev/urandom', 'r')
1736     if not input:
1737         print 'Unable to open /dev/urandom!'
1738         sys.exit(1)
1739     seed = input.read(32)
1740     input.close()
1741     random.seed(seed)
1742
1743     sanitise_path()
1744
1745     args = parse_cmdline(sys.argv[1:])
1746     if len(args) > 0:
1747         if not os.access(args[0], os.R_OK):
1748             print 'File not found or readable:', args[0]
1749             sys.exit(1)
1750         dom = xml.dom.minidom.parse(args[0])
1751     elif config.url():
1752         xmldata = fetch(config.url())
1753         dom = xml.dom.minidom.parseString(xmldata)
1754     else:
1755         usage()
1756
1757     node_list = []
1758     if config.node():
1759         node_list.append(config.node())
1760     else:
1761         if len(host) > 0:
1762             node_list.append(host)
1763         node_list.append('localhost')
1764     debug("configuring for host: ", node_list)
1765
1766     if len(host) > 0:
1767         config._debug_path = config._debug_path + '-' + host
1768         config._gdb_script = config._gdb_script + '-' + host
1769
1770     TCP_ACCEPTOR = find_prog('acceptor')
1771     if not TCP_ACCEPTOR:
1772         if config.noexec():
1773             TCP_ACCEPTOR = 'acceptor'
1774             debug('! acceptor not found')
1775         else:
1776             panic('acceptor not found')
1777
1778     lctl = LCTLInterface('lctl')
1779
1780     sys_make_devices()
1781     sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
1782     sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
1783     doHost(dom.documentElement, node_list)
1784
1785 if __name__ == "__main__":
1786     try:
1787         main()
1788     except LconfError, e:
1789         print e
1790     except CommandError, e:
1791         e.dump()
1792         sys.exit(e.rc)
1793
1794     if first_cleanup_error:
1795         sys.exit(first_cleanup_error)
1796