Whamcloud - gitweb
* 1st attempt to prevent duplicate devices being started.
[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
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 --startlevel <num>  Specify the level of services to start with (default 0)
68 --endlevel <num>    Specify the level of services to end with (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._start_level = 0
110         self._end_level = 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 startlevel(self, val = None):
173         if val: self._start_level = int(val)
174         return self._start_level
175
176     def endlevel(self, val = None):
177         if val: self._end_level = int(val)
178         return self._end_level
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  == 'tcp':
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 == 'tcp':
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 == 'tcp':
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     out = lctl.device_list()
649     for s in out:
650         if uuid == string.split(s)[4]:
651             return 1
652     return 0
653     
654
655 # ============================================================
656 # Classes to prepare and cleanup the various objects
657 #
658 class Module:
659     """ Base class for the rest of the modules. The default cleanup method is
660     defined here, as well as some utilitiy funcs.
661     """
662     def __init__(self, module_name, dom_node):
663         self.dom_node = dom_node
664         self.module_name = module_name
665         self.name = get_attr(dom_node, 'name')
666         self.uuid = get_attr(dom_node, 'uuid')
667         self.kmodule_list = []
668         self._server = None
669         self._connected = 0
670         
671     def info(self, *args):
672         msg = string.join(map(str,args))
673         print self.module_name + ":", self.name, self.uuid, msg
674
675
676     def lookup_server(self, srv_uuid):
677         """ Lookup a server's network information """
678         net = get_ost_net(self.dom_node.parentNode, srv_uuid)
679         if not net:
680             panic ("Unable to find a server for:", srv_uuid)
681         self._server = Network(net)
682
683     def get_server(self):
684         return self._server
685
686     def cleanup(self):
687         """ default cleanup, used for most modules """
688         if not is_prepared(self.uuid):
689             return
690         self.info()
691         srv = self.get_server()
692         if srv and local_net(srv):
693             try:
694                 lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
695             except CommandError, e:
696                 log(self.module_name, "disconnect failed: ", self.name)
697                 e.dump()
698                 cleanup_error(e.rc)
699         try:
700             lctl.cleanup(self.name, self.uuid)
701         except CommandError, e:
702             log(self.module_name, "cleanup failed: ", self.name)
703             e.dump()
704             cleanup_error(e.rc)
705
706     def add_module(self, dev_dir, modname):
707         """Append a module to list of modules to load."""
708         self.kmodule_list.append((dev_dir, modname))
709
710     def mod_loaded(self, modname):
711         """Check if a module is already loaded. Look in /proc/modules for it."""
712         fp = open('/proc/modules')
713         lines = fp.readlines()
714         fp.close()
715         # please forgive my tired fingers for this one
716         ret = filter(lambda word, mod=modname: word == mod,
717                      map(lambda line: string.split(line)[0], lines))
718         return ret
719
720     def load_module(self):
721         """Load all the modules in the list in the order they appear."""
722         for dev_dir, mod in self.kmodule_list:
723             #  (rc, out) = run ('/sbin/lsmod | grep -s', mod)
724             if self.mod_loaded(mod) and not config.noexec():
725                 continue
726             log ('loading module:', mod)
727             if config.src_dir():
728                 module = find_module(config.src_dir(),dev_dir,  mod)
729                 if not module:
730                     panic('module not found:', mod)
731                 (rc, out)  = run('/sbin/insmod', module)
732                 if rc:
733                     raise CommandError('insmod', out, rc)
734             else:
735                 (rc, out) = run('/sbin/modprobe', mod)
736                 if rc:
737                     raise CommandError('modprobe', out, rc)
738             
739     def cleanup_module(self):
740         """Unload the modules in the list in reverse order."""
741         rev = self.kmodule_list
742         rev.reverse()
743         for dev_dir, mod in rev:
744             if not self.mod_loaded(mod):
745                 continue
746             # debug hack
747             if mod == 'portals' and config.dump_file():
748                 lctl.dump(config.dump_file())
749             log('unloading module:', mod)
750             if config.noexec():
751                 continue
752             (rc, out) = run('/sbin/rmmod', mod)
753             if rc:
754                 log('! unable to unload module:', mod)
755                 logall(out)
756         
757
758 class Network(Module):
759     def __init__(self,dom_node):
760         Module.__init__(self, 'NETWORK', dom_node)
761         self.net_type = get_attr(dom_node,'type')
762         self.nid = get_text(dom_node, 'server', '*')
763         self.port = get_text_int(dom_node, 'port', 0)
764         self.send_mem = get_text_int(dom_node, 'send_mem', DEFAULT_TCPBUF)
765         self.recv_mem = get_text_int(dom_node, 'recv_mem', DEFAULT_TCPBUF)
766         if '*' in self.nid:
767             self.nid = get_local_address(self.net_type, self.nid)
768             if not self.nid:
769                 panic("unable to set nid for", self.net_type, self.nid)
770             debug("nid:", self.nid)
771
772         self.add_module('portals/linux/oslib/', 'portals')
773         if node_needs_router():
774             self.add_module('portals/linux/router', 'kptlrouter')
775         if self.net_type == 'tcp':
776             self.add_module('portals/linux/socknal', 'ksocknal')
777         if self.net_type == 'toe':
778             self.add_module('portals/linux/toenal', 'ktoenal')
779         if self.net_type == 'elan':
780             self.add_module('portals/linux/rqswnal', 'kqswnal')
781         if self.net_type == 'gm':
782             self.add_module('portals/linux/gmnal', 'kgmnal')
783         self.add_module('lustre/obdclass', 'obdclass')
784         self.add_module('lustre/ptlrpc', 'ptlrpc')
785
786     def prepare(self):
787         self.info(self.net_type, self.nid, self.port)
788         if self.net_type in ('tcp', 'toe'):
789             nal_id = '' # default is socknal
790             if self.net_type == 'toe':
791                 nal_id = '-N 4'
792             ret, out = run(TCP_ACCEPTOR, '-s', self.send_mem, '-r', self.recv_mem, nal_id, self.port)
793             if ret:
794                 raise CommandError(TCP_ACCEPTOR, out, ret)
795         ret = self.dom_node.getElementsByTagName('route_tbl')
796         for a in ret:
797             for r in a.getElementsByTagName('route'):
798                 net_type = get_attr(r, 'type')
799                 gw = get_attr(r, 'gw')
800                 lo = get_attr(r, 'lo')
801                 hi = get_attr(r,'hi', '')
802                 lctl.add_route(net_type, gw, lo, hi)
803                 if net_type == 'tcp' and net_type == self.net_type and hi == '':
804                     srv = nid2server(self.dom_node.parentNode.parentNode, lo)
805                     if not srv:
806                         panic("no server for nid", lo)
807                     else:
808                         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
809
810             
811         lctl.network(self.net_type, self.nid)
812         lctl.newdev(attach = "ptlrpc RPCDEV RPCDEV_UUID")
813
814     def cleanup(self):
815         self.info(self.net_type, self.nid, self.port)
816         ret = self.dom_node.getElementsByTagName('route_tbl')
817         for a in ret:
818             for r in a.getElementsByTagName('route'):
819                 lo = get_attr(r, 'lo')
820                 hi = get_attr(r,'hi', '')
821                 if self.net_type == 'tcp' and hi == '':
822                     srv = nid2server(self.dom_node.parentNode.parentNode, lo)
823                     if not srv:
824                         panic("no server for nid", lo)
825                     else:
826                         try:
827                             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
828                         except CommandError, e:
829                             print "disconnect failed: ", self.name
830                             e.dump()
831                             cleanup_error(e.rc)
832                 try:
833                     lctl.del_route(self.net_type, self.nid, lo, hi)
834                 except CommandError, e:
835                     print "del_route failed: ", self.name
836                     e.dump()
837                     cleanup_error(e.rc)
838               
839         try:
840             lctl.cleanup("RPCDEV", "RPCDEV_UUID")
841         except CommandError, e:
842             print "cleanup failed: ", self.name
843             e.dump()
844             cleanup_error(e.rc)
845         try:
846             lctl.disconnectAll(self.net_type)
847         except CommandError, e:
848             print "disconnectAll failed: ", self.name
849             e.dump()
850             cleanup_error(e.rc)
851         if self.net_type == 'tcp':
852             # yikes, this ugly! need to save pid in /var/something
853             run("killall acceptor")
854
855 class LDLM(Module):
856     def __init__(self,dom_node):
857         Module.__init__(self, 'LDLM', dom_node)
858         self.add_module('lustre/ldlm', 'ldlm')
859     def prepare(self):
860         if is_prepared(self.uuid):
861             return
862         self.info()
863         lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
864                     setup ="")
865
866 class LOV(Module):
867     def __init__(self,dom_node):
868         Module.__init__(self, 'LOV', dom_node)
869         self.mds_uuid = get_first_ref(dom_node, 'mds')
870         mds= lookup(dom_node.parentNode, self.mds_uuid)
871         self.mds_name = getName(mds)
872         devs = dom_node.getElementsByTagName('devices')
873         if len(devs) > 0:
874             dev_node = devs[0]
875             self.stripe_sz = get_attr_int(dev_node, 'stripesize', 65536)
876             self.stripe_off = get_attr_int(dev_node, 'stripeoffset', 0)
877             self.pattern = get_attr_int(dev_node, 'pattern', 0)
878             self.devlist = get_all_refs(dev_node, 'osc')
879             self.stripe_cnt = get_attr_int(dev_node, 'stripecount', len(self.devlist))
880         self.add_module('lustre/mdc', 'mdc')
881         self.add_module('lustre/lov', 'lov')
882
883     def prepare(self):
884         if is_prepared(self.uuid):
885             return
886         for osc_uuid in self.devlist:
887             osc = lookup(self.dom_node.parentNode, osc_uuid)
888             if osc:
889                 n = OSC(osc)
890                 n.prepare()
891             else:
892                 panic('osc not found:', osc_uuid)
893         mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
894         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
895                   self.stripe_off, self.pattern, self.devlist, self.mds_name)
896         lctl.newdev(attach="lov %s %s" % (self.name, self.uuid),
897                     setup ="%s" % (mdc_uuid))
898
899     def cleanup(self):
900         if not is_prepared(self.uuid):
901             return
902         for osc_uuid in self.devlist:
903             osc = lookup(self.dom_node.parentNode, osc_uuid)
904             if osc:
905                 n = OSC(osc)
906                 n.cleanup()
907             else:
908                 panic('osc not found:', osc_uuid)
909         Module.cleanup(self)
910         cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
911
912
913     def load_module(self):
914         for osc_uuid in self.devlist:
915             osc = lookup(self.dom_node.parentNode, osc_uuid)
916             if osc:
917                 n = OSC(osc)
918                 n.load_module()
919                 break
920             else:
921                 panic('osc not found:', osc_uuid)
922         Module.load_module(self)
923
924
925     def cleanup_module(self):
926         Module.cleanup_module(self)
927         for osc_uuid in self.devlist:
928             osc = lookup(self.dom_node.parentNode, osc_uuid)
929             if osc:
930                 n = OSC(osc)
931                 n.cleanup_module()
932                 break
933             else:
934                 panic('osc not found:', osc_uuid)
935
936 class LOVConfig(Module):
937     def __init__(self,dom_node):
938         Module.__init__(self, 'LOVConfig', dom_node)
939         self.lov_uuid = get_first_ref(dom_node, 'lov')
940         l = lookup(dom_node.parentNode, self.lov_uuid)
941         self.lov = LOV(l)
942         
943     def prepare(self):
944         lov = self.lov
945         self.info(lov.mds_uuid, lov.stripe_cnt, lov.stripe_sz, lov.stripe_off,
946                   lov.pattern, lov.devlist, lov.mds_name)
947         lctl.lov_setconfig(lov.uuid, lov.mds_name, lov.stripe_cnt,
948                            lov.stripe_sz, lov.stripe_off, lov.pattern,
949                            string.join(lov.devlist))
950
951     def cleanup(self):
952         #nothing to do here
953         pass
954
955
956 class MDS(Module):
957     def __init__(self,dom_node):
958         Module.__init__(self, 'MDS', dom_node)
959         self.devname, self.size = get_device(dom_node)
960         self.fstype = get_text(dom_node, 'fstype')
961         # FIXME: if fstype not set, then determine based on kernel version
962         self.format = get_text(dom_node, 'autoformat', "no")
963         if self.fstype == 'extN':
964             self.add_module('lustre/extN', 'extN') 
965         self.add_module('lustre/mds', 'mds')
966         self.add_module('lustre/mds', 'mds_%s' % (self.fstype))
967             
968     def prepare(self):
969         if is_prepared(self.uuid):
970             return
971         self.info(self.devname, self.fstype, self.format)
972         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
973         if not is_prepared('MDT_UUID'):
974             lctl.newdev(attach="mdt %s %s" % ('MDT', 'MDT_UUID'),
975                         setup ="")
976         lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
977                     setup ="%s %s" %(blkdev, self.fstype))
978     def cleanup(self):
979         if is_prepared('MDT_UUID'):
980             try:
981                 lctl.cleanup("MDT", "MDT_UUID")
982             except CommandError, e:
983                 print "cleanup failed: ", self.name
984                 e.dump()
985                 cleanup_error(e.rc)
986         if not is_prepared(self.uuid):
987             return
988         Module.cleanup(self)
989         clean_loop(self.devname)
990
991 # Very unusual case, as there is no MDC element in the XML anymore
992 # Builds itself from an MDS node
993 class MDC(Module):
994     def __init__(self,dom_node):
995         self.mds = MDS(dom_node)
996         self.dom_node = dom_node
997         self.module_name = 'MDC'
998         self.kmodule_list = []
999         self._server = None
1000         self._connected = 0
1001
1002         host = socket.gethostname()
1003         self.name = 'MDC_%s_%s' % ( host, self.mds.name )
1004         self.uuid = self.name + '_UUID' 
1005
1006         self.lookup_server(self.mds.uuid)
1007         self.add_module('lustre/mdc', 'mdc')
1008
1009     def prepare(self):
1010         if is_prepared(self.uuid):
1011             return
1012         self.info(self.mds.uuid)
1013         srv = self.get_server()
1014         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1015         lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
1016                         setup ="%s %s" %(self.mds.uuid, srv.uuid))
1017             
1018 class OBD(Module):
1019     def __init__(self, dom_node):
1020         Module.__init__(self, 'OBD', dom_node)
1021         self.obdtype = get_attr(dom_node, 'type')
1022         self.devname, self.size = get_device(dom_node)
1023         self.fstype = get_text(dom_node, 'fstype')
1024         # FIXME: if fstype not set, then determine based on kernel version
1025         self.format = get_text(dom_node, 'autoformat', 'yes')
1026         if self.fstype == 'extN':
1027             self.add_module('lustre/extN', 'extN') 
1028         self.add_module('lustre/' + self.obdtype, self.obdtype)
1029
1030     # need to check /proc/mounts and /etc/mtab before
1031     # formatting anything.
1032     # FIXME: check if device is already formatted.
1033     def prepare(self):
1034         if is_prepared(self.uuid):
1035             return
1036         self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
1037         if self.obdtype == 'obdecho':
1038             blkdev = ''
1039         else:
1040             blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
1041         lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
1042                     setup ="%s %s" %(blkdev, self.fstype))
1043     def cleanup(self):
1044         if not is_prepared(self.uuid):
1045             return
1046         Module.cleanup(self)
1047         if not self.obdtype == 'obdecho':
1048             clean_loop(self.devname)
1049
1050 class OST(Module):
1051     def __init__(self,dom_node):
1052         Module.__init__(self, 'OST', dom_node)
1053         self.obd_uuid = get_first_ref(dom_node, 'obd')
1054         self.add_module('lustre/ost', 'ost')
1055
1056     def prepare(self):
1057         if is_prepared(self.uuid):
1058             return
1059         self.info(self.obd_uuid)
1060         lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
1061                     setup ="%s" % (self.obd_uuid))
1062
1063
1064 # virtual interface for  OSC and LOV
1065 class VOSC(Module):
1066     def __init__(self,dom_node):
1067         Module.__init__(self, 'VOSC', dom_node)
1068         if dom_node.nodeName == 'lov':
1069             self.osc = LOV(dom_node)
1070         else:
1071             self.osc = OSC(dom_node)
1072     def prepare(self):
1073         self.osc.prepare()
1074     def cleanup(self):
1075         self.osc.cleanup()
1076     def load_module(self):
1077         self.osc.load_module()
1078     def cleanup_module(self):
1079         self.osc.cleanup_module()
1080         
1081
1082 class OSC(Module):
1083     def __init__(self,dom_node):
1084         Module.__init__(self, 'OSC', dom_node)
1085         self.obd_uuid = get_first_ref(dom_node, 'obd')
1086         self.ost_uuid = get_first_ref(dom_node, 'ost')
1087         self.lookup_server(self.ost_uuid)
1088         self.add_module('lustre/osc', 'osc')
1089
1090     def prepare(self):
1091         if is_prepared(self.uuid):
1092             return
1093         self.info(self.obd_uuid, self.ost_uuid)
1094         srv = self.get_server()
1095         if local_net(srv):
1096             lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1097         else:
1098             r =  find_route(srv)
1099             if r:
1100                 lctl.add_route_host(r[0], srv.uuid, r[1], r[2])
1101             else:
1102                 panic ("no route to",  srv.nid)
1103             
1104         lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
1105                     setup ="%s %s" %(self.obd_uuid, srv.uuid))
1106
1107     def cleanup(self):
1108         if not is_prepared(self.uuid):
1109             return
1110         srv = self.get_server()
1111         if local_net(srv):
1112             Module.cleanup(self)
1113         else:
1114             self.info(self.obd_uuid, self.ost_uuid)
1115             r =  find_route(srv)
1116             if r:
1117                 try:
1118                     lctl.del_route_host(r[0], srv.uuid, r[1], r[2])
1119                 except CommandError, e:
1120                     print "del_route failed: ", self.name
1121                     e.dump()
1122                     cleanup_error(e.rc)
1123             Module.cleanup(self)
1124             
1125
1126 class Mountpoint(Module):
1127     def __init__(self,dom_node):
1128         Module.__init__(self, 'MTPT', dom_node)
1129         self.path = get_text(dom_node, 'path')
1130         self.mds_uuid = get_first_ref(dom_node, 'mds')
1131         self.lov_uuid = get_first_ref(dom_node, 'osc')
1132         self.add_module('lustre/mdc', 'mdc')
1133         self.add_module('lustre/llite', 'llite')
1134         l = lookup(self.dom_node.parentNode, self.lov_uuid)
1135         self.osc = VOSC(l)
1136
1137     def prepare(self):
1138         self.osc.prepare()
1139         mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
1140         self.info(self.path, self.mds_uuid, self.lov_uuid)
1141         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
1142               (self.lov_uuid, mdc_uuid, self.path)
1143         run("mkdir", self.path)
1144         ret, val = run(cmd)
1145         if ret:
1146             panic("mount failed:", self.path)
1147
1148     def cleanup(self):
1149         self.info(self.path, self.mds_uuid,self.lov_uuid)
1150         if config.force():
1151             (rc, out) = run("umount -f", self.path)
1152         else:
1153             (rc, out) = run("umount", self.path)
1154         if rc:
1155             log("umount failed, cleanup will most likely not work.")
1156         l = lookup(self.dom_node.parentNode, self.lov_uuid)
1157         self.osc.cleanup()
1158         cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
1159
1160     def load_module(self):
1161         self.osc.load_module()
1162         Module.load_module(self)
1163     def cleanup_module(self):
1164         Module.cleanup_module(self)
1165         self.osc.cleanup_module()
1166
1167
1168 # ============================================================
1169 # XML processing and query
1170 # TODO: Change query funcs to use XPath, which is muc cleaner
1171
1172 def get_device(obd):
1173     list = obd.getElementsByTagName('device')
1174     if len(list) > 0:
1175         dev = list[0]
1176         dev.normalize();
1177         size = get_attr_int(dev, 'size', 0)
1178         return dev.firstChild.data, size
1179     return '', 0
1180
1181 # Get the text content from the first matching child
1182 # If there is no content (or it is all whitespace), return
1183 # the default
1184 def get_text(dom_node, tag, default=""):
1185     list = dom_node.getElementsByTagName(tag)
1186     if len(list) > 0:
1187         dom_node = list[0]
1188         dom_node.normalize()
1189         if dom_node.firstChild:
1190             txt = string.strip(dom_node.firstChild.data)
1191             if txt:
1192                 return txt
1193     return default
1194
1195 def get_text_int(dom_node, tag, default=0):
1196     list = dom_node.getElementsByTagName(tag)
1197     n = default
1198     if len(list) > 0:
1199         dom_node = list[0]
1200         dom_node.normalize()
1201         if dom_node.firstChild:
1202             txt = string.strip(dom_node.firstChild.data)
1203             if txt:
1204                 try:
1205                     n = int(txt)
1206                 except ValueError:
1207                     panic("text value is not integer:", txt)
1208     return n
1209
1210 def get_attr(dom_node, attr, default=""):
1211     v = dom_node.getAttribute(attr)
1212     if v:
1213         return v
1214     return default
1215
1216 def get_attr_int(dom_node, attr, default=0):
1217     n = default
1218     v = dom_node.getAttribute(attr)
1219     if v:
1220         try:
1221             n = int(v)
1222         except ValueError:
1223             panic("attr value is not integer", v)
1224     return n
1225
1226 def get_first_ref(dom_node, tag):
1227     """ Get the first uuidref of the type TAG. Used one only
1228     one is expected.  Returns the uuid."""
1229     uuid = None
1230     refname = '%s_ref' % tag
1231     list = dom_node.getElementsByTagName(refname)
1232     if len(list) > 0:
1233         uuid = getRef(list[0])
1234     return uuid
1235     
1236 def get_all_refs(dom_node, tag):
1237     """ Get all the refs of type TAG.  Returns list of uuids. """
1238     uuids = []
1239     refname = '%s_ref' % tag
1240     list = dom_node.getElementsByTagName(refname)
1241     if len(list) > 0:
1242         for i in list:
1243             uuids.append(getRef(i))
1244     return uuids
1245
1246 def get_ost_net(dom_node, uuid):
1247     ost = lookup(dom_node, uuid)
1248     uuid = get_first_ref(ost, 'network')
1249     if not uuid:
1250         return None
1251     return lookup(dom_node, uuid)
1252
1253 def nid2server(dom_node, nid):
1254     netlist = dom_node.getElementsByTagName('network')
1255     for net_node in netlist:
1256         if get_text(net_node, 'server') == nid:
1257             return Network(net_node)
1258     return None
1259     
1260 def lookup(dom_node, uuid):
1261     for n in dom_node.childNodes:
1262         if n.nodeType == n.ELEMENT_NODE:
1263             if getUUID(n) == uuid:
1264                 return n
1265             else:
1266                 n = lookup(n, uuid)
1267                 if n: return n
1268     return None
1269             
1270 # Get name attribute of dom_node
1271 def getName(dom_node):
1272     return dom_node.getAttribute('name')
1273
1274 def getRef(dom_node):
1275     return dom_node.getAttribute('uuidref')
1276
1277 # Get name attribute of dom_node
1278 def getUUID(dom_node):
1279     return dom_node.getAttribute('uuid')
1280
1281 # the tag name is the service type
1282 # fixme: this should do some checks to make sure the dom_node is a service
1283 def getServiceType(dom_node):
1284     return dom_node.nodeName
1285
1286 #
1287 # determine what "level" a particular node is at.
1288 # the order of iniitailization is based on level. 
1289 def getServiceLevel(dom_node):
1290     type = getServiceType(dom_node)
1291     ret=0;
1292     if type in ('network',):
1293         ret = 10
1294     elif type in ('device', 'ldlm'):
1295         ret = 20
1296     elif type in ('obd', 'mdd'):
1297         ret = 30
1298     elif type in ('mds','ost'):
1299         ret = 40
1300     elif type in ('mdc','osc'):
1301         ret = 50
1302     elif type in ('lov', 'lovconfig'):
1303         ret = 60
1304     elif type in ('mountpoint',):
1305         ret = 70
1306
1307     if ret < config.startlevel() or ret > config.endlevel():
1308         ret = 0 
1309     return ret
1310
1311 #
1312 # return list of services in a profile. list is a list of tuples
1313 # [(level, dom_node),]
1314 def getServices(lustreNode, profileNode):
1315     list = []
1316     for n in profileNode.childNodes:
1317         if n.nodeType == n.ELEMENT_NODE:
1318             servNode = lookup(lustreNode, getRef(n))
1319             if not servNode:
1320                 print n
1321                 panic('service not found: ' + getRef(n))
1322             level = getServiceLevel(servNode)
1323             if level > 0:
1324                 list.append((level, servNode))
1325     list.sort()
1326     return list
1327
1328 def getByName(lustreNode, name, tag):
1329     ndList = lustreNode.getElementsByTagName(tag)
1330     for nd in ndList:
1331         if getName(nd) == name:
1332             return nd
1333     return None
1334     
1335
1336 ############################################################
1337 # MDC UUID hack - 
1338 # FIXME: clean this mess up!
1339 #
1340 saved_mdc = {}
1341 def prepare_mdc(dom_node, mds_uuid):
1342     global saved_mdc
1343     mds_node = lookup(dom_node, mds_uuid);
1344     if not mds_node:
1345         panic("no mds:", mds_uuid)
1346     if saved_mdc.has_key(mds_uuid):
1347         return saved_mdc[mds_uuid]
1348     mdc = MDC(mds_node)
1349     mdc.prepare()
1350     saved_mdc[mds_uuid] = mdc.uuid
1351     return mdc.uuid
1352
1353 def cleanup_mdc(dom_node, mds_uuid):
1354     global saved_mdc
1355     mds_node = lookup(dom_node, mds_uuid);
1356     if not mds_node:
1357         panic("no mds:", mds_uuid)
1358     if not saved_mdc.has_key(mds_uuid):
1359         mdc = MDC(mds_node)
1360         mdc.cleanup()
1361         saved_mdc[mds_uuid] = mdc.uuid
1362         
1363
1364 ############################################################
1365 # routing ("rooting")
1366 #
1367 routes = []
1368 local_node = []
1369 router_flag = 0
1370
1371 def init_node(dom_node):
1372     global local_node, router_flag
1373     netlist = dom_node.getElementsByTagName('network')
1374     for dom_net in netlist:
1375         type = get_attr(dom_net, 'type')
1376         gw = get_text(dom_net, 'server')
1377         local_node.append((type, gw))
1378
1379 def node_needs_router():
1380     return router_flag
1381
1382 def get_routes(type, gw, dom_net):
1383     """ Return the routes as a list of tuples of the form:
1384         [(type, gw, lo, hi),]"""
1385     res = []
1386     tbl = dom_net.getElementsByTagName('route_tbl')
1387     for t in tbl:
1388         routes = t.getElementsByTagName('route')
1389         for r in routes:
1390             lo = get_attr(r, 'lo')
1391             hi = get_attr(r, 'hi', '')
1392             res.append((type, gw, lo, hi))
1393     return res
1394     
1395
1396 def init_route_config(lustre):
1397     """ Scan the lustre config looking for routers.  Build list of
1398     routes. """
1399     global routes, router_flag
1400     routes = []
1401     list = lustre.getElementsByTagName('node')
1402     for node in list:
1403         if get_attr(node, 'router'):
1404             router_flag = 1
1405             for (local_type, local_nid) in local_node:
1406                 gw = None
1407                 netlist = node.getElementsByTagName('network')
1408                 for dom_net in netlist:
1409                     if local_type == get_attr(dom_net, 'type'):
1410                         gw = get_text(dom_net, 'server')
1411                         break
1412                 if not gw:
1413                     continue
1414                 for dom_net in netlist:
1415                     if local_type != get_attr(dom_net, 'type'):
1416                         for route in get_routes(local_type, gw, dom_net):
1417                             routes.append(route)
1418     
1419
1420 def local_net(net):
1421     global local_node
1422     for iface in local_node:
1423         if net.net_type == iface[0]:
1424             return 1
1425     return 0
1426
1427 def find_route(net):
1428     global local_node, routes
1429     frm_type = local_node[0][0]
1430     to_type = net.net_type
1431     to = net.nid
1432     debug ('looking for route to', to_type,to)
1433     for r in routes:
1434         if  r[2] == to:
1435             return r
1436     return None
1437            
1438     
1439         
1440
1441 ############################################################
1442 # lconf level logic
1443 # Start a service.
1444 def startService(dom_node, module_flag):
1445     type = getServiceType(dom_node)
1446     debug('Service:', type, getName(dom_node), getUUID(dom_node))
1447     # there must be a more dynamic way of doing this...
1448     n = None
1449     if type == 'ldlm':
1450         n = LDLM(dom_node)
1451     elif type == 'lov':
1452         n = LOV(dom_node)
1453     elif type == 'lovconfig':
1454         n = LOVConfig(dom_node)
1455     elif type == 'network':
1456         n = Network(dom_node)
1457     elif type == 'obd':
1458         n = OBD(dom_node)
1459     elif type == 'ost':
1460         n = OST(dom_node)
1461     elif type == 'mds':
1462         n = MDS(dom_node)
1463     elif type == 'osc':
1464         n = VOSC(dom_node)
1465     elif type == 'mdc':
1466         n = MDC(dom_node)
1467     elif type == 'mountpoint':
1468         n = Mountpoint(dom_node)
1469     else:
1470         panic ("unknown service type:", type)
1471
1472     if module_flag:
1473         if config.nomod():
1474             return
1475         if config.cleanup():
1476             n.cleanup_module()
1477         else:
1478             n.load_module()
1479     else:
1480         if config.nosetup():
1481             return
1482         if config.cleanup():
1483             n.cleanup()
1484         else:
1485             n.prepare()
1486
1487 #
1488 # Prepare the system to run lustre using a particular profile
1489 # in a the configuration. 
1490 #  * load & the modules
1491 #  * setup networking for the current node
1492 #  * make sure partitions are in place and prepared
1493 #  * initialize devices with lctl
1494 # Levels is important, and needs to be enforced.
1495 def startProfile(lustreNode, profileNode, module_flag):
1496     if not profileNode:
1497         panic("profile:", profile, "not found.")
1498     services = getServices(lustreNode, profileNode)
1499     if config.cleanup():
1500         services.reverse()
1501     for s in services:
1502         startService(s[1], module_flag)
1503
1504
1505 #
1506 # Load profile for 
1507 def doHost(lustreNode, hosts):
1508     global routes
1509     dom_node = None
1510     for h in hosts:
1511         dom_node = getByName(lustreNode, h, 'node')
1512         if dom_node:
1513             break
1514
1515     if not dom_node:
1516         print 'No host entry found.'
1517         return
1518
1519     if not get_attr(dom_node, 'router'):
1520         init_node(dom_node)
1521         init_route_config(lustreNode)
1522     else:
1523         global router_flag 
1524         router_flag = 1
1525
1526     # Two step process: (1) load modules, (2) setup lustre
1527     # if not cleaning, load modules first.
1528     module_flag = not config.cleanup()
1529     reflist = dom_node.getElementsByTagName('profile')
1530     for profile in reflist:
1531             startProfile(lustreNode,  profile, module_flag)
1532
1533     if not config.cleanup():
1534         sys_set_debug_path()
1535         script = config.gdb_script()
1536         run(lctl.lctl, ' modules >', script)
1537         if config.gdb():
1538             # dump /tmp/ogdb and sleep/pause here
1539             log ("The GDB module script is in", script)
1540             time.sleep(5)
1541             
1542     module_flag = not module_flag
1543     for profile in reflist:
1544             startProfile(lustreNode,  profile, module_flag)
1545
1546 ############################################################
1547 # Command line processing
1548 #
1549 def parse_cmdline(argv):
1550     short_opts = "hdnvf"
1551     long_opts = ["ldap", "reformat", "lustre=", "verbose", "gdb",
1552                  "portals=", "makeldiff", "cleanup", "noexec",
1553                  "help", "node=", "nomod", "nosetup",
1554                  "dump=", "force", "startlevel=", "endlevel="]
1555     opts = []
1556     args = []
1557     try:
1558         opts, args = getopt.getopt(argv, short_opts, long_opts)
1559     except getopt.error:
1560         print "invalid opt"
1561         usage()
1562
1563     for o, a in opts:
1564         if o in ("-h", "--help"):
1565             usage()
1566         if o in ("-d","--cleanup"):
1567             config.cleanup(1)
1568         if o in ("-v", "--verbose"):
1569             config.verbose(1)
1570         if o in ("-n", "--noexec"):
1571             config.noexec(1)
1572             config.verbose(1)
1573         if o == "--portals":
1574             config.portals = a
1575         if o == "--lustre":
1576             config.lustre = a
1577         if o == "--reformat":
1578             config.reformat(1)
1579         if o == "--node":
1580             config.node(a)
1581         if o == "--gdb":
1582             config.gdb(1)
1583         if o == "--nomod":
1584             config.nomod(1)
1585         if o == "--nosetup":
1586             config.nosetup(1)
1587         if o == "--dump":
1588             config.dump_file(a)
1589         if o in ("-f", "--force"):
1590             config.force(1)
1591         if o in ("--startlevel",):
1592                 config.startlevel(a)
1593         if o in ("--endlevel",):
1594                 config.endlevel(a)
1595
1596     return args
1597
1598 def fetch(url):
1599     import urllib
1600     data = ""
1601     try:
1602         s = urllib.urlopen(url)
1603         data = s.read()
1604     except:
1605         usage()
1606     return data
1607
1608 def setupModulePath(cmd):
1609     base = os.path.dirname(cmd)
1610     if os.access(base+"/Makefile", os.R_OK):
1611         config.src_dir(base + "/../../")
1612
1613 def sys_set_debug_path():
1614     debug("debug path: ", config.debug_path())
1615     if config.noexec():
1616         return
1617     try:
1618         fp = open('/proc/sys/portals/debug_path', 'w')
1619         fp.write(config.debug_path())
1620         fp.close()
1621     except IOError, e:
1622         print e
1623              
1624 #/proc/sys/net/core/rmem_max
1625 #/proc/sys/net/core/wmem_max
1626 def sys_set_netmem_max(path, max):
1627     debug("setting", path, "to at least", max)
1628     if config.noexec():
1629         return
1630     fp = open(path)
1631     str = fp.readline()
1632     fp.close
1633     cur = int(str)
1634     if max > cur:
1635         fp = open(path, 'w')
1636         fp.write('%d\n' %(max))
1637         fp.close()
1638     
1639     
1640 def sys_make_devices():
1641     if not os.access('/dev/portals', os.R_OK):
1642         run('mknod /dev/portals c 10 240')
1643     if not os.access('/dev/obd', os.R_OK):
1644         run('mknod /dev/obd c 10 241')
1645
1646
1647 # Add dir to the global PATH, if not already there.
1648 def add_to_path(new_dir):
1649     syspath = string.split(os.environ['PATH'], ':')
1650     if new_dir in syspath:
1651         return
1652     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
1653     
1654
1655 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
1656 # ensure basic elements are in the system path
1657 def sanitise_path():
1658     for dir in DEFAULT_PATH:
1659         add_to_path(dir)
1660
1661 # Initialize or shutdown lustre according to a configuration file
1662 #   * prepare the system for lustre
1663 #   * configure devices with lctl
1664 # Shutdown does steps in reverse
1665 #
1666 def main():
1667     global TCP_ACCEPTOR, lctl, MAXTCPBUF
1668     host = socket.gethostname()
1669
1670     sanitise_path()
1671
1672     args = parse_cmdline(sys.argv[1:])
1673     if len(args) > 0:
1674         if not os.access(args[0], os.R_OK):
1675             print 'File not found or readable:', args[0]
1676             sys.exit(1)
1677         dom = xml.dom.minidom.parse(args[0])
1678     elif config.url():
1679         xmldata = fetch(config.url())
1680         dom = xml.dom.minidom.parseString(xmldata)
1681     else:
1682         usage()
1683
1684     node_list = []
1685     if config.node():
1686         node_list.append(config.node())
1687     else:
1688         if len(host) > 0:
1689             node_list.append(host)
1690         node_list.append('localhost')
1691     debug("configuring for host: ", node_list)
1692
1693     if len(host) > 0:
1694         config._debug_path = config._debug_path + '-' + host
1695         config._gdb_script = config._gdb_script + '-' + host
1696
1697     TCP_ACCEPTOR = find_prog('acceptor')
1698     if not TCP_ACCEPTOR:
1699         if config.noexec():
1700             TCP_ACCEPTOR = 'acceptor'
1701             debug('! acceptor not found')
1702         else:
1703             panic('acceptor not found')
1704
1705     lctl = LCTLInterface('lctl')
1706
1707     setupModulePath(sys.argv[0])
1708     sys_make_devices()
1709     sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
1710     sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
1711     doHost(dom.documentElement, node_list)
1712
1713 if __name__ == "__main__":
1714     try:
1715         main()
1716     except LconfError, e:
1717         print e
1718     except CommandError, e:
1719         e.dump()
1720         sys.exit(e.rc)
1721
1722     if first_cleanup_error:
1723         sys.exit(first_cleanup_error)
1724