Whamcloud - gitweb
Default mount options stuff.
[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', 65536)
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 = toplevel.lookup_name(mtpt_name)
1853     fs_uuid = mtpt_db.get_first_ref('filesystem')
1854     fs = toplevel.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         for_each_profile(node_db, prof_list, doModules)
2440         sys_make_devices()
2441         for_each_profile(node_db, prof_list, doWriteconf)
2442         for_each_profile(node_db, prof_list, doUnloadModules)
2443
2444     elif config.recover:
2445         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
2446             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
2447                                      "--client_uuid <UUID> --conn_uuid <UUID>")
2448         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
2449                    config.conn_uuid)
2450     elif config.cleanup:
2451         if config.force:
2452             # the command line can override this value
2453             timeout = 5
2454         # ugly hack, only need to run lctl commands for --dump
2455         if config.lctl_dump or config.record:
2456             for_each_profile(node_db, prof_list, doCleanup)
2457             return
2458
2459         sys_set_timeout(timeout)
2460         sys_set_ptldebug(ptldebug)
2461         sys_set_subsystem(subsystem)
2462         sys_set_lustre_upcall(lustre_upcall)
2463         sys_set_portals_upcall(portals_upcall)
2464
2465         for_each_profile(node_db, prof_list, doCleanup)
2466         for_each_profile(node_db, prof_list, doUnloadModules)
2467
2468     else:
2469         # ugly hack, only need to run lctl commands for --dump
2470         if config.lctl_dump or config.record:
2471             sys_set_timeout(timeout)
2472             sys_set_lustre_upcall(lustre_upcall)
2473             for_each_profile(node_db, prof_list, doSetup)
2474             return
2475
2476         sys_make_devices()
2477         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
2478         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
2479
2480         for_each_profile(node_db, prof_list, doModules)
2481
2482         sys_set_debug_path()
2483         sys_set_ptldebug(ptldebug)
2484         sys_set_subsystem(subsystem)
2485         script = config.gdb_script
2486         run(lctl.lctl, ' modules >', script)
2487         if config.gdb:
2488             log ("The GDB module script is in", script)
2489             # pause, so user has time to break and
2490             # load the script
2491             time.sleep(5)
2492         sys_set_timeout(timeout)
2493         sys_set_lustre_upcall(lustre_upcall)
2494         sys_set_portals_upcall(portals_upcall)
2495
2496         for_each_profile(node_db, prof_list, doSetup)
2497
2498 def doRecovery(db, lctl, tgt_uuid, client_uuid, nid_uuid):
2499     tgt = db.lookup(tgt_uuid)
2500     if not tgt:
2501         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
2502     new_uuid = get_active_target(tgt)
2503     if not new_uuid:
2504         raise Lustre.LconfError("doRecovery: no active target found for: " +
2505                                 tgt_uuid)
2506     net = choose_local_server(get_ost_net(db, new_uuid))
2507     if not net:
2508         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
2509
2510     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
2511     try:
2512         oldnet = get_server_by_nid_uuid(db, nid_uuid)
2513         if oldnet:
2514             lctl.disconnect(oldnet)
2515     except CommandError, e:
2516         log("recover: disconnect", nid_uuid, "failed: ")
2517         e.dump()
2518
2519     try:
2520         lctl.connect(net)
2521     except CommandError, e:
2522         log("recover: connect failed")
2523         e.dump()
2524
2525     lctl.recover(client_uuid, net.nid_uuid)
2526
2527
2528 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
2529     base = os.path.dirname(cmd)
2530     if development_mode():
2531         if not config.lustre:
2532             debug('using objdir module paths')            
2533             config.lustre = (os.path.join(base, ".."))
2534         # normalize the portals dir, using command line arg if set
2535         if config.portals:
2536             portals_dir = config.portals
2537         dir = os.path.join(config.lustre, portals_dir)
2538         config.portals = dir
2539         debug('config.portals', config.portals)
2540     elif config.lustre and config.portals:
2541         # production mode
2542         # if --lustre and --portals, normalize portals 
2543         # can ignore POTRALS_DIR here, since it is probly useless here
2544         config.portals = os.path.join(config.lustre, config.portals)
2545         debug('config.portals B', config.portals)
2546
2547 def sysctl(path, val):
2548     debug("+ sysctl", path, val)
2549     if config.noexec:
2550         return
2551     try:
2552         fp = open(os.path.join('/proc/sys', path), 'w')
2553         fp.write(str(val))
2554         fp.close()
2555     except IOError, e:
2556         panic(str(e))
2557
2558
2559 def sys_set_debug_path():
2560     sysctl('portals/debug_path', config.debug_path)
2561
2562 def sys_set_lustre_upcall(upcall):
2563     # the command overrides the value in the node config
2564     if config.lustre_upcall:
2565         upcall = config.lustre_upcall
2566     elif config.upcall:
2567         upcall = config.upcall
2568     if upcall:
2569         lctl.set_lustre_upcall(upcall)
2570
2571 def sys_set_portals_upcall(upcall):
2572     # the command overrides the value in the node config
2573     if config.portals_upcall:
2574         upcall = config.portals_upcall
2575     elif config.upcall:
2576         upcall = config.upcall
2577     if upcall:
2578         sysctl('portals/upcall', upcall)
2579
2580 def sys_set_timeout(timeout):
2581     # the command overrides the value in the node config
2582     if config.timeout and config.timeout > 0:
2583         timeout = config.timeout
2584     if timeout != None and timeout > 0:
2585         lctl.set_timeout(timeout)
2586
2587 def sys_tweak_socknal ():
2588     if config.single_socket:
2589         sysctl("socknal/typed", 0)
2590
2591 def sys_optimize_elan ():
2592     procfiles = ["/proc/elan/config/eventint_punt_loops",
2593                  "/proc/qsnet/elan3/config/eventint_punt_loops",
2594                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
2595     for p in procfiles:
2596         if os.access(p, os.R_OK):
2597             run ("echo 0 > " + p)
2598
2599 def sys_set_ptldebug(ptldebug):
2600     if config.ptldebug:
2601         ptldebug = config.ptldebug
2602     if ptldebug:
2603         try:
2604             val = eval(ptldebug, ptldebug_names)
2605             val = "0x%x" % (val)
2606             sysctl('portals/debug', val)
2607         except NameError, e:
2608             panic(str(e))
2609
2610 def sys_set_subsystem(subsystem):
2611     if config.subsystem:
2612         subsystem = config.subsystem
2613     if subsystem:
2614         try:
2615             val = eval(subsystem, subsystem_names)
2616             val = "0x%x" % (val)
2617             sysctl('portals/subsystem_debug', val)
2618         except NameError, e:
2619             panic(str(e))
2620
2621 def sys_set_netmem_max(path, max):
2622     debug("setting", path, "to at least", max)
2623     if config.noexec:
2624         return
2625     fp = open(path)
2626     str = fp.readline()
2627     fp.close()
2628     cur = int(str)
2629     if max > cur:
2630         fp = open(path, 'w')
2631         fp.write('%d\n' %(max))
2632         fp.close()
2633     
2634     
2635 def sys_make_devices():
2636     if not os.access('/dev/portals', os.R_OK):
2637         run('mknod /dev/portals c 10 240')
2638     if not os.access('/dev/obd', os.R_OK):
2639         run('mknod /dev/obd c 10 241')
2640
2641
2642 # Add dir to the global PATH, if not already there.
2643 def add_to_path(new_dir):
2644     syspath = string.split(os.environ['PATH'], ':')
2645     if new_dir in syspath:
2646         return
2647     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
2648     
2649 def default_debug_path():
2650     path = '/tmp/lustre-log'
2651     if os.path.isdir('/r'):
2652         return '/r' + path
2653     else:
2654         return path
2655
2656 def default_gdb_script():
2657     script = '/tmp/ogdb'
2658     if os.path.isdir('/r'):
2659         return '/r' + script
2660     else:
2661         return script
2662
2663
2664 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
2665 # ensure basic elements are in the system path
2666 def sanitise_path():
2667     for dir in DEFAULT_PATH:
2668         add_to_path(dir)
2669
2670 # global hack for the --select handling
2671 tgt_select = {}
2672 def init_select(args):
2673     # args = [service=nodeA,service2=nodeB service3=nodeC]
2674     global tgt_select
2675     for arg in args:
2676         list = string.split(arg, ',')
2677         for entry in list:
2678             srv, node = string.split(entry, '=')
2679             tgt_select[srv] = node
2680
2681 def get_select(srv):
2682     if tgt_select.has_key(srv):
2683         return tgt_select[srv]
2684     return None
2685
2686
2687 FLAG = Lustre.Options.FLAG
2688 PARAM = Lustre.Options.PARAM
2689 INTPARAM = Lustre.Options.INTPARAM
2690 PARAMLIST = Lustre.Options.PARAMLIST
2691 lconf_options = [
2692     ('verbose,v', "Print system commands as they are run"),
2693     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
2694     ('config', "Cluster config name used for LDAP query", PARAM),
2695     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
2696     ('node',   "Load config for <nodename>", PARAM),
2697     ('cleanup,d', "Cleans up config. (Shutdown)"),
2698     ('force,f', "Forced unmounting and/or obd detach during cleanup",
2699                FLAG, 0),
2700     ('single_socket', "socknal option: only use one socket instead of bundle",
2701                FLAG, 0),
2702     ('failover',"""Used to shut down without saving state.
2703                    This will allow this node to "give up" a service to a
2704                    another node for failover purposes. This will not
2705                    be a clean shutdown.""",
2706                FLAG, 0),
2707     ('gdb', """Prints message after creating gdb module script
2708                     and sleeps for 5 seconds."""),
2709     ('noexec,n', """Prints the commands and steps that will be run for a
2710                     config without executing them. This can used to check if a
2711                     config file is doing what it should be doing"""),
2712     ('nomod', "Skip load/unload module step."),
2713     ('nosetup', "Skip device setup/cleanup step."),
2714     ('reformat', "Reformat all devices (without question)"),
2715     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
2716     ('mountfsoptions', "Additional options for mount fs command line", PARAM),
2717     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
2718                PARAM),
2719     ('write_conf', "Save all the client config information on mds."),
2720     ('record', "Write config information on mds."),
2721     ('record_log', "Name of config record log.", PARAM),
2722     ('record_device', "MDS device name that will record the config commands",
2723               PARAM),
2724     ('minlevel', "Minimum level of services to configure/cleanup",
2725                  INTPARAM, 0),
2726     ('maxlevel', """Maximum level of services to configure/cleanup 
2727                     Levels are aproximatly like:
2728                             10 - netwrk
2729                             20 - device, ldlm
2730                             30 - osd, mdd
2731                             40 - mds, ost
2732                             70 - mountpoint, echo_client, osc, mdc, lov""",
2733                INTPARAM, 100),
2734     ('lustre', """Base directory of lustre sources. This parameter will
2735                   cause lconf to load modules from a source tree.""", PARAM),
2736     ('portals', """Portals source directory.  If this is a relative path,
2737                    then it is assumed to be relative to lustre. """, PARAM),
2738     ('timeout', "Set recovery timeout", INTPARAM),
2739     ('upcall',  "Set both portals and lustre upcall script", PARAM),
2740     ('lustre_upcall', "Set lustre upcall script", PARAM),
2741     ('portals_upcall', "Set portals upcall script", PARAM),
2742     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
2743     ('ptldebug', "Set the portals debug level",  PARAM),
2744     ('subsystem', "Set the portals debug subsystem",  PARAM),
2745     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
2746     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
2747 # Client recovery options
2748     ('recover', "Recover a device"),
2749     ('group', "The group of devices to configure or cleanup", PARAM),
2750     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
2751     ('client_uuid', "The failed client (required for recovery)", PARAM),
2752     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
2753
2754     ('inactive', """The name of an inactive service, to be ignored during
2755                     mounting (currently OST-only). Can be repeated.""",
2756                 PARAMLIST),
2757     ]      
2758
2759 def main():
2760     global lctl, config, toplevel, CONFIG_FILE
2761
2762     # in the upcall this is set to SIG_IGN
2763     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
2764     
2765     cl = Lustre.Options("lconf", "config.xml", lconf_options)
2766     try:
2767         config, args = cl.parse(sys.argv[1:])
2768     except Lustre.OptionError, e:
2769         print e
2770         sys.exit(1)
2771
2772     setupModulePath(sys.argv[0])
2773
2774     host = socket.gethostname()
2775
2776     # the PRNG is normally seeded with time(), which is not so good for starting
2777     # time-synchronized clusters
2778     input = open('/dev/urandom', 'r')
2779     if not input:
2780         print 'Unable to open /dev/urandom!'
2781         sys.exit(1)
2782     seed = input.read(32)
2783     input.close()
2784     random.seed(seed)
2785
2786     sanitise_path()
2787     
2788     init_select(config.select)
2789
2790     if len(args) > 0:
2791         # allow config to be fetched via HTTP, but only with python2
2792         if sys.version[0] != '1' and args[0].startswith('http://'):
2793             import urllib2
2794             try:
2795                 config_file = urllib2.urlopen(args[0])
2796             except (urllib2.URLError, socket.error), err:
2797                 if hasattr(err, 'args'):
2798                     err = err.args[1]
2799                 print "Could not access '%s': %s" %(args[0], err)
2800                 sys.exit(1)
2801         elif not os.access(args[0], os.R_OK):
2802             print 'File not found or readable:', args[0]
2803             sys.exit(1)
2804         else:
2805             # regular file
2806             config_file = open(args[0], 'r')
2807         try:
2808             dom = xml.dom.minidom.parse(config_file)
2809         except Exception:
2810             panic("%s does not appear to be a config file." % (args[0]))
2811             sys.exit(1) # make sure to die here, even in debug mode.
2812         CONFIG_FILE = args[0]
2813         db = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
2814         if not config.config:
2815             config.config = os.path.basename(args[0])# use full path?
2816             if config.config[-4:] == '.xml':
2817                 config.config = config.config[:-4]
2818     elif config.ldapurl:
2819         if not config.config:
2820             panic("--ldapurl requires --config name")
2821         dn = "config=%s,fs=lustre" % (config.config)
2822         db = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
2823     elif config.ptldebug or config.subsystem:
2824         sys_set_ptldebug(None)
2825         sys_set_subsystem(None)
2826         sys.exit(0)
2827     else:
2828         print 'Missing config file or ldap URL.'
2829         print 'see lconf --help for command summary'
2830         sys.exit(1)
2831
2832     toplevel = db
2833
2834     ver = db.get_version()
2835     if not ver:
2836         panic("No version found in config data, please recreate.")
2837     if ver != Lustre.CONFIG_VERSION:
2838         panic("Config version", ver, "does not match lconf version",
2839               Lustre.CONFIG_VERSION)
2840
2841     node_list = []
2842     if config.node:
2843         node_list.append(config.node)
2844     else:
2845         if len(host) > 0:
2846             node_list.append(host)
2847         node_list.append('localhost')
2848
2849     debug("configuring for host: ", node_list)
2850
2851     if len(host) > 0:
2852         config.debug_path = config.debug_path + '-' + host
2853         config.gdb_script = config.gdb_script + '-' + host
2854
2855     lctl = LCTLInterface('lctl')
2856
2857     if config.lctl_dump:
2858         lctl.use_save_file(config.lctl_dump)
2859
2860     if config.record:
2861         if not (config.record_device and config.record_log):
2862             panic("When recording, both --record_log and --record_device must be specified.")
2863         lctl.clear_log(config.record_device, config.record_log)
2864         lctl.record(config.record_device, config.record_log)
2865
2866     doHost(db, node_list)
2867
2868     if config.record:
2869         lctl.end_record()
2870
2871 if __name__ == "__main__":
2872     try:
2873         main()
2874     except Lustre.LconfError, e:
2875         print e
2876 #        traceback.print_exc(file=sys.stdout)
2877         sys.exit(1)
2878     except CommandError, e:
2879         e.dump()
2880         sys.exit(e.rc)
2881
2882     if first_cleanup_error:
2883         sys.exit(first_cleanup_error)