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