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