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