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