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