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