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