Whamcloud - gitweb
276a2b2c5f3eb112563f26f1e99b02b6b4904ef7
[fs/lustre-release.git] / lustre / utils / lconf
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002-2003 Cluster File Systems, Inc.
4 #   Authors: Robert Read <rread@clusterfs.com>
5 #            Mike Shaver <shaver@clusterfs.com>
6 #   This file is part of Lustre, http://www.lustre.org.
7 #
8 #   Lustre is free software; you can redistribute it and/or
9 #   modify it under the terms of version 2 of the GNU General Public
10 #   License as published by the Free Software Foundation.
11 #
12 #   Lustre is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with Lustre; if not, write to the Free Software
19 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #
21 # lconf - lustre configuration tool
22 #
23 # lconf is the main driver script for starting and stopping
24 # lustre filesystem services.
25 #
26 # Based in part on the XML obdctl modifications done by Brian Behlendorf 
27
28 import sys, getopt, types
29 import string, os, stat, popen2, socket, time, random, fcntl, select
30 import re, exceptions, signal, traceback
31 import xml.dom.minidom
32
33 if sys.version[0] == '1':
34     from FCNTL import F_GETFL, F_SETFL
35 else:
36     from fcntl import F_GETFL, F_SETFL
37
38 PYMOD_DIR = "/usr/lib/lustre/python"
39
40 def development_mode():
41     base = os.path.dirname(sys.argv[0])
42     if os.access(base+"/Makefile", os.R_OK):
43         return 1
44     return 0
45
46 if development_mode():
47     sys.path.append('../utils')
48 else:
49     sys.path.append(PYMOD_DIR)
50
51 import Lustre
52
53 # Global parameters
54 MAXTCPBUF = 16777216
55 DEFAULT_TCPBUF = 8388608
56 DEFAULT_PORT = 988
57 #
58 # Maximum number of devices to search for.
59 # (the /dev/loop* nodes need to be created beforehand)
60 MAX_LOOP_DEVICES = 256
61 PORTALS_DIR = 'portals'
62
63 # Needed to call lconf --record
64 CONFIG_FILE = "" 
65
66 # Please keep these in sync with the values in portals/kp30.h
67 ptldebug_names = { 
68     "trace" :     (1 << 0),
69     "inode" :     (1 << 1),
70     "super" :     (1 << 2),
71     "ext2" :      (1 << 3),
72     "malloc" :    (1 << 4),
73     "cache" :     (1 << 5),
74     "info" :      (1 << 6),
75     "ioctl" :     (1 << 7),
76     "blocks" :    (1 << 8),
77     "net" :       (1 << 9),
78     "warning" :   (1 << 10),
79     "buffs" :     (1 << 11),
80     "other" :     (1 << 12),
81     "dentry" :    (1 << 13),
82     "portals" :   (1 << 14),
83     "page" :      (1 << 15),
84     "dlmtrace" :  (1 << 16),
85     "error" :     (1 << 17),
86     "emerg" :     (1 << 18),
87     "ha" :        (1 << 19),
88     "rpctrace" :  (1 << 20),
89     "vfstrace" :  (1 << 21),
90     "reada" :     (1 << 22),
91     "mmap" :      (1 << 23),
92     "config" :    (1 << 24),
93     }
94
95 subsystem_names = {
96     "undefined" :    (1 << 0),
97     "mdc" :          (1 << 1),
98     "mds" :          (1 << 2),
99     "osc" :          (1 << 3),
100     "ost" :          (1 << 4),
101     "class" :        (1 << 5),
102     "log" :          (1 << 6),
103     "llite" :        (1 << 7),
104     "rpc" :          (1 << 8),
105     "mgmt" :         (1 << 9),
106     "portals" :      (1 << 10),
107     "socknal" :      (1 << 11),
108     "qswnal" :       (1 << 12),
109     "pinger" :       (1 << 13),
110     "filter" :       (1 << 14),
111     "ptlbd" :        (1 << 15),
112     "echo" :         (1 << 16),
113     "ldlm" :         (1 << 17),
114     "lov" :          (1 << 18),
115     "gmnal" :        (1 << 19),
116     "ptlrouter" :    (1 << 20),
117     "cobd" :         (1 << 21),
118     "ibnal" :        (1 << 22),
119     "sm" :           (1 << 23),
120     "asobd" :        (1 << 24),
121     "confobd" :      (1 << 25),
122     "lmv" :          (1 << 26),
123     "cmobd" :        (1 << 27),
124     "lonal" :        (1 << 28),
125     }
126
127
128 first_cleanup_error = 0
129 def cleanup_error(rc):
130     global first_cleanup_error
131     if not first_cleanup_error:
132         first_cleanup_error = rc
133
134 # ============================================================ 
135 # debugging and error funcs
136
137 def fixme(msg = "this feature"):
138     raise Lustre.LconfError, msg + ' not implemented yet.'
139
140 def panic(*args):
141     msg = string.join(map(str,args))
142     if not config.noexec:
143         raise Lustre.LconfError(msg)
144     else:
145         print "! " + msg
146
147 def log(*args):
148     msg = string.join(map(str,args))
149     print msg
150
151 def logall(msgs):
152     for s in msgs:
153         print string.strip(s)
154
155 def debug(*args):
156     if config.verbose:
157         msg = string.join(map(str,args))
158         print msg
159
160 # ack, python's builtin int() does not support '0x123' syntax.
161 # eval can do it, although what a hack!
162 def my_int(s):
163     try:
164         if s[0:2] == '0x':
165             return eval(s, {}, {})
166         else:
167             return int(s)
168     except SyntaxError, e:
169         raise ValueError("not a number")
170     except NameError, e:
171         raise ValueError("not a number")
172
173 # ============================================================
174 # locally defined exceptions
175 class CommandError (exceptions.Exception):
176     def __init__(self, cmd_name, cmd_err, rc=None):
177         self.cmd_name = cmd_name
178         self.cmd_err = cmd_err
179         self.rc = rc
180
181     def dump(self):
182         import types
183         if type(self.cmd_err) == types.StringType:
184             if self.rc:
185                 print "! %s (%d): %s" % (self.cmd_name, self.rc, self.cmd_err)
186             else:
187                 print "! %s: %s" % (self.cmd_name, self.cmd_err)
188         elif type(self.cmd_err) == types.ListType:
189             if self.rc:
190                 print "! %s (error %d):" % (self.cmd_name, self.rc)
191             else:
192                 print "! %s:" % (self.cmd_name)
193             for s in self.cmd_err:
194                 print "> %s" %(string.strip(s))
195         else:
196             print self.cmd_err
197
198
199 # ============================================================
200 # handle daemons, like the acceptor
201 class DaemonHandler:
202     """ Manage starting and stopping a daemon. Assumes daemon manages
203     it's own pid file. """
204
205     def __init__(self, cmd):
206         self.command = cmd
207         self.path =""
208
209     def start(self):
210         if self.running():
211             log(self.command, "already running.")
212         if not self.path:
213             self.path = find_prog(self.command)
214             if not self.path:
215                 panic(self.command, "not found.")
216         ret, out = runcmd(self.path +' '+ self.command_line())
217
218         # FIXME: add this check can only narrow the race but can not avoid it
219         # completely, so I don't apply this method on inserting module.
220         if ret and not self.running():
221             raise CommandError(self.path, out, ret)
222
223     def stop(self):
224         if self.running():
225             pid = self.read_pidfile()
226             try:
227                 log ("killing process", pid)
228                 os.kill(pid, 15)
229                 #time.sleep(1) # let daemon die
230             except OSError, e:
231                 log("unable to kill", self.command, e)
232             if self.running():
233                 log("unable to kill", self.command)
234
235     def running(self):
236         pid = self.read_pidfile()
237         if pid:
238             try:
239                 os.kill(pid, 0)
240             except OSError:
241                 self.clean_pidfile()
242             else:
243                 return 1
244         return 0
245
246     def read_pidfile(self):
247         try:
248             fp = open(self.pidfile(), 'r')
249             pid = int(fp.read())
250             fp.close()
251             return pid
252         except IOError:
253             return 0
254         except ValueError:
255             print "WARNING: invalid pid in %s, removed" % self.pidfile()
256             print "WARNING: You may need to stop acceptor by yourself and then unload the module libcfs"
257             os.unlink(self.pidfile())
258             return 0
259         
260     def clean_pidfile(self):
261         """ Remove a stale pidfile """
262         log("removing stale pidfile:", self.pidfile())
263         try:
264             os.unlink(self.pidfile())
265         except OSError, e:
266             log(self.pidfile(), e)
267             
268 class AcceptorHandler(DaemonHandler):
269     def __init__(self, port, net_type):
270         DaemonHandler.__init__(self, "acceptor")
271         self.port = port
272         self.flags = ''
273
274     def pidfile(self):
275         return "/var/run/%s-%d.pid" % (self.command, self.port)
276
277     def command_line(self):
278         return string.join(map(str,(self.flags, self.port)))
279     
280 acceptors = {}
281
282 # start the acceptors
283 def run_acceptors():
284     if config.lctl_dump or config.record:
285         return
286     for port in acceptors.keys():
287         daemon = acceptors[port]
288         if not daemon.running():
289             daemon.start()
290
291 def run_one_acceptor(port):
292     if config.lctl_dump or config.record:
293         return
294     if acceptors.has_key(port):
295         daemon = acceptors[port]
296         if not daemon.running():
297             daemon.start()
298     else:
299          panic("run_one_acceptor: No acceptor defined for port:", port)   
300         
301 def stop_acceptor(port):
302     if acceptors.has_key(port):
303         daemon = acceptors[port]
304         if daemon.running():
305             daemon.stop()
306         
307
308 # ============================================================
309 # handle lctl interface
310 class LCTLInterface:
311     """
312     Manage communication with lctl
313     """
314
315     def __init__(self, cmd):
316         """
317         Initialize close by finding the lctl binary.
318         """
319         self.lctl = find_prog(cmd)
320         self.save_file = ''
321         self.record_device = ''
322         if not self.lctl:
323             if config.noexec:
324                 debug('! lctl not found')
325                 self.lctl = 'lctl'
326             else:
327                 raise CommandError('lctl', "unable to find lctl binary.")
328
329     def use_save_file(self, file):
330         self.save_file = file
331         
332     def record(self, dev_name, logname):
333         log("Recording log", logname, "on", dev_name)
334         self.record_device = dev_name
335         self.record_log = logname
336
337     def end_record(self):
338         log("End recording log", self.record_log, "on", self.record_device)
339         self.record_device = None
340         self.record_log = None
341
342     def set_nonblock(self, fd):
343         fl = fcntl.fcntl(fd, F_GETFL)
344         fcntl.fcntl(fd, F_SETFL, fl | os.O_NDELAY)
345
346     def run(self, cmds):
347         """
348         run lctl
349         the cmds are written to stdin of lctl
350         lctl doesn't return errors when run in script mode, so
351         stderr is checked
352         should modify command line to accept multiple commands, or
353         create complex command line options
354         """
355         cmd_line = self.lctl
356         if self.save_file:
357             cmds = '\n  dump ' + self.save_file + '\n' + cmds
358         elif self.record_device:
359             cmds = """
360     device $%s
361     record %s
362     %s""" % (self.record_device, self.record_log, cmds)
363             
364         debug("+", cmd_line, cmds)
365         if config.noexec: return (0, [])
366
367         child = popen2.Popen3(cmd_line, 1) # Capture stdout and stderr from command
368         child.tochild.write(cmds + "\n")
369         child.tochild.close()
370
371         # From "Python Cookbook" from O'Reilly
372         outfile = child.fromchild
373         outfd = outfile.fileno()
374         self.set_nonblock(outfd)
375         errfile = child.childerr
376         errfd = errfile.fileno()
377         self.set_nonblock(errfd)
378
379         outdata = errdata = ''
380         outeof = erreof = 0
381         while 1:
382             ready = select.select([outfd,errfd],[],[]) # Wait for input
383             if outfd in ready[0]:
384                 outchunk = outfile.read()
385                 if outchunk == '': outeof = 1
386                 outdata = outdata + outchunk
387             if errfd in ready[0]:
388                 errchunk = errfile.read()
389                 if errchunk == '': erreof = 1
390                 errdata = errdata + errchunk
391             if outeof and erreof: break
392         # end of "borrowed" code
393
394         ret = child.wait()
395         if os.WIFEXITED(ret):
396             rc = os.WEXITSTATUS(ret)
397         else:
398             rc = 0
399         if rc or len(errdata):
400             raise CommandError(self.lctl, errdata, rc)
401         return rc, outdata
402
403     def runcmd(self, *args):
404         """
405         run lctl using the command line
406         """
407         cmd = string.join(map(str,args))
408         debug("+", self.lctl, cmd)
409         rc, out = run(self.lctl, cmd)
410         if rc:
411             raise CommandError(self.lctl, out, rc)
412         return rc, out
413
414             
415     def clear_log(self, dev, log):
416         """ clear an existing log """
417         cmds =  """
418   device $%s
419   probe
420   clear_log %s
421   quit """ % (dev, log)
422         self.run(cmds)
423
424     def network(self, net, nid):
425         """ set mynid """
426         cmds =  """
427   network %s
428   mynid %s
429   quit """ % (net, nid)
430         self.run(cmds)
431
432     # add an interface
433     def add_interface(self, net, ip, netmask = ""):
434         """ add an interface """
435         cmds = """
436   network %s
437   add_interface %s %s
438   quit """ % (net, ip, netmask)
439         self.run(cmds)
440
441     # delete an interface
442     def del_interface(self, net, ip):
443         """ delete an interface """
444         cmds = """
445   network %s
446   del_interface %s
447   quit """ % (net, ip)
448         self.run(cmds)
449
450     # create a new connection
451     def add_uuid(self, net_type, uuid, nid):
452         cmds = "\n  add_uuid %s %s %s" %(uuid, nid, net_type)
453         self.run(cmds)
454
455     def add_peer(self, net_type, nid, hostaddr, port):
456         if net_type  in ('tcp',) and not config.lctl_dump:
457             cmds =  """
458   network %s
459   add_peer %s %s %d
460   quit""" % (net_type,
461              nid, hostaddr, port )
462             self.run(cmds)
463         elif net_type in ('openib','iib',) and not config.lctl_dump:
464             cmds =  """
465   network %s
466   add_peer %s
467   quit""" % (net_type,
468              nid )
469             self.run(cmds)
470     
471     def connect(self, srv):
472         self.add_uuid(srv.net_type, srv.nid_uuid, srv.nid)
473         if srv.net_type  in ('tcp','openib','iib',) and not config.lctl_dump:
474             if srv.hostaddr[0]:
475                 hostaddr = string.split(srv.hostaddr[0], '/')[0]
476             self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port)
477
478     # Recover a device
479     def recover(self, dev_name, new_conn):
480         cmds = """
481     device $%s
482     recover %s""" %(dev_name, new_conn)
483         self.run(cmds)
484                 
485     # add a route to a range
486     def add_route(self, net, gw, lo, hi):
487         cmds =  """
488   network %s
489   add_route %s %s %s
490   quit  """ % (net,
491                gw, lo, hi)
492         try:
493             self.run(cmds)
494         except CommandError, e:
495             log ("ignore: ")
496             e.dump()
497                 
498     def del_route(self, net, gw, lo, hi):
499         cmds =  """
500   ignore_errors
501   network %s
502   del_route %s %s %s
503   quit  """ % (net, gw, lo, hi)
504         self.run(cmds)
505
506     # add a route to a host
507     def add_route_host(self, net, uuid, gw, tgt):
508         self.add_uuid(net, uuid, tgt)
509         cmds =  """
510   network %s
511   add_route %s %s
512   quit """ % (net,
513               gw, tgt)
514         try:
515             self.run(cmds)
516         except CommandError, e:
517             log ("ignore: ")
518             e.dump()
519
520     # add a route to a range
521     def del_route_host(self, net, uuid, gw, tgt):
522         self.del_uuid(uuid)
523         cmds =  """
524   ignore_errors
525   network %s
526   del_route %s %s
527   quit  """ % (net, gw, tgt)
528         self.run(cmds)
529
530
531     def del_peer(self, net_type, nid, hostaddr):
532         if net_type  in ('tcp',) and not config.lctl_dump:
533                 cmds =  """
534   ignore_errors
535   network %s
536   del_peer %s %s single_share
537   quit""" % (net_type,
538              nid, hostaddr)
539                 self.run(cmds)
540         elif net_type  in ('openib','iib',) and not config.lctl_dump:
541                 cmds =  """
542   ignore_errors
543   network %s
544   del_peer %s single_share
545   quit""" % (net_type,
546              nid)
547                 self.run(cmds)
548         
549     # disconnect one connection
550     def disconnect(self, srv):
551         self.del_uuid(srv.nid_uuid)
552         if srv.net_type  in ('tcp','openib','iib',) and not config.lctl_dump:
553             if srv.hostaddr[0]:
554                 hostaddr = string.split(srv.hostaddr[0], '/')[0]
555             self.del_peer(srv.net_type, srv.nid, hostaddr)
556
557     def del_uuid(self, uuid):
558         cmds =  """
559   ignore_errors
560   del_uuid %s
561   quit""" % (uuid,)
562         self.run(cmds)
563
564     # disconnect all
565     def disconnectAll(self, net):
566         cmds =  """
567   ignore_errors
568   network %s
569   disconnect
570   quit""" % (net)
571         self.run(cmds)
572
573     def attach(self, type, name, uuid):
574         cmds = """
575   attach %s %s %s
576   quit""" % (type, name, uuid)
577         self.run(cmds)
578         
579     def setup(self, name, setup = ""):
580         cmds = """
581   cfg_device %s
582   setup %s
583   quit""" % (name, setup)
584         self.run(cmds)
585         
586
587     # create a new device with lctl
588     def newdev(self, type, name, uuid, setup = ""):
589         self.attach(type, name, uuid);
590         try:
591             self.setup(name, setup)
592         except CommandError, e:
593             self.cleanup(name, uuid, 0)
594             raise e
595         
596
597     # cleanup a device
598     def cleanup(self, name, uuid, force, failover = 0):
599         if failover: force = 1
600         cmds = """
601   ignore_errors
602   cfg_device $%s
603   cleanup %s %s
604   detach
605   quit""" % (name, ('', 'force')[force],
606              ('', 'failover')[failover])
607         self.run(cmds)
608
609     # create an lov
610     def lov_setup(self, name, uuid, desc_uuid, mdsuuid, stripe_cnt,
611                   stripe_sz, stripe_off,
612                       pattern, devlist):
613         cmds = """
614   attach lov %s %s
615   lov_setup %s %d %d %d %s %s
616   quit""" % (name, uuid, desc_uuid, stripe_cnt, stripe_sz, stripe_off,
617              pattern, devlist)
618         self.run(cmds)
619
620     # create an lov
621     def lov_setconfig(self, uuid, mdsuuid, stripe_cnt, stripe_sz, stripe_off,
622                       pattern, devlist):
623         cmds = """
624   cfg_device $%s
625   lov_setconfig %s %d %d %d %s %s
626   quit""" % (mdsuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
627         self.run(cmds)
628
629     # dump the log file
630     def dump(self, dump_file):
631         cmds = """
632   debug_kernel %s 1
633   quit""" % (dump_file)
634         self.run(cmds)
635
636     # get list of devices
637     def device_list(self):
638         devices = '/proc/fs/lustre/devices'
639         ret = []
640         if os.access(devices, os.R_OK):
641             try:
642                 fp = open(devices, 'r')
643                 ret =  fp.readlines()
644                 fp.close()
645             except IOError, e:
646                 log(e)
647         return ret
648
649     # get lustre version
650     def lustre_version(self):
651         rc, out = self.runcmd('version')
652         return out
653
654     # dump mount options
655     def mount_option(self, profile, osc, mdc):
656         cmds = """
657   mount_option %s %s %s
658   quit""" % (profile, osc, mdc)
659         self.run(cmds)
660
661     # delete mount options
662     def del_mount_option(self, profile):
663         cmds = """
664   del_mount_option %s
665   quit""" % (profile,)
666         self.run(cmds)
667
668     def set_timeout(self, timeout):
669         cmds = """
670   set_timeout %s
671   quit""" % (timeout,)
672         self.run(cmds)
673
674     # delete mount options
675     def set_lustre_upcall(self, upcall):
676         cmds = """
677   set_lustre_upcall %s
678   quit""" % (upcall,)
679         self.run(cmds)
680 # ============================================================
681 # Various system-level functions
682 # (ideally moved to their own module)
683
684 # Run a command and return the output and status.
685 # stderr is sent to /dev/null, could use popen3 to
686 # save it if necessary
687 def runcmd(cmd):
688     debug ("+", cmd)
689     if config.noexec: return (0, [])
690     f = os.popen(cmd + ' 2>&1')
691     out = f.readlines()
692     ret = f.close()
693     if ret:
694         ret = ret >> 8
695     else:
696         ret = 0
697     return (ret, out)
698
699 def run(*args):
700     cmd = string.join(map(str,args))
701     return runcmd(cmd)
702
703 # Run a command in the background.
704 def run_daemon(*args):
705     cmd = string.join(map(str,args))
706     debug ("+", cmd)
707     if config.noexec: return 0
708     f = os.popen(cmd + ' 2>&1')
709     ret = f.close()
710     if ret:
711         ret = ret >> 8
712     else:
713         ret = 0
714     return ret
715
716 # Determine full path to use for an external command
717 # searches dirname(argv[0]) first, then PATH
718 def find_prog(cmd):
719     syspath = string.split(os.environ['PATH'], ':')
720     cmdpath = os.path.dirname(sys.argv[0])
721     syspath.insert(0, cmdpath);
722     if config.portals:
723         syspath.insert(0, os.path.join(config.portals, 'utils/'))
724     for d in syspath:
725         prog = os.path.join(d,cmd)
726         if os.access(prog, os.X_OK):
727             return prog
728     return ''
729
730 # Recursively look for file starting at base dir
731 def do_find_file(base, mod):
732     fullname = os.path.join(base, mod)
733     if os.access(fullname, os.R_OK):
734         return fullname
735     for d in os.listdir(base):
736         dir = os.path.join(base,d)
737         if os.path.isdir(dir):
738             module = do_find_file(dir, mod)
739             if module:
740                 return module
741
742 def find_module(src_dir, dev_dir, modname):
743     modbase = src_dir +'/'+ dev_dir +'/'+ modname
744     for modext in '.ko', '.o':
745         module = modbase + modext
746         try: 
747             if os.access(module, os.R_OK):
748                 return module
749         except OSError:
750             pass
751     return None
752
753 # is the path a block device?
754 def is_block(path):
755     s = ()
756     try:
757         s =  os.stat(path)
758     except OSError:
759         return 0
760     return stat.S_ISBLK(s[stat.ST_MODE])
761
762 # build fs according to type
763 # fixme: dangerous
764 def mkfs(dev, devsize, fstype, jsize, isize, mkfsoptions, isblock=1):
765     block_cnt = ''
766     jopt = ''
767     iopt = ''
768     if devsize:
769         if devsize < 8000:
770             panic("size of filesystem on '%s' must be larger than 8MB, but is set to %s"%
771                   (dev, devsize))
772         # devsize is in 1k, and fs block count is in 4k
773         block_cnt = devsize/4
774
775     if fstype in ('ext3', 'extN', 'ldiskfs'):
776         # ext3 journal size is in megabytes
777         if jsize == 0:
778             if devsize == 0:
779                 if not is_block(dev):
780                     ret, out = runcmd("ls -l %s" %dev)
781                     devsize = int(string.split(out[0])[4]) / 1024
782                 else:
783                     # sfdisk works for symlink, hardlink, and realdev
784                     ret, out = runcmd("sfdisk -s %s" %dev)
785                     if not ret:
786                         devsize = int(out[0])
787                     else:
788                         # sfdisk -s will fail for too large block device,
789                         # then, read the size of partition from /proc/partitions
790                                                                                                                
791                         # get the realpath of the device
792                         # it may be the real device, such as /dev/hda7
793                         # or the hardlink created via mknod for a device
794                         if 'realpath' in dir(os.path):
795                             real_dev = os.path.realpath(dev)
796                         else:
797                             real_dev = dev
798                             link_count = 0
799                             while os.path.islink(real_dev) and (link_count < 20):
800                                 link_count = link_count + 1
801                                 dev_link = os.readlink(real_dev)
802                                 if os.path.isabs(dev_link):
803                                     real_dev = dev_link
804                                 else:
805                                     real_dev = os.path.join(os.path.dirname(real_dev), dev_link)
806                                 if link_count > 19:
807                                     panic("Entountered too many symbolic links resolving block device:", dev)
808                                                                                                                
809                         # get the major and minor number of the realpath via ls
810                         # it seems python(os.stat) does not return
811                         # the st_rdev member of the stat structure
812                         ret, out = runcmd("ls -l %s" %real_dev)
813                         major = string.split(string.split(out[0])[4], ",")[0]
814                         minor = string.split(out[0])[5]
815                                                                                                                
816                         # get the devsize from /proc/partitions with the major and minor number
817                         ret, out = runcmd("cat /proc/partitions")
818                         for line in out:
819                             if len(line) > 1:
820                                 if string.split(line)[0] == major and string.split(line)[1] == minor:
821                                     devsize = int(string.split(line)[2])
822                                     break
823
824             if devsize > 1024 * 1024:
825                 jsize = ((devsize / 102400) * 4)
826             if jsize > 400:
827                 jsize = 400        
828         if jsize:  jopt = "-J size=%d" %(jsize,)
829         if isize:  iopt = "-I %d" %(isize,)
830         mkfs = 'mkfs.ext2 -j -b 4096 '
831         if not isblock or config.force:
832             mkfs = mkfs + ' -F '
833     elif fstype == 'reiserfs':
834         # reiserfs journal size is in blocks
835         if jsize:  jopt = "--journal_size %d" %(jsize,)
836         mkfs = 'mkreiserfs -ff'
837     else:
838         panic('unsupported fs type: ', fstype)
839
840     if config.mkfsoptions != None:
841         mkfs = mkfs + ' ' + config.mkfsoptions
842     if mkfsoptions != None:
843         mkfs = mkfs + ' ' + mkfsoptions
844     (ret, out) = run (mkfs, jopt, iopt, dev, block_cnt)
845     if ret:
846         panic("Unable to build fs:", dev, string.join(out))
847     # enable hash tree indexing on fsswe
848     if fstype in ('ext3', 'extN', 'ldiskfs'):
849         htree = 'echo "feature FEATURE_C5" | debugfs -w'
850         (ret, out) = run (htree, dev)
851         if ret:
852             panic("Unable to enable htree:", dev)
853
854 # some systems use /dev/loopN, some /dev/loop/N
855 def loop_base():
856     import re
857     loop = '/dev/loop'
858     if not os.access(loop + str(0), os.R_OK):
859         loop = loop + '/'
860         if not os.access(loop + str(0), os.R_OK):
861             panic ("can't access loop devices")
862     return loop
863     
864 # find loop device assigned to thefile
865 def find_loop(file):
866     loop = loop_base()
867     for n in xrange(0, MAX_LOOP_DEVICES):
868         dev = loop + str(n)
869         if os.access(dev, os.R_OK):
870             (stat, out) = run('losetup', dev)
871             if out and stat == 0:
872                 m = re.search(r'\((.*)\)', out[0])
873                 if m and file == m.group(1):
874                     return dev
875         else:
876             break
877     return ''
878
879 # create file if necessary and assign the first free loop device
880 def init_loop(file, size, fstype, journal_size, inode_size, mkfsoptions, reformat):
881     dev = find_loop(file)
882     if dev:
883         print 'WARNING file:', file, 'already mapped to', dev
884         return dev
885     if reformat or not os.access(file, os.R_OK | os.W_OK):
886         if size < 8000:
887             panic("size of loopback file '%s' must be larger than 8MB, but is set to %s" % (file,size))
888         (ret, out) = run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,
889                                                                          file))
890         if ret:
891             panic("Unable to create backing store:", file)
892         mkfs(file, size, fstype, journal_size, inode_size, mkfsoptions, isblock=0)
893
894     loop = loop_base()
895     # find next free loop
896     for n in xrange(0, MAX_LOOP_DEVICES):
897         dev = loop + str(n)
898         if os.access(dev, os.R_OK):
899             (stat, out) = run('losetup', dev)
900             if stat:
901                 run('losetup', dev, file)
902                 return dev
903         else:
904             print "out of loop devices"
905             return ''
906     print "out of loop devices"
907     return ''
908
909 # undo loop assignment
910 def clean_loop(file):
911     dev = find_loop(file)
912     if dev:
913         ret, out = run('losetup -d', dev)
914         if ret:
915             log('unable to clean loop device:', dev, 'for file:', file)
916             logall(out)
917
918 # determine if dev is formatted as a <fstype> filesystem
919 def need_format(fstype, dev):
920     # FIXME don't know how to implement this    
921     return 0
922
923 # initialize a block device if needed
924 def block_dev(dev, size, fstype, reformat, autoformat, journal_size,
925               inode_size, mkfsoptions):
926     if config.noexec: return dev
927     if not is_block(dev):
928         dev = init_loop(dev, size, fstype, journal_size, inode_size,
929                         mkfsoptions, reformat)
930     elif reformat or (need_format(fstype, dev) and autoformat == 'yes'):
931         mkfs(dev, size, fstype, journal_size, inode_size, mkfsoptions,
932              isblock=0)
933 #    else:
934 #        panic("device:", dev,
935 #              "not prepared, and autoformat is not set.\n",
936 #              "Rerun with --reformat option to format ALL filesystems")
937         
938     return dev
939
940 def if2addr(iface):
941     """lookup IP address for an interface"""
942     rc, out = run("/sbin/ifconfig", iface)
943     if rc or not out:
944        return None
945     addr = string.split(out[1])[1]
946     ip = string.split(addr, ':')[1]
947     return ip
948
949 def def_mount_options(fstype, target):
950     """returns deafult mount options for passed fstype and target (mds, ost)"""
951     if fstype == 'ext3' or fstype == 'ldiskfs':
952         mountfsoptions = "errors=remount-ro"
953         if target == 'ost' and sys_get_branch() == '2.4':
954             mountfsoptions = "%s,asyncdel" % (mountfsoptions)
955         return mountfsoptions
956     return ""
957
958 def sys_get_elan_position_file():
959     procfiles = ["/proc/elan/device0/position",
960                  "/proc/qsnet/elan4/device0/position",
961                  "/proc/qsnet/elan3/device0/position"]
962     for p in procfiles:
963         if os.access(p, os.R_OK):
964             return p
965     return ""
966
967 def sys_get_local_nid(net_type, wildcard, cluster_id):
968     """Return the local nid."""
969     local = ""
970     if sys_get_elan_position_file():
971         local = sys_get_local_address('elan', '*', cluster_id)
972     else:
973         local = sys_get_local_address(net_type, wildcard, cluster_id)
974     return local
975         
976 def sys_get_local_address(net_type, wildcard, cluster_id):
977     """Return the local address for the network type."""
978     local = ""
979     if net_type in ('tcp','openib','iib',):
980         if  ':' in wildcard:
981             iface, star = string.split(wildcard, ':')
982             local = if2addr(iface)
983             if not local:
984                 panic("unable to determine ip for:", wildcard)
985         else:
986             host = socket.gethostname()
987             local = socket.gethostbyname(host)
988     elif net_type == 'elan':
989         # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()'
990         f = sys_get_elan_position_file()
991         if not f:
992             panic ("unable to determine local Elan ID")
993         try:
994             fp = open(f, 'r')
995             lines = fp.readlines()
996             fp.close()
997             for l in lines:
998                 a = string.split(l)
999                 if a[0] == 'NodeId':
1000                     elan_id = a[1]
1001                     break
1002             try:
1003                 nid = my_int(cluster_id) + my_int(elan_id) 
1004                 local = "%d" % (nid)
1005             except ValueError, e:
1006                 local = elan_id
1007         except IOError, e:
1008             log(e)
1009     elif net_type == 'lo':
1010         fixme("automatic local address for loopback")
1011     elif net_type == 'gm':
1012         fixme("automatic local address for GM")
1013
1014     return local
1015
1016 def sys_get_branch():
1017     """Returns kernel release"""
1018     try:
1019         fp = open('/proc/sys/kernel/osrelease')
1020         lines = fp.readlines()
1021         fp.close()
1022                                                                                                                              
1023         for l in lines:
1024             version = string.split(l)
1025             a = string.split(version[0], '.')
1026             return a[0] + '.' + a[1]
1027     except IOError, e:
1028         log(e)
1029     return ""
1030
1031 def mod_loaded(modname):
1032     """Check if a module is already loaded. Look in /proc/modules for it."""
1033     try:
1034         fp = open('/proc/modules')
1035         lines = fp.readlines()
1036         fp.close()
1037         # please forgive my tired fingers for this one
1038         ret = filter(lambda word, mod=modname: word == mod,
1039                      map(lambda line: string.split(line)[0], lines))
1040         return ret
1041     except Exception, e:
1042         return 0
1043
1044 # XXX: instead of device_list, ask for $name and see what we get
1045 def is_prepared(name):
1046     """Return true if a device exists for the name"""
1047     if config.lctl_dump:
1048         return 0
1049     if (config.noexec or config.record) and config.cleanup:
1050         return 1
1051     try:
1052         # expect this format:
1053         # 1 UP ldlm ldlm ldlm_UUID 2
1054         out = lctl.device_list()
1055         for s in out:
1056             if name == string.split(s)[3]:
1057                 return 1
1058     except CommandError, e:
1059         e.dump()
1060     return 0
1061
1062 def is_network_prepared():
1063     """If the any device exists, then assume that all networking
1064        has been configured"""
1065     out = lctl.device_list()
1066     return len(out) > 0
1067
1068 def fs_is_mounted(path):
1069     """Return true if path is a mounted lustre filesystem"""
1070     try:
1071         fp = open('/proc/mounts')
1072         lines = fp.readlines()
1073         fp.close()
1074         for l in lines:
1075             a = string.split(l)
1076             if a[1] == path and a[2] == 'lustre_lite':
1077                 return 1
1078     except IOError, e:
1079         log(e)
1080     return 0
1081         
1082
1083 class kmod:
1084     """Manage kernel modules"""
1085     def __init__(self, lustre_dir, portals_dir):
1086         self.lustre_dir = lustre_dir
1087         self.portals_dir = portals_dir
1088         self.kmodule_list = []
1089
1090     def add_portals_module(self, dev_dir, modname):
1091         """Append a module to list of modules to load."""
1092         self.kmodule_list.append((self.portals_dir, dev_dir, modname))
1093
1094     def add_lustre_module(self, dev_dir, modname):
1095         """Append a module to list of modules to load."""
1096         self.kmodule_list.append((self.lustre_dir, dev_dir, modname))
1097
1098     def load_module(self):
1099         """Load all the modules in the list in the order they appear."""
1100         for src_dir, dev_dir, mod in self.kmodule_list:
1101             if mod_loaded(mod) and not config.noexec:
1102                 continue
1103             log ('loading module:', mod, 'srcdir', src_dir, 'devdir', dev_dir)
1104             if src_dir:
1105                 module = find_module(src_dir, dev_dir,  mod)
1106                 if not module:
1107                     panic('module not found:', mod)
1108                 (rc, out)  = run('/sbin/insmod', module)
1109                 if rc and not mod_loaded(mod):
1110                     raise CommandError('insmod', out, rc)
1111             else:
1112                 (rc, out)  = run('/sbin/modprobe', mod)
1113                 if rc and not mod_loaded(mod):
1114                     raise CommandError('modprobe', out, rc)
1115
1116
1117     def cleanup_module(self):
1118         """Unload the modules in the list in reverse order."""
1119         rev = self.kmodule_list
1120         rev.reverse()
1121         for src_dir, dev_dir, mod in rev:
1122             if not mod_loaded(mod) and not config.noexec:
1123                 continue
1124             # debug hack
1125             if mod == 'portals' and config.dump:
1126                 lctl.dump(config.dump)
1127             log('unloading module:', mod)
1128             (rc, out) = run('/sbin/rmmod', mod)
1129             if rc:
1130                 log('! unable to unload module:', mod)
1131                 logall(out)
1132
1133 # ============================================================
1134 # Classes to prepare and cleanup the various objects
1135 #
1136 class Module:
1137     """ Base class for the rest of the modules. The default cleanup method is
1138     defined here, as well as some utilitiy funcs.
1139     """
1140     def __init__(self, module_name, db):
1141         self.db = db
1142         self.module_name = module_name
1143         self.name = self.db.getName()
1144         self.uuid = self.db.getUUID()
1145         self._server = None
1146         self._connected = 0
1147         self.kmod = kmod(config.lustre, config.portals)
1148         
1149     def info(self, *args):
1150         msg = string.join(map(str,args))
1151         print self.module_name + ":", self.name, self.uuid, msg
1152
1153     def cleanup(self):
1154         """ default cleanup, used for most modules """
1155         self.info()
1156         try:
1157             lctl.cleanup(self.name, self.uuid, config.force)
1158         except CommandError, e:
1159             log(self.module_name, "cleanup failed: ", self.name)
1160             e.dump()
1161             cleanup_error(e.rc)
1162             
1163     def add_portals_module(self, dev_dir, modname):
1164         """Append a module to list of modules to load."""
1165         self.kmod.add_portals_module(dev_dir, modname)
1166
1167     def add_lustre_module(self, dev_dir, modname):
1168         """Append a module to list of modules to load."""
1169         self.kmod.add_lustre_module(dev_dir, modname)
1170
1171     def load_module(self):
1172         """Load all the modules in the list in the order they appear."""
1173         self.kmod.load_module()
1174             
1175     def cleanup_module(self):
1176         """Unload the modules in the list in reverse order."""
1177         if self.safe_to_clean():
1178             self.kmod.cleanup_module()
1179
1180     def safe_to_clean(self):
1181         return 1
1182         
1183     def safe_to_clean_modules(self):
1184         return self.safe_to_clean()
1185         
1186 class Network(Module):
1187     def __init__(self,db):
1188         Module.__init__(self, 'NETWORK', db)
1189         self.net_type = self.db.get_val('nettype')
1190         self.nid = self.db.get_val('nid', '*')
1191         self.cluster_id = self.db.get_val('clusterid', "0")
1192         self.port = self.db.get_val_int('port', 0)
1193
1194         if '*' in self.nid:
1195             self.nid = sys_get_local_nid(self.net_type, self.nid, self.cluster_id)
1196             if not self.nid:
1197                 panic("unable to set nid for", self.net_type, self.nid, cluster_id)
1198             self.generic_nid = 1
1199             debug("nid:", self.nid)
1200         else:
1201             self.generic_nid = 0
1202
1203         self.nid_uuid = self.nid_to_uuid(self.nid)
1204
1205         self.hostaddr = self.db.get_hostaddr()
1206         if len(self.hostaddr) == 0:
1207             self.hostaddr.append(self.nid)
1208         if '*' in self.hostaddr[0]:
1209             self.hostaddr[0] = sys_get_local_address(self.net_type, self.hostaddr[0], self.cluster_id)
1210             if not self.hostaddr[0]:
1211                 panic("unable to set hostaddr for", self.net_type, self.hostaddr[0], self.cluster_id)
1212             debug("hostaddr:", self.hostaddr[0])
1213
1214         self.add_portals_module("libcfs", 'libcfs')
1215         self.add_portals_module("portals", 'portals')
1216         if node_needs_router():
1217             self.add_portals_module("router", 'kptlrouter')
1218         if self.net_type == 'tcp':
1219             self.add_portals_module("knals/socknal", 'ksocknal')
1220         if self.net_type == 'elan':
1221             self.add_portals_module("knals/qswnal", 'kqswnal')
1222         if self.net_type == 'gm':
1223             self.add_portals_module("knals/gmnal", 'kgmnal')
1224         if self.net_type == 'openib':
1225             self.add_portals_module("knals/openibnal", 'kopenibnal')
1226         if self.net_type == 'iib':
1227             self.add_portals_module("knals/iibnal", 'kiibnal')
1228         if self.net_type == 'lo':
1229             self.add_portals_module("knals/lonal", 'klonal')
1230
1231     def nid_to_uuid(self, nid):
1232         return "NID_%s_UUID" %(nid,)
1233
1234     def prepare(self):
1235         if is_network_prepared():
1236             return
1237         self.info(self.net_type, self.nid, self.port)
1238         if not (config.record and self.generic_nid):
1239             lctl.network(self.net_type, self.nid)
1240         if self.net_type == 'tcp':
1241             sys_tweak_socknal()
1242             for hostaddr in self.db.get_hostaddr():
1243                 ip = string.split(hostaddr, '/')[0]
1244                 if len(string.split(hostaddr, '/')) == 2:
1245                     netmask = string.split(hostaddr, '/')[1]
1246                 else:
1247                     netmask = ""
1248                 lctl.add_interface(self.net_type, ip, netmask)
1249         if self.net_type == 'elan':
1250             sys_optimize_elan()
1251         if self.port and  node_is_router():
1252             run_one_acceptor(self.port)
1253             self.connect_peer_gateways()
1254
1255     def connect_peer_gateways(self):
1256         for router in self.db.lookup_class('node'):
1257             if router.get_val_int('router', 0):
1258                 for netuuid in router.get_networks():
1259                     net = self.db.lookup(netuuid)
1260                     gw = Network(net)
1261                     if (gw.cluster_id == self.cluster_id and
1262                         gw.net_type == self.net_type):
1263                         if gw.nid != self.nid:
1264                             lctl.connect(gw)
1265
1266     def disconnect_peer_gateways(self):
1267         for router in self.db.lookup_class('node'):
1268             if router.get_val_int('router', 0):
1269                 for netuuid in router.get_networks():
1270                     net = self.db.lookup(netuuid)
1271                     gw = Network(net)
1272                     if (gw.cluster_id == self.cluster_id and
1273                         gw.net_type == self.net_type):
1274                         if gw.nid != self.nid:
1275                             try:
1276                                 lctl.disconnect(gw)
1277                             except CommandError, e:
1278                                 print "disconnect failed: ", self.name
1279                                 e.dump()
1280                                 cleanup_error(e.rc)
1281
1282     def safe_to_clean(self):
1283         return not is_network_prepared()
1284
1285     def cleanup(self):
1286         self.info(self.net_type, self.nid, self.port)
1287         if self.port:
1288             stop_acceptor(self.port)
1289         if  node_is_router():
1290             self.disconnect_peer_gateways()
1291         if self.net_type == 'tcp':
1292             for hostaddr in self.db.get_hostaddr():
1293                 ip = string.split(hostaddr, '/')[0]
1294                 lctl.del_interface(self.net_type, ip)
1295
1296 class RouteTable(Module):
1297     def __init__(self,db):
1298         Module.__init__(self, 'ROUTES', db)
1299
1300     def server_for_route(self, net_type, gw, gw_cluster_id, tgt_cluster_id,
1301                          lo, hi):
1302         # only setup connections for tcp, openib, and iib NALs
1303         srvdb = None
1304         if not net_type in ('tcp','openib','iib',):
1305             return None
1306
1307         # connect to target if route is to single node and this node is the gw
1308         if lo == hi and local_interface(net_type, gw_cluster_id, gw):
1309             if not local_cluster(net_type, tgt_cluster_id):
1310                 panic("target", lo, " not on the local cluster")
1311             srvdb = self.db.nid2server(lo, net_type, gw_cluster_id)
1312         # connect to gateway if this node is not the gw
1313         elif (local_cluster(net_type, gw_cluster_id)
1314               and not local_interface(net_type, gw_cluster_id, gw)):
1315             srvdb = self.db.nid2server(gw, net_type, gw_cluster_id)
1316         else:
1317             return None
1318
1319         if not srvdb:
1320             panic("no server for nid", lo)
1321             return None
1322
1323         return Network(srvdb)
1324         
1325     def prepare(self):
1326         if is_network_prepared():
1327             return
1328         self.info()
1329         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1330             lctl.add_route(net_type, gw, lo, hi)
1331             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1332             if srv:
1333                 lctl.connect(srv)
1334
1335     def safe_to_clean(self):
1336         return not is_network_prepared()
1337
1338     def cleanup(self):
1339         if is_network_prepared():
1340             # the network is still being used, don't clean it up
1341             return
1342         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1343             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1344             if srv:
1345                 try:
1346                     lctl.disconnect(srv)
1347                 except CommandError, e:
1348                     print "disconnect failed: ", self.name
1349                     e.dump()
1350                     cleanup_error(e.rc)
1351
1352             try:
1353                 lctl.del_route(net_type, gw, lo, hi)
1354             except CommandError, e:
1355                 print "del_route failed: ", self.name
1356                 e.dump()
1357                 cleanup_error(e.rc)
1358
1359 class Management(Module):
1360     def __init__(self, db):
1361         Module.__init__(self, 'MGMT', db)
1362         self.add_lustre_module('lvfs', 'lvfs')
1363         self.add_lustre_module('obdclass', 'obdclass')
1364         self.add_lustre_module('ptlrpc', 'ptlrpc')
1365         self.add_lustre_module('mgmt', 'mgmt_svc')
1366
1367     def prepare(self):
1368         if is_prepared(self.name):
1369             return
1370         self.info()
1371         lctl.newdev("mgmt", self.name, self.uuid)
1372
1373     def safe_to_clean(self):
1374         return 1
1375
1376     def cleanup(self):
1377         if is_prepared(self.name):
1378             Module.cleanup(self)
1379
1380 # This is only needed to load the modules; the LDLM device
1381 # is now created automatically.
1382 class LDLM(Module):
1383     def __init__(self,db):
1384         Module.__init__(self, 'LDLM', db)
1385         self.add_lustre_module('lvfs', 'lvfs')
1386         self.add_lustre_module('obdclass', 'obdclass')
1387         self.add_lustre_module('ptlrpc', 'ptlrpc')
1388
1389     def prepare(self):
1390         return
1391
1392     def cleanup(self):
1393         return
1394
1395 class LOV(Module):
1396     def __init__(self, db, uuid, fs_name, name_override = None, config_only = None):
1397         Module.__init__(self, 'LOV', db)
1398         if name_override != None:
1399             self.name = "lov_%s" % name_override
1400         self.add_lustre_module('lov', 'lov')
1401         self.mds_uuid = self.db.get_first_ref('mds')
1402         self.stripe_sz = self.db.get_val_int('stripesize', 1048576)
1403         self.stripe_off = self.db.get_val_int('stripeoffset', 0)
1404         self.pattern = self.db.get_val_int('stripepattern', 0)
1405         self.devlist = self.db.get_refs('obd')
1406         self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist))
1407         self.osclist = []
1408         self.desc_uuid = self.uuid
1409         self.uuid = generate_client_uuid(self.name)
1410         self.fs_name = fs_name
1411         if config_only:
1412             self.config_only = 1
1413             return
1414         self.config_only = None
1415         mds= self.db.lookup(self.mds_uuid)
1416         self.mds_name = mds.getName()
1417         for obd_uuid in self.devlist:
1418             obd = self.db.lookup(obd_uuid)
1419             osc = get_osc(obd, self.uuid, fs_name)
1420             if osc:
1421                 self.osclist.append(osc)
1422             else:
1423                 panic('osc not found:', obd_uuid)
1424             
1425     def prepare(self):
1426         if is_prepared(self.name):
1427             return
1428         if self.config_only:
1429             panic("Can't prepare config_only LOV ", self.name)
1430             
1431         for osc in self.osclist:
1432             try:
1433                 # Only ignore connect failures with --force, which
1434                 # isn't implemented here yet.
1435                 osc.prepare(ignore_connect_failure=0)
1436             except CommandError, e:
1437                 print "Error preparing OSC %s\n" % osc.uuid
1438                 raise e
1439         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
1440                   self.stripe_off, self.pattern, self.devlist, self.mds_name)
1441         lctl.lov_setup(self.name, self.uuid,
1442                        self.desc_uuid, self.mds_name, self.stripe_cnt,
1443                        self.stripe_sz, self.stripe_off, self.pattern,
1444                        string.join(self.devlist))
1445
1446     def cleanup(self):
1447         if is_prepared(self.name):
1448             Module.cleanup(self)
1449         if self.config_only:
1450             panic("Can't clean up config_only LOV ", self.name)
1451         for osc in self.osclist:
1452             osc.cleanup()
1453
1454     def load_module(self):
1455         if self.config_only:
1456             panic("Can't load modules for config_only LOV ", self.name)
1457         for osc in self.osclist:
1458             osc.load_module()
1459             break
1460         Module.load_module(self)
1461
1462     def cleanup_module(self):
1463         if self.config_only:
1464             panic("Can't cleanup modules for config_only LOV ", self.name)
1465         Module.cleanup_module(self)
1466         for osc in self.osclist:
1467             osc.cleanup_module()
1468             break
1469
1470 class MDSDEV(Module):
1471     def __init__(self,db):
1472         Module.__init__(self, 'MDSDEV', db)
1473         self.devpath = self.db.get_val('devpath','')
1474         self.size = self.db.get_val_int('devsize', 0)
1475         self.journal_size = self.db.get_val_int('journalsize', 0)
1476         self.fstype = self.db.get_val('fstype', '')
1477         self.nspath = self.db.get_val('nspath', '')
1478         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1479         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
1480         # overwrite the orignal MDSDEV name and uuid with the MDS name and uuid
1481         target_uuid = self.db.get_first_ref('target')
1482         mds = self.db.lookup(target_uuid)
1483         self.name = mds.getName()
1484         self.filesystem_uuids = mds.get_refs('filesystem')
1485         # FIXME: if fstype not set, then determine based on kernel version
1486         self.format = self.db.get_val('autoformat', "no")
1487         if mds.get_val('failover', 0):
1488             self.failover_mds = 'f'
1489         else:
1490             self.failover_mds = 'n'
1491         active_uuid = get_active_target(mds)
1492         if not active_uuid:
1493             panic("No target device found:", target_uuid)
1494         if active_uuid == self.uuid:
1495             self.active = 1
1496         else:
1497             self.active = 0
1498         if self.active and config.group and config.group != mds.get_val('group'):
1499             self.active = 0
1500
1501         self.inode_size = self.db.get_val_int('inodesize', 0)
1502         if self.inode_size == 0:
1503             # find the LOV for this MDS
1504             lovconfig_uuid = mds.get_first_ref('lovconfig')
1505             if not lovconfig_uuid:
1506                 panic("No LOV config found for MDS ", mds.name)
1507             lovconfig = mds.lookup(lovconfig_uuid)
1508             lov_uuid = lovconfig.get_first_ref('lov')
1509             if not lov_uuid:
1510                 panic("No LOV found for lovconfig ", lovconfig.name)
1511             lov = LOV(self.db.lookup(lov_uuid), lov_uuid, 'FS_name', config_only = 1)
1512
1513             # default stripe count controls default inode_size
1514             if (lov.stripe_cnt > 0):
1515                 stripe_count = lov.stripe_cnt
1516             else:
1517                 stripe_count = len(lov.devlist)
1518             if stripe_count > 77:
1519                 self.inode_size = 4096
1520             elif stripe_count > 35:
1521                 self.inode_size = 2048
1522             elif stripe_count > 13:
1523                 self.inode_size = 1024
1524             elif stripe_count > 3:
1525                 self.inode_size = 512
1526             else:
1527                 self.inode_size = 256
1528
1529         self.target_dev_uuid = self.uuid
1530         self.uuid = target_uuid
1531
1532         # loading modules
1533         self.add_lustre_module('mdc', 'mdc')
1534         self.add_lustre_module('osc', 'osc')
1535         self.add_lustre_module('lov', 'lov')
1536         self.add_lustre_module('mds', 'mds')
1537         if self.fstype == 'ldiskfs':
1538             self.add_lustre_module('ldiskfs', 'ldiskfs')
1539         if self.fstype:
1540             self.add_lustre_module('lvfs', 'fsfilt_%s' % (self.fstype))
1541
1542     def load_module(self):
1543         if self.active:
1544             Module.load_module(self)
1545             
1546     def prepare(self):
1547         if is_prepared(self.name):
1548             return
1549         if not self.active:
1550             debug(self.uuid, "not active")
1551             return
1552         if config.reformat:
1553             # run write_conf automatically, if --reformat used
1554             self.write_conf()
1555         self.info(self.devpath, self.fstype, self.size, self.format)
1556         run_acceptors()
1557         # never reformat here
1558         blkdev = block_dev(self.devpath, self.size, self.fstype, 0,
1559                            self.format, self.journal_size, self.inode_size,
1560                            self.mkfsoptions)
1561         if not is_prepared('MDT'):
1562             lctl.newdev("mdt", 'MDT', 'MDT_UUID', setup ="")
1563         try: 
1564             mountfsoptions = def_mount_options(self.fstype, 'mds')
1565                                                                                                                              
1566             if config.mountfsoptions:
1567                 if mountfsoptions:
1568                     mountfsoptions = mountfsoptions + ',' + config.mountfsoptions
1569                 else:
1570                     mountfsoptions = config.mountfsoptions
1571                 if self.mountfsoptions:
1572                     mountfsoptions = mountfsoptions + ',' + self.mountfsoptions
1573             else:
1574                 if self.mountfsoptions:
1575                     if mountfsoptions:
1576                         mountfsoptions = mountfsoptions + ',' + self.mountfsoptions
1577                     else:
1578                         mountfsoptions = self.mountfsoptions
1579
1580             print 'MDS mount options: ' + mountfsoptions
1581
1582             lctl.newdev("mds", self.name, self.uuid,
1583                         setup ="%s %s %s %s" %(blkdev, self.fstype, self.name, mountfsoptions))
1584         except CommandError, e:
1585             if e.rc == 2:
1586                 panic("MDS is missing the config log. Need to run " +
1587                        "lconf --write_conf.")
1588             else:
1589                 raise e
1590
1591     def write_conf(self):
1592         if is_prepared(self.name):
1593             return
1594         self.info(self.devpath, self.fstype, self.format)
1595         blkdev = block_dev(self.devpath, self.size, self.fstype,
1596                            config.reformat, self.format, self.journal_size,
1597                            self.inode_size, self.mkfsoptions)
1598         lctl.newdev("mds", self.name, self.uuid,
1599                     setup ="%s %s" %(blkdev, self.fstype))
1600                 
1601         # record logs for the MDS lov
1602         for uuid in self.filesystem_uuids:
1603             log("recording clients for filesystem:", uuid)
1604             fs = self.db.lookup(uuid)
1605             obd_uuid = fs.get_first_ref('obd')
1606             client_uuid = generate_client_uuid(self.name)
1607             client = VOSC(self.db.lookup(obd_uuid), client_uuid, self.name,
1608                           self.name)
1609             config.record = 1
1610             lctl.clear_log(self.name, self.name)
1611             lctl.record(self.name, self.name)
1612             client.prepare()
1613             lctl.mount_option(self.name, client.get_name(), "")
1614             lctl.end_record()
1615
1616             config.cleanup = 1
1617             lctl.clear_log(self.name, self.name + '-clean')
1618             lctl.record(self.name, self.name + '-clean')
1619             client.cleanup()
1620             lctl.del_mount_option(self.name)
1621             lctl.end_record()
1622             config.cleanup = 0
1623             config.record = 0
1624
1625         # record logs for each client
1626         if config.ldapurl:
1627             config_options = "--ldapurl " + config.ldapurl + " --config " + config.config
1628         else:
1629             config_options = CONFIG_FILE
1630
1631         for node_db in self.db.lookup_class('node'):
1632             client_name = node_db.getName()
1633             for prof_uuid in node_db.get_refs('profile'):
1634                 prof_db = node_db.lookup(prof_uuid)
1635                 # refactor this into a funtion to test "clientness"
1636                 # of a node.
1637                 for ref_class, ref_uuid in prof_db.get_all_refs():
1638                     if ref_class in ('mountpoint','echoclient'):
1639                         debug("recording", client_name)
1640                         old_noexec = config.noexec
1641                         config.noexec = 0
1642                         noexec_opt = ('', '-n')
1643                         ret, out = run (sys.argv[0],
1644                                         noexec_opt[old_noexec == 1],
1645                                         " -v --record --nomod",
1646                                         "--record_log", client_name,
1647                                         "--record_device", self.name,
1648                                         "--node", client_name,
1649                                         config_options)
1650                         if ret:
1651                             lctl.clear_log(self.name, client_name)
1652                             print out
1653                             self.cleanup()
1654                             panic("Record client log %s on %s failed" %(
1655                                            client_name, self.name))
1656                         if config.verbose:
1657                             for s in out: log("record> ", string.strip(s))
1658                         ret, out = run (sys.argv[0],
1659                                         noexec_opt[old_noexec == 1],
1660                                         "--cleanup -v --record --nomod",
1661                                         "--record_log", client_name + "-clean",
1662                                         "--record_device", self.name,
1663                                         "--node", client_name,
1664                                         config_options)
1665                         if ret:
1666               # In this case, although 0-conf mount works but 0-conf umount
1667               # doesn't work. As a boring result, the user is forced to
1668               # cleanup client service manually again and again. So I prefer 
1669               # deleting these two llogs together and let the user write_conf.
1670                             lctl.clear_log(self.name, client_name)
1671                             lctl.clear_log(self.name, client_name + '-clean')
1672                             print out
1673                             self.cleanup()
1674                             panic("Record client log %s on %s failed" %(
1675                                            client_name + '-clean', self.name))
1676                         if config.verbose:
1677                             for s in out: log("record> ", string.strip(s))
1678                         config.noexec = old_noexec
1679         try:
1680             lctl.cleanup(self.name, self.uuid, 0, 0)
1681         except CommandError, e:
1682             log(self.module_name, "cleanup failed: ", self.name)
1683             e.dump()
1684             cleanup_error(e.rc)
1685             Module.cleanup(self)
1686         clean_loop(self.devpath)
1687  
1688     def msd_remaining(self):
1689         out = lctl.device_list()
1690         for s in out:
1691             if string.split(s)[2] in ('mds',):
1692                 return 1
1693
1694     def safe_to_clean(self):
1695         return self.active
1696
1697     def safe_to_clean_modules(self):
1698         return not self.msd_remaining()
1699
1700     def cleanup(self):
1701         if not self.active:
1702             debug(self.uuid, "not active")
1703             return
1704         self.info()
1705         if is_prepared(self.name):
1706             try:
1707                 lctl.cleanup(self.name, self.uuid, config.force,
1708                              config.failover)
1709             except CommandError, e:
1710                 log(self.module_name, "cleanup failed: ", self.name)
1711                 e.dump()
1712                 cleanup_error(e.rc)
1713                 Module.cleanup(self)
1714         if not self.msd_remaining() and is_prepared('MDT'):
1715             try:
1716                 lctl.cleanup("MDT", "MDT_UUID", config.force,
1717                              config.failover)
1718             except CommandError, e:
1719                 print "cleanup failed: ", self.name
1720                 e.dump()
1721                 cleanup_error(e.rc)
1722         clean_loop(self.devpath)
1723
1724 class OSD(Module):
1725     def __init__(self, db):
1726         Module.__init__(self, 'OSD', db)
1727         self.osdtype = self.db.get_val('osdtype')
1728         self.devpath = self.db.get_val('devpath', '')
1729         self.size = self.db.get_val_int('devsize', 0)
1730         self.journal_size = self.db.get_val_int('journalsize', 0)
1731         self.inode_size = self.db.get_val_int('inodesize', 0)
1732         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1733         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
1734         self.fstype = self.db.get_val('fstype', '')
1735         self.nspath = self.db.get_val('nspath', '')
1736         target_uuid = self.db.get_first_ref('target')
1737         ost = self.db.lookup(target_uuid)
1738         self.name = ost.getName()
1739         self.format = self.db.get_val('autoformat', 'yes')
1740         if ost.get_val('failover', 0):
1741             self.failover_ost = 'f'
1742         else:
1743             self.failover_ost = 'n'
1744
1745         active_uuid = get_active_target(ost)
1746         if not active_uuid:
1747             panic("No target device found:", target_uuid)
1748         if active_uuid == self.uuid:
1749             self.active = 1
1750         else:
1751             self.active = 0
1752         if self.active and config.group and config.group != ost.get_val('group'):
1753             self.active = 0
1754             
1755         self.target_dev_uuid = self.uuid
1756         self.uuid = target_uuid
1757         # modules
1758         self.add_lustre_module('ost', 'ost')
1759         # FIXME: should we default to ext3 here?
1760         if self.fstype == 'ldiskfs':
1761             self.add_lustre_module('ldiskfs', 'ldiskfs')
1762         if self.fstype:
1763             self.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.fstype))
1764         self.add_lustre_module(self.osdtype, self.osdtype)
1765
1766     def load_module(self):
1767         if self.active:
1768             Module.load_module(self)
1769
1770     # need to check /proc/mounts and /etc/mtab before
1771     # formatting anything.
1772     # FIXME: check if device is already formatted.
1773     def prepare(self):
1774         if is_prepared(self.name):
1775             return
1776         if not self.active:
1777             debug(self.uuid, "not active")
1778             return
1779         self.info(self.osdtype, self.devpath, self.size, self.fstype,
1780                   self.format, self.journal_size, self.inode_size)
1781         run_acceptors()
1782         if self.osdtype == 'obdecho':
1783             blkdev = ''
1784         else:
1785             blkdev = block_dev(self.devpath, self.size, self.fstype,
1786                                config.reformat, self.format, self.journal_size,
1787                                self.inode_size, self.mkfsoptions)
1788
1789         mountfsoptions = def_mount_options(self.fstype, 'ost')
1790                                                                                                                              
1791         if config.mountfsoptions:
1792             if mountfsoptions:
1793                 mountfsoptions = mountfsoptions + ',' + config.mountfsoptions
1794             else:
1795                 mountfsoptions = config.mountfsoptions
1796             if self.mountfsoptions:
1797                 mountfsoptions = mountfsoptions + ',' + self.mountfsoptions
1798         else:
1799             if self.mountfsoptions:
1800                 if mountfsoptions:
1801                     mountfsoptions = mountfsoptions + ',' + self.mountfsoptions
1802                 else:
1803                     mountfsoptions = self.mountfsoptions
1804         
1805         print 'OST mount options: ' + mountfsoptions
1806
1807         lctl.newdev(self.osdtype, self.name, self.uuid,
1808                     setup ="%s %s %s %s" %(blkdev, self.fstype,
1809                                            self.failover_ost, mountfsoptions))
1810         if not is_prepared('OSS'):
1811             lctl.newdev("ost", 'OSS', 'OSS_UUID', setup ="")
1812
1813     def osd_remaining(self):
1814         out = lctl.device_list()
1815         for s in out:
1816             if string.split(s)[2] in ('obdfilter', 'obdecho'):
1817                 return 1
1818
1819     def safe_to_clean(self):
1820         return self.active
1821
1822     def safe_to_clean_modules(self):
1823         return not self.osd_remaining()
1824
1825     def cleanup(self):
1826         if not self.active:
1827             debug(self.uuid, "not active")
1828             return
1829         if is_prepared(self.name):
1830             self.info()
1831             try:
1832                 lctl.cleanup(self.name, self.uuid, config.force,
1833                              config.failover)
1834             except CommandError, e:
1835                 log(self.module_name, "cleanup failed: ", self.name)
1836                 e.dump()
1837                 cleanup_error(e.rc)
1838         if not self.osd_remaining() and is_prepared('OSS'):
1839             try:
1840                 lctl.cleanup("OSS", "OSS_UUID", config.force,
1841                              config.failover)
1842             except CommandError, e:
1843                 print "cleanup failed: ", self.name
1844                 e.dump()
1845                 cleanup_error(e.rc)
1846         if not self.osdtype == 'obdecho':
1847             clean_loop(self.devpath)
1848
1849 def mgmt_uuid_for_fs(mtpt_name):
1850     if not mtpt_name:
1851         return ''
1852     mtpt_db = toplustreDB.lookup_name(mtpt_name)
1853     fs_uuid = mtpt_db.get_first_ref('filesystem')
1854     fs = toplustreDB.lookup(fs_uuid)
1855     if not fs:
1856         return ''
1857     return fs.get_first_ref('mgmt')
1858
1859 # Generic client module, used by OSC and MDC
1860 class Client(Module):
1861     def __init__(self, tgtdb, uuid, module, fs_name, self_name=None,
1862                  module_dir=None):
1863         self.target_name = tgtdb.getName()
1864         self.target_uuid = tgtdb.getUUID()
1865         self.db = tgtdb
1866
1867         self.tgt_dev_uuid = get_active_target(tgtdb)
1868         if not self.tgt_dev_uuid:
1869             panic("No target device found for target:", self.target_name)
1870             
1871         self.kmod = kmod(config.lustre, config.portals)
1872         self._server = None
1873         self._connected = 0
1874
1875         self.module = module
1876         self.module_name = string.upper(module)
1877         if not self_name:
1878             self.name = '%s_%s_%s_%s' % (self.module_name, socket.gethostname(),
1879                                          self.target_name, fs_name)
1880         else:
1881             self.name = self_name
1882         self.uuid = uuid
1883         self.lookup_server(self.tgt_dev_uuid)
1884         mgmt_uuid = mgmt_uuid_for_fs(fs_name)
1885         if mgmt_uuid:
1886             self.mgmt_name = mgmtcli_name_for_uuid(mgmt_uuid)
1887         else:
1888             self.mgmt_name = ''
1889         self.fs_name = fs_name
1890         if not module_dir:
1891             module_dir = module
1892         self.add_lustre_module(module_dir, module)
1893
1894     def lookup_server(self, srv_uuid):
1895         """ Lookup a server's network information """
1896         self._server_nets = get_ost_net(self.db, srv_uuid)
1897         if len(self._server_nets) == 0:
1898             panic ("Unable to find a server for:", srv_uuid)
1899
1900     def get_servers(self):
1901         return self._server_nets
1902
1903     def prepare(self, ignore_connect_failure = 0):
1904         self.info(self.target_uuid)
1905         if is_prepared(self.name):
1906             self.cleanup()
1907         try:
1908             srv = choose_local_server(self.get_servers())
1909             if srv:
1910                 lctl.connect(srv)
1911             else:
1912                 routes = find_route(self.get_servers())
1913                 if len(routes) == 0:
1914                     panic ("no route to",  self.target_uuid)
1915                 for (srv, r) in routes:
1916                     lctl.add_route_host(r[0], srv.nid_uuid, r[1], r[3])
1917         except CommandError, e:
1918             if not ignore_connect_failure:
1919                 raise e
1920         if srv:
1921             if self.target_uuid in config.inactive and self.permits_inactive():
1922                 debug("%s inactive" % self.target_uuid)
1923                 inactive_p = "inactive"
1924             else:
1925                 debug("%s active" % self.target_uuid)
1926                 inactive_p = ""
1927             lctl.newdev(self.module, self.name, self.uuid,
1928                         setup ="%s %s %s %s" % (self.target_uuid, srv.nid_uuid,
1929                                                 inactive_p, self.mgmt_name))
1930
1931     def cleanup(self):
1932         if is_prepared(self.name):
1933             Module.cleanup(self)
1934             try:
1935                 srv = choose_local_server(self.get_servers())
1936                 if srv:
1937                     lctl.disconnect(srv)
1938                 else:
1939                     for (srv, r) in find_route(self.get_servers()):
1940                         lctl.del_route_host(r[0], srv.nid_uuid, r[1], r[3])
1941             except CommandError, e:
1942                 log(self.module_name, "cleanup failed: ", self.name)
1943                 e.dump()
1944                 cleanup_error(e.rc)
1945
1946
1947 class MDC(Client):
1948     def __init__(self, db, uuid, fs_name):
1949          Client.__init__(self, db, uuid, 'mdc', fs_name)
1950
1951     def permits_inactive(self):
1952         return 0
1953
1954 class OSC(Client):
1955     def __init__(self, db, uuid, fs_name):
1956          Client.__init__(self, db, uuid, 'osc', fs_name)
1957
1958     def permits_inactive(self):
1959         return 1
1960
1961 def mgmtcli_name_for_uuid(uuid):
1962     return 'MGMTCLI_%s' % uuid
1963
1964 class ManagementClient(Client):
1965     def __init__(self, db, uuid):
1966         Client.__init__(self, db, uuid, 'mgmt_cli', '',
1967                         self_name = mgmtcli_name_for_uuid(db.getUUID()),
1968                         module_dir = 'mgmt')
1969             
1970 class COBD(Module):
1971     def __init__(self, db):
1972         Module.__init__(self, 'COBD', db)
1973         self.real_uuid = self.db.get_first_ref('realobd')
1974         self.cache_uuid = self.db.get_first_ref('cacheobd')
1975         self.add_lustre_module('cobd' , 'cobd')
1976
1977     # need to check /proc/mounts and /etc/mtab before
1978     # formatting anything.
1979     # FIXME: check if device is already formatted.
1980     def prepare(self):
1981         if is_prepared(self.name):
1982             return
1983         self.info(self.real_uuid, self.cache_uuid)
1984         lctl.newdev("cobd", self.name, self.uuid,
1985                     setup ="%s %s" %(self.real_uuid, self.cache_uuid))
1986
1987
1988 # virtual interface for  OSC and LOV
1989 class VOSC(Module):
1990     def __init__(self, db, uuid, fs_name, name_override = None):
1991         Module.__init__(self, 'VOSC', db)
1992         if db.get_class() == 'lov':
1993             self.osc = LOV(db, uuid, fs_name, name_override)
1994         else:
1995             self.osc = get_osc(db, uuid, fs_name)
1996     def get_uuid(self):
1997         return self.osc.uuid
1998     def get_name(self):
1999         return self.osc.name
2000     def prepare(self):
2001         self.osc.prepare()
2002     def cleanup(self):
2003         self.osc.cleanup()
2004     def load_module(self):
2005         self.osc.load_module()
2006     def cleanup_module(self):
2007         self.osc.cleanup_module()
2008
2009
2010 class ECHO_CLIENT(Module):
2011     def __init__(self,db):
2012         Module.__init__(self, 'ECHO_CLIENT', db)
2013         self.add_lustre_module('obdecho', 'obdecho')
2014         self.obd_uuid = self.db.get_first_ref('obd')
2015         obd = self.db.lookup(self.obd_uuid)
2016         self.uuid = generate_client_uuid(self.name)
2017         self.osc = VOSC(obd, self.uuid, self.name)
2018
2019     def prepare(self):
2020         if is_prepared(self.name):
2021             return
2022         run_acceptors()
2023         self.osc.prepare() # XXX This is so cheating. -p
2024         self.info(self.obd_uuid)
2025
2026         lctl.newdev("echo_client", self.name, self.uuid,
2027                     setup = self.osc.get_name())
2028
2029     def cleanup(self):
2030         if is_prepared(self.name):
2031             Module.cleanup(self)
2032         self.osc.cleanup()
2033
2034     def load_module(self):
2035         self.osc.load_module()
2036         Module.load_module(self)
2037
2038     def cleanup_module(self):
2039         Module.cleanup_module(self)
2040         self.osc.cleanup_module()
2041
2042
2043 def generate_client_uuid(name):
2044         client_uuid = '%05x_%.19s_%05x%05x' % (int(random.random() * 1048576),
2045                                                name,
2046                                                int(random.random() * 1048576),
2047                                                int(random.random() * 1048576))
2048         return client_uuid[:36]
2049
2050
2051 def my_rstrip(s, chars):
2052     """my_rstrip(s, chars) -> strips any instances of the characters
2053     found in chars from the right side of string s"""
2054     # XXX required because python versions pre 2.2.3 don't allow
2055     #string.rstrip() to take alternate char lists
2056     import string
2057     ns=s
2058     try:
2059         ns = string.rstrip(s, '/')
2060     except TypeError, e:
2061         for i in range(len(s) - 1, 0, -1):
2062             if s[i] in chars:
2063                 continue
2064             else:
2065                 ns = s[0:i+1]
2066                 break
2067     return ns
2068
2069
2070 class Mountpoint(Module):
2071     def __init__(self,db):
2072         Module.__init__(self, 'MTPT', db)
2073         self.path = my_rstrip(self.db.get_val('path'), '/')
2074         self.clientoptions = self.db.get_val('clientoptions', '')
2075         self.fs_uuid = self.db.get_first_ref('filesystem')
2076         fs = self.db.lookup(self.fs_uuid)
2077         self.mds_uuid = fs.get_first_ref('mds')
2078         self.obd_uuid = fs.get_first_ref('obd')
2079         self.mgmt_uuid = fs.get_first_ref('mgmt')
2080         obd = self.db.lookup(self.obd_uuid)
2081         client_uuid = generate_client_uuid(self.name)
2082         self.vosc = VOSC(obd, client_uuid, self.name)
2083         self.mdc = get_mdc(db, client_uuid, self.name, self.mds_uuid)
2084
2085         self.add_lustre_module('mdc', 'mdc')
2086         self.add_lustre_module('llite', 'llite')
2087         if self.mgmt_uuid:
2088             self.mgmtcli = ManagementClient(db.lookup(self.mgmt_uuid),
2089                                             client_uuid)
2090         else:
2091             self.mgmtcli = None
2092
2093     def prepare(self):
2094         if fs_is_mounted(self.path):
2095             log(self.path, "already mounted.")
2096             return
2097         run_acceptors()
2098         if self.mgmtcli:
2099             self.mgmtcli.prepare()
2100         self.vosc.prepare()
2101         self.mdc.prepare()
2102         mdc_name = self.mdc.name
2103
2104         self.info(self.path, self.mds_uuid, self.obd_uuid)
2105         if config.record or config.lctl_dump:
2106             lctl.mount_option(local_node_name, self.vosc.get_name(), mdc_name)
2107             return
2108
2109         if config.clientoptions:
2110             if self.clientoptions:
2111                 self.clientoptions = self.clientoptions + ',' + config.clientoptions
2112             else:
2113                 self.clientoptions = config.clientoptions
2114         if self.clientoptions:
2115             self.clientoptions = ',' + self.clientoptions
2116             # Linux kernel will deal with async and not pass it to ll_fill_super,
2117             # so replace it with Lustre async
2118             self.clientoptions = string.replace(self.clientoptions, "async", "lasync")
2119
2120         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s%s %s %s" % \
2121               (self.vosc.get_name(), mdc_name, self.clientoptions, config.config, self.path)
2122         run("mkdir", self.path)
2123         ret, val = run(cmd)
2124         if ret:
2125             self.mdc.cleanup()            
2126             self.vosc.cleanup()
2127             panic("mount failed:", self.path, ":", string.join(val))
2128
2129     def cleanup(self):
2130         self.info(self.path, self.mds_uuid,self.obd_uuid)
2131
2132         if config.record or config.lctl_dump:
2133             lctl.del_mount_option(local_node_name)
2134         else:
2135             if fs_is_mounted(self.path):
2136                 if config.force:
2137                     (rc, out) = run("umount", "-f", self.path)
2138                 else:
2139                     (rc, out) = run("umount", self.path)
2140                 if rc:
2141                     raise CommandError('umount', out, rc)
2142
2143             if fs_is_mounted(self.path):
2144                 panic("fs is still mounted:", self.path)
2145
2146         self.mdc.cleanup()
2147         self.vosc.cleanup()
2148         if self.mgmtcli:
2149             self.mgmtcli.cleanup()
2150
2151     def load_module(self):
2152         if self.mgmtcli:
2153             self.mgmtcli.load_module()
2154         self.vosc.load_module()
2155         Module.load_module(self)
2156
2157     def cleanup_module(self):
2158         Module.cleanup_module(self)
2159         self.vosc.cleanup_module()
2160         if self.mgmtcli:
2161             self.mgmtcli.cleanup_module()
2162
2163
2164 # ============================================================
2165 # misc query functions
2166
2167 def get_ost_net(self, osd_uuid):
2168     srv_list = []
2169     if not osd_uuid:
2170         return srv_list
2171     osd = self.lookup(osd_uuid)
2172     node_uuid = osd.get_first_ref('node')
2173     node = self.lookup(node_uuid)
2174     if not node:
2175         panic("unable to find node for osd_uuid:", osd_uuid,
2176               " node_ref:", node_uuid)
2177     for net_uuid in node.get_networks():
2178         db = node.lookup(net_uuid)
2179         srv_list.append(Network(db))
2180     return srv_list
2181
2182
2183 # the order of iniitailization is based on level. 
2184 def getServiceLevel(self):
2185     type = self.get_class()
2186     ret=0;
2187     if type in ('network',):
2188         ret = 5
2189     elif type in ('routetbl',):
2190         ret = 6
2191     elif type in ('ldlm',):
2192         ret = 20
2193     elif type in ('mgmt',):
2194         ret = 25
2195     elif type in ('osd', 'cobd'):
2196         ret = 30
2197     elif type in ('mdsdev',):
2198         ret = 40
2199     elif type in ('mountpoint', 'echoclient'):
2200         ret = 70
2201     else:
2202         panic("Unknown type: ", type)
2203
2204     if ret < config.minlevel or ret > config.maxlevel:
2205         ret = 0 
2206     return ret
2207
2208 #
2209 # return list of services in a profile. list is a list of tuples
2210 # [(level, db_object),]
2211 def getServices(self):
2212     list = []
2213     for ref_class, ref_uuid in self.get_all_refs(): 
2214             servdb = self.lookup(ref_uuid)
2215             if  servdb:
2216                 level = getServiceLevel(servdb)
2217                 if level > 0:
2218                     list.append((level, servdb))
2219             else:
2220                 panic('service not found: ' + ref_uuid)
2221
2222     list.sort()
2223     return list
2224
2225
2226 ############################################################
2227 # MDC UUID hack - 
2228 # FIXME: clean this mess up!
2229 #
2230 # OSC is no longer in the xml, so we have to fake it.
2231 # this is getting ugly and begging for another refactoring
2232 def get_osc(ost_db, uuid, fs_name):
2233     osc = OSC(ost_db, uuid, fs_name)
2234     return osc
2235
2236 def get_mdc(db, uuid, fs_name, mds_uuid):
2237     mds_db = db.lookup(mds_uuid);
2238     if not mds_db:
2239         panic("no mds:", mds_uuid)
2240     mdc = MDC(mds_db, uuid, fs_name)
2241     return mdc
2242
2243 ############################################################
2244 # routing ("rooting")
2245
2246 # list of (nettype, cluster_id, nid)
2247 local_clusters = []
2248
2249 def find_local_clusters(node_db):
2250     global local_clusters
2251     for netuuid in node_db.get_networks():
2252         net = node_db.lookup(netuuid)
2253         srv = Network(net)
2254         debug("add_local", netuuid)
2255         local_clusters.append((srv.net_type, srv.cluster_id, srv.nid))
2256         if srv.port > 0:
2257             if acceptors.has_key(srv.port):
2258                 panic("duplicate port:", srv.port)
2259             acceptors[srv.port] = AcceptorHandler(srv.port, srv.net_type)
2260
2261 # This node is a gateway.
2262 is_router = 0
2263 def node_is_router():
2264     return is_router
2265
2266 # If there are any routers found in the config, then this will be true
2267 # and all nodes will load kptlrouter.
2268 needs_router = 0
2269 def node_needs_router():
2270     return needs_router or is_router
2271
2272 # list of (nettype, gw, tgt_cluster_id, lo, hi)
2273 # Currently, these local routes are only added to kptlrouter route
2274 # table if they are needed to connect to a specific server.  This
2275 # should be changed so all available routes are loaded, and the
2276 # ptlrouter can make all the decisions.
2277 local_routes = []
2278
2279 def find_local_routes(lustre):
2280     """ Scan the lustre config looking for routers .  Build list of
2281     routes. """
2282     global local_routes, needs_router
2283     local_routes = []
2284     list = lustre.lookup_class('node')
2285     for router in list:
2286         if router.get_val_int('router', 0):
2287             needs_router = 1
2288             for (local_type, local_cluster_id, local_nid) in local_clusters:
2289                 gw = None
2290                 for netuuid in router.get_networks():
2291                     db = router.lookup(netuuid)
2292                     if (local_type == db.get_val('nettype') and
2293                        local_cluster_id == db.get_val('clusterid')):
2294                         gw = db.get_val('nid')
2295                         break
2296                 if gw:
2297                     debug("find_local_routes: gw is", gw)
2298                     for route in router.get_local_routes(local_type, gw):
2299                         local_routes.append(route)
2300     debug("find_local_routes:", local_routes)
2301
2302
2303 def choose_local_server(srv_list):
2304     for srv in srv_list:
2305         if local_cluster(srv.net_type, srv.cluster_id):
2306             return srv
2307
2308 def local_cluster(net_type, cluster_id):
2309     for cluster in local_clusters:
2310         if net_type == cluster[0] and cluster_id == cluster[1]:
2311             return 1
2312     return 0
2313
2314 def local_interface(net_type, cluster_id, nid):
2315     for cluster in local_clusters:
2316         if (net_type == cluster[0] and cluster_id == cluster[1]
2317             and nid == cluster[2]):
2318             return 1
2319     return 0
2320
2321 def find_route(srv_list):
2322     result = []
2323     frm_type = local_clusters[0][0]
2324     for srv in srv_list:
2325         debug("find_route: srv:", srv.nid, "type: ", srv.net_type)
2326         to_type = srv.net_type
2327         to = srv.nid
2328         cluster_id = srv.cluster_id
2329         debug ('looking for route to', to_type, to)
2330         for r in local_routes:
2331             debug("find_route: ", r)
2332             if  (r[3] <= to and to <= r[4]) and cluster_id == r[2]:
2333                 result.append((srv, r))
2334     return result
2335            
2336 def get_active_target(db):
2337     target_uuid = db.getUUID()
2338     target_name = db.getName()
2339     node_name = get_select(target_name)
2340     if node_name:
2341         tgt_dev_uuid = db.get_node_tgt_dev(node_name, target_uuid)
2342     else:
2343         tgt_dev_uuid = db.get_first_ref('active')
2344     return tgt_dev_uuid
2345
2346 def get_server_by_nid_uuid(db,  nid_uuid):
2347     for n in db.lookup_class("network"):
2348         net = Network(n)
2349         if net.nid_uuid == nid_uuid:
2350             return net
2351         
2352
2353 ############################################################
2354 # lconf level logic
2355 # Start a service.
2356 def newService(db):
2357     type = db.get_class()
2358     debug('Service:', type, db.getName(), db.getUUID())
2359     n = None
2360     if type == 'ldlm':
2361         n = LDLM(db)
2362     elif type == 'lov':
2363         n = LOV(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2364     elif type == 'network':
2365         n = Network(db)
2366     elif type == 'routetbl':
2367         n = RouteTable(db)
2368     elif type == 'osd':
2369         n = OSD(db)
2370     elif type == 'cobd':
2371         n = COBD(db)
2372     elif type == 'mdsdev':
2373         n = MDSDEV(db)
2374     elif type == 'mountpoint':
2375         n = Mountpoint(db)
2376     elif type == 'echoclient':
2377         n = ECHO_CLIENT(db)
2378     elif type == 'mgmt':
2379         n = Management(db)
2380     else:
2381         panic ("unknown service type:", type)
2382     return n
2383
2384 #
2385 # Prepare the system to run lustre using a particular profile
2386 # in a the configuration. 
2387 #  * load & the modules
2388 #  * setup networking for the current node
2389 #  * make sure partitions are in place and prepared
2390 #  * initialize devices with lctl
2391 # Levels is important, and needs to be enforced.
2392 def for_each_profile(db, prof_list, operation):
2393     for prof_uuid in prof_list:
2394         prof_db = db.lookup(prof_uuid)
2395         if not prof_db:
2396             panic("profile:", prof_uuid, "not found.")
2397         services = getServices(prof_db)
2398         operation(services)
2399         
2400 def doWriteconf(services):
2401     if config.nosetup:
2402         return
2403     for s in services:
2404         if s[1].get_class() == 'mdsdev':
2405             n = newService(s[1])
2406             n.write_conf()
2407
2408 def doSetup(services):
2409     if config.nosetup:
2410         return
2411     for s in services:
2412         n = newService(s[1])
2413         n.prepare()
2414     
2415 def doModules(services):
2416     if config.nomod:
2417         return
2418     for s in services:
2419         n = newService(s[1])
2420         n.load_module()
2421
2422 def doCleanup(services):
2423     if config.nosetup:
2424         return
2425     services.reverse()
2426     for s in services:
2427         n = newService(s[1])
2428         if n.safe_to_clean():
2429             n.cleanup()
2430
2431 def doUnloadModules(services):
2432     if config.nomod:
2433         return
2434     services.reverse()
2435     for s in services:
2436         n = newService(s[1])
2437         if n.safe_to_clean_modules():
2438             n.cleanup_module()
2439
2440 #
2441 # Load profile for 
2442 def doHost(lustreDB, hosts):
2443     global is_router, local_node_name
2444     node_db = None
2445     for h in hosts:
2446         node_db = lustreDB.lookup_name(h, 'node')
2447         if node_db:
2448             break
2449     if not node_db:
2450         panic('No host entry found.')
2451
2452     local_node_name = node_db.get_val('name', 0)
2453     is_router = node_db.get_val_int('router', 0)
2454     lustre_upcall = node_db.get_val('lustreUpcall', '')
2455     portals_upcall = node_db.get_val('portalsUpcall', '')
2456     timeout = node_db.get_val_int('timeout', 0)
2457     ptldebug = node_db.get_val('ptldebug', '')
2458     subsystem = node_db.get_val('subsystem', '')
2459     
2460     find_local_clusters(node_db)
2461     if not is_router:
2462         find_local_routes(lustreDB)
2463
2464     # Two step process: (1) load modules, (2) setup lustre
2465     # if not cleaning, load modules first.
2466     prof_list = node_db.get_refs('profile')
2467
2468     if config.write_conf:
2469         for_each_profile(node_db, prof_list, doModules)
2470         sys_make_devices()
2471         for_each_profile(node_db, prof_list, doWriteconf)
2472         for_each_profile(node_db, prof_list, doUnloadModules)
2473         lustreDB.close()
2474
2475     elif config.recover:
2476         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
2477             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
2478                                      "--client_uuid <UUID> --conn_uuid <UUID>")
2479         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
2480                    config.conn_uuid)
2481     elif config.cleanup:
2482         if config.force:
2483             # the command line can override this value
2484             timeout = 5
2485         # ugly hack, only need to run lctl commands for --dump
2486         if config.lctl_dump or config.record:
2487             for_each_profile(node_db, prof_list, doCleanup)
2488             return
2489
2490         sys_set_timeout(timeout)
2491         sys_set_ptldebug(ptldebug)
2492         sys_set_subsystem(subsystem)
2493         sys_set_lustre_upcall(lustre_upcall)
2494         sys_set_portals_upcall(portals_upcall)
2495
2496         for_each_profile(node_db, prof_list, doCleanup)
2497         for_each_profile(node_db, prof_list, doUnloadModules)
2498         lustreDB.close()
2499
2500     else:
2501         # ugly hack, only need to run lctl commands for --dump
2502         if config.lctl_dump or config.record:
2503             sys_set_timeout(timeout)
2504             sys_set_lustre_upcall(lustre_upcall)
2505             for_each_profile(node_db, prof_list, doSetup)
2506             return
2507
2508         sys_make_devices()
2509         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
2510         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
2511
2512         for_each_profile(node_db, prof_list, doModules)
2513
2514         sys_set_debug_path()
2515         sys_set_ptldebug(ptldebug)
2516         sys_set_subsystem(subsystem)
2517         script = config.gdb_script
2518         run(lctl.lctl, ' modules >', script)
2519         if config.gdb:
2520             log ("The GDB module script is in", script)
2521             # pause, so user has time to break and
2522             # load the script
2523             time.sleep(5)
2524         sys_set_timeout(timeout)
2525         sys_set_lustre_upcall(lustre_upcall)
2526         sys_set_portals_upcall(portals_upcall)
2527
2528         for_each_profile(node_db, prof_list, doSetup)
2529         lustreDB.close()
2530
2531 def doRecovery(lustreDB, lctl, tgt_uuid, client_uuid, nid_uuid):
2532     tgt = lustreDB.lookup(tgt_uuid)
2533     if not tgt:
2534         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
2535     new_uuid = get_active_target(tgt)
2536     if not new_uuid:
2537         raise Lustre.LconfError("doRecovery: no active target found for: " +
2538                                 tgt_uuid)
2539     net = choose_local_server(get_ost_net(lustreDB, new_uuid))
2540     if not net:
2541         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
2542
2543     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
2544     try:
2545         oldnet = get_server_by_nid_uuid(lustreDB, nid_uuid)
2546         lustreDB.close()
2547         if oldnet:
2548             lctl.disconnect(oldnet)
2549     except CommandError, e:
2550         log("recover: disconnect", nid_uuid, "failed: ")
2551         e.dump()
2552
2553     try:
2554         lctl.connect(net)
2555     except CommandError, e:
2556         log("recover: connect failed")
2557         e.dump()
2558
2559     lctl.recover(client_uuid, net.nid_uuid)
2560
2561
2562 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
2563     base = os.path.dirname(cmd)
2564     if development_mode():
2565         if not config.lustre:
2566             debug('using objdir module paths')            
2567             config.lustre = (os.path.join(base, ".."))
2568         # normalize the portals dir, using command line arg if set
2569         if config.portals:
2570             portals_dir = config.portals
2571         dir = os.path.join(config.lustre, portals_dir)
2572         config.portals = dir
2573         debug('config.portals', config.portals)
2574     elif config.lustre and config.portals:
2575         # production mode
2576         # if --lustre and --portals, normalize portals 
2577         # can ignore POTRALS_DIR here, since it is probly useless here
2578         config.portals = os.path.join(config.lustre, config.portals)
2579         debug('config.portals B', config.portals)
2580
2581 def sysctl(path, val):
2582     debug("+ sysctl", path, val)
2583     if config.noexec:
2584         return
2585     try:
2586         fp = open(os.path.join('/proc/sys', path), 'w')
2587         fp.write(str(val))
2588         fp.close()
2589     except IOError, e:
2590         panic(str(e))
2591
2592
2593 def sys_set_debug_path():
2594     sysctl('portals/debug_path', config.debug_path)
2595
2596 def sys_set_lustre_upcall(upcall):
2597     # the command overrides the value in the node config
2598     if config.lustre_upcall:
2599         upcall = config.lustre_upcall
2600     elif config.upcall:
2601         upcall = config.upcall
2602     if upcall:
2603         lctl.set_lustre_upcall(upcall)
2604
2605 def sys_set_portals_upcall(upcall):
2606     # the command overrides the value in the node config
2607     if config.portals_upcall:
2608         upcall = config.portals_upcall
2609     elif config.upcall:
2610         upcall = config.upcall
2611     if upcall:
2612         sysctl('portals/upcall', upcall)
2613
2614 def sys_set_timeout(timeout):
2615     # the command overrides the value in the node config
2616     if config.timeout and config.timeout > 0:
2617         timeout = config.timeout
2618     if timeout != None and timeout > 0:
2619         lctl.set_timeout(timeout)
2620
2621 def sys_tweak_socknal ():
2622     if config.single_socket:
2623         sysctl("socknal/typed", 0)
2624
2625 def sys_optimize_elan ():
2626     procfiles = ["/proc/elan/config/eventint_punt_loops",
2627                  "/proc/qsnet/elan3/config/eventint_punt_loops",
2628                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
2629     for p in procfiles:
2630         if os.access(p, os.W_OK):
2631             run ("echo 1 > " + p)
2632
2633 def sys_set_ptldebug(ptldebug):
2634     if config.ptldebug:
2635         ptldebug = config.ptldebug
2636     if ptldebug:
2637         try:
2638             val = eval(ptldebug, ptldebug_names)
2639             val = "0x%x" % (val)
2640             sysctl('portals/debug', val)
2641         except NameError, e:
2642             panic(str(e))
2643
2644 def sys_set_subsystem(subsystem):
2645     if config.subsystem:
2646         subsystem = config.subsystem
2647     if subsystem:
2648         try:
2649             val = eval(subsystem, subsystem_names)
2650             val = "0x%x" % (val)
2651             sysctl('portals/subsystem_debug', val)
2652         except NameError, e:
2653             panic(str(e))
2654
2655 def sys_set_netmem_max(path, max):
2656     debug("setting", path, "to at least", max)
2657     if config.noexec:
2658         return
2659     fp = open(path)
2660     str = fp.readline()
2661     fp.close()
2662     cur = int(str)
2663     if max > cur:
2664         fp = open(path, 'w')
2665         fp.write('%d\n' %(max))
2666         fp.close()
2667     
2668     
2669 def sys_make_devices():
2670     if not os.access('/dev/portals', os.R_OK):
2671         run('mknod /dev/portals c 10 240')
2672     if not os.access('/dev/obd', os.R_OK):
2673         run('mknod /dev/obd c 10 241')
2674
2675
2676 # Add dir to the global PATH, if not already there.
2677 def add_to_path(new_dir):
2678     syspath = string.split(os.environ['PATH'], ':')
2679     if new_dir in syspath:
2680         return
2681     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
2682     
2683 def default_debug_path():
2684     path = '/tmp/lustre-log'
2685     if os.path.isdir('/r'):
2686         return '/r' + path
2687     else:
2688         return path
2689
2690 def default_gdb_script():
2691     script = '/tmp/ogdb'
2692     if os.path.isdir('/r'):
2693         return '/r' + script
2694     else:
2695         return script
2696
2697
2698 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
2699 # ensure basic elements are in the system path
2700 def sanitise_path():
2701     for dir in DEFAULT_PATH:
2702         add_to_path(dir)
2703
2704 # global hack for the --select handling
2705 tgt_select = {}
2706 def init_select(args):
2707     # args = [service=nodeA,service2=nodeB service3=nodeC]
2708     global tgt_select
2709     for arg in args:
2710         list = string.split(arg, ',')
2711         for entry in list:
2712             srv, node = string.split(entry, '=')
2713             tgt_select[srv] = node
2714
2715 def get_select(srv):
2716     if tgt_select.has_key(srv):
2717         return tgt_select[srv]
2718     return None
2719
2720
2721 FLAG = Lustre.Options.FLAG
2722 PARAM = Lustre.Options.PARAM
2723 INTPARAM = Lustre.Options.INTPARAM
2724 PARAMLIST = Lustre.Options.PARAMLIST
2725 lconf_options = [
2726     ('verbose,v', "Print system commands as they are run"),
2727     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
2728     ('config', "Cluster config name used for LDAP query", PARAM),
2729     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
2730     ('node',   "Load config for <nodename>", PARAM),
2731     ('cleanup,d', "Cleans up config. (Shutdown)"),
2732     ('force,f', "Forced unmounting and/or obd detach during cleanup",
2733                FLAG, 0),
2734     ('single_socket', "socknal option: only use one socket instead of bundle",
2735                FLAG, 0),
2736     ('failover',"""Used to shut down without saving state.
2737                    This will allow this node to "give up" a service to a
2738                    another node for failover purposes. This will not
2739                    be a clean shutdown.""",
2740                FLAG, 0),
2741     ('gdb', """Prints message after creating gdb module script
2742                     and sleeps for 5 seconds."""),
2743     ('noexec,n', """Prints the commands and steps that will be run for a
2744                     config without executing them. This can used to check if a
2745                     config file is doing what it should be doing"""),
2746     ('nomod', "Skip load/unload module step."),
2747     ('nosetup', "Skip device setup/cleanup step."),
2748     ('reformat', "Reformat all devices (without question)"),
2749     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
2750     ('mountfsoptions', "Additional options for mount fs command line", PARAM),
2751     ('clientoptions', "Additional options for Lustre", PARAM),
2752     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
2753                PARAM),
2754     ('write_conf', "Save all the client config information on mds."),
2755     ('record', "Write config information on mds."),
2756     ('record_log', "Name of config record log.", PARAM),
2757     ('record_device', "MDS device name that will record the config commands",
2758               PARAM),
2759     ('minlevel', "Minimum level of services to configure/cleanup",
2760                  INTPARAM, 0),
2761     ('maxlevel', """Maximum level of services to configure/cleanup 
2762                     Levels are aproximatly like:
2763                             10 - netwrk
2764                             20 - device, ldlm
2765                             30 - osd, mdd
2766                             40 - mds, ost
2767                             70 - mountpoint, echo_client, osc, mdc, lov""",
2768                INTPARAM, 100),
2769     ('lustre', """Base directory of lustre sources. This parameter will
2770                   cause lconf to load modules from a source tree.""", PARAM),
2771     ('portals', """Portals source directory.  If this is a relative path,
2772                    then it is assumed to be relative to lustre. """, PARAM),
2773     ('timeout', "Set recovery timeout", INTPARAM),
2774     ('upcall',  "Set both portals and lustre upcall script", PARAM),
2775     ('lustre_upcall', "Set lustre upcall script", PARAM),
2776     ('portals_upcall', "Set portals upcall script", PARAM),
2777     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
2778     ('ptldebug', "Set the portals debug level",  PARAM),
2779     ('subsystem', "Set the portals debug subsystem",  PARAM),
2780     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
2781     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
2782 # Client recovery options
2783     ('recover', "Recover a device"),
2784     ('group', "The group of devices to configure or cleanup", PARAM),
2785     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
2786     ('client_uuid', "The failed client (required for recovery)", PARAM),
2787     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
2788
2789     ('inactive', """The name of an inactive service, to be ignored during
2790                     mounting (currently OST-only). Can be repeated.""",
2791                 PARAMLIST),
2792     ]      
2793
2794 def main():
2795     global lctl, config, toplustreDB, CONFIG_FILE
2796
2797     # in the upcall this is set to SIG_IGN
2798     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
2799     
2800     cl = Lustre.Options("lconf", "config.xml", lconf_options)
2801     try:
2802         config, args = cl.parse(sys.argv[1:])
2803     except Lustre.OptionError, e:
2804         print e
2805         sys.exit(1)
2806
2807     setupModulePath(sys.argv[0])
2808
2809     host = socket.gethostname()
2810
2811     # the PRNG is normally seeded with time(), which is not so good for starting
2812     # time-synchronized clusters
2813     input = open('/dev/urandom', 'r')
2814     if not input:
2815         print 'Unable to open /dev/urandom!'
2816         sys.exit(1)
2817     seed = input.read(32)
2818     input.close()
2819     random.seed(seed)
2820
2821     sanitise_path()
2822     
2823     init_select(config.select)
2824
2825     if len(args) > 0:
2826         # allow config to be fetched via HTTP, but only with python2
2827         if sys.version[0] != '1' and args[0].startswith('http://'):
2828             import urllib2
2829             try:
2830                 config_file = urllib2.urlopen(args[0])
2831             except (urllib2.URLError, socket.error), err:
2832                 if hasattr(err, 'args'):
2833                     err = err.args[1]
2834                 print "Could not access '%s': %s" %(args[0], err)
2835                 sys.exit(1)
2836         elif not os.access(args[0], os.R_OK):
2837             print 'File not found or readable:', args[0]
2838             sys.exit(1)
2839         else:
2840             # regular file
2841             config_file = open(args[0], 'r')
2842         try:
2843             dom = xml.dom.minidom.parse(config_file)
2844         except Exception:
2845             panic("%s does not appear to be a config file." % (args[0]))
2846             sys.exit(1) # make sure to die here, even in debug mode.
2847         config_file.close()
2848         CONFIG_FILE = args[0]
2849         lustreDB = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
2850         if not config.config:
2851             config.config = os.path.basename(args[0])# use full path?
2852             if config.config[-4:] == '.xml':
2853                 config.config = config.config[:-4]
2854     elif config.ldapurl:
2855         if not config.config:
2856             panic("--ldapurl requires --config name")
2857         dn = "config=%s,fs=lustre" % (config.config)
2858         lustreDB = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
2859     elif config.ptldebug or config.subsystem:
2860         sys_set_ptldebug(None)
2861         sys_set_subsystem(None)
2862         sys.exit(0)
2863     else:
2864         print 'Missing config file or ldap URL.'
2865         print 'see lconf --help for command summary'
2866         sys.exit(1)
2867
2868     toplustreDB = lustreDB
2869
2870     ver = lustreDB.get_version()
2871     if not ver:
2872         panic("No version found in config data, please recreate.")
2873     if ver != Lustre.CONFIG_VERSION:
2874         panic("Config version", ver, "does not match lconf version",
2875               Lustre.CONFIG_VERSION)
2876
2877     node_list = []
2878     if config.node:
2879         node_list.append(config.node)
2880     else:
2881         if len(host) > 0:
2882             node_list.append(host)
2883         node_list.append('localhost')
2884
2885     debug("configuring for host: ", node_list)
2886
2887     if len(host) > 0:
2888         config.debug_path = config.debug_path + '-' + host
2889         config.gdb_script = config.gdb_script + '-' + host
2890
2891     lctl = LCTLInterface('lctl')
2892
2893     if config.lctl_dump:
2894         lctl.use_save_file(config.lctl_dump)
2895
2896     if config.record:
2897         if not (config.record_device and config.record_log):
2898             panic("When recording, both --record_log and --record_device must be specified.")
2899         lctl.clear_log(config.record_device, config.record_log)
2900         lctl.record(config.record_device, config.record_log)
2901
2902     doHost(lustreDB, node_list)
2903
2904     if config.record:
2905         lctl.end_record()
2906
2907 if __name__ == "__main__":
2908     try:
2909         main()
2910     except Lustre.LconfError, e:
2911         print e
2912 #        traceback.print_exc(file=sys.stdout)
2913         sys.exit(1)
2914     except CommandError, e:
2915         e.dump()
2916         sys.exit(e.rc)
2917
2918     if first_cleanup_error:
2919         sys.exit(first_cleanup_error)