Whamcloud - gitweb
smash the HEAD with the contents of b_cmd. HEAD_PRE_CMD_SMASH and
[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.mds_uuid = self.db.get_first_ref('mds')
1384         mds = self.db.lookup(self.mds_uuid)
1385         self.lmv_name = mds.getName()
1386         self.devlist = self.db.get_refs('mds')
1387         self.mdclist = []
1388         self.desc_uuid = self.uuid
1389         self.uuid = uuid
1390         self.fs_name = fs_name
1391         for mds_uuid in self.devlist:
1392             mds = self.db.lookup(mds_uuid)
1393             if not mds:
1394                 panic("MDS not found!")
1395             mdc = MDC(mds, self.uuid, fs_name)
1396             if mdc:
1397                  self.mdclist.append(mdc)
1398             else:
1399                  panic('mdc not found:', mds_uuid)
1400             
1401     def prepare(self):
1402         if is_prepared(self.name):
1403             return
1404         for mdc in self.mdclist:
1405             try:
1406                 # Only ignore connect failures with --force, which
1407                 # isn't implemented here yet.
1408                 mdc.prepare(ignore_connect_failure=0)
1409             except CommandError, e:
1410                 print "Error preparing LMV %s\n" % mdc.uuid
1411                 raise e
1412         self.info(self.mds_uuid)
1413         lctl.lmv_setup(self.name, self.uuid, self.desc_uuid,
1414                        string.join(self.devlist))
1415
1416     def cleanup(self):
1417         for mdc in self.mdclist:
1418             mdc.cleanup()
1419         if is_prepared(self.name):
1420             Module.cleanup(self)
1421
1422     def load_module(self):
1423         for mdc in self.mdclist:
1424             mdc.load_module()
1425             break
1426         Module.load_module(self)
1427
1428     def cleanup_module(self):
1429         Module.cleanup_module(self)
1430         for mds in self.mdclist:
1431             mdc.cleanup_module()
1432             break
1433
1434     def correct_level(self, level, op=None):
1435         return level
1436
1437 class MDSDEV(Module):
1438     def __init__(self,db):
1439         Module.__init__(self, 'MDSDEV', db)
1440         self.devpath = self.db.get_val('devpath','')
1441         self.size = self.db.get_val_int('devsize', 0)
1442         self.journal_size = self.db.get_val_int('journalsize', 0)
1443         self.fstype = self.db.get_val('fstype', '')
1444         self.nspath = self.db.get_val('nspath', '')
1445         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1446         # overwrite the orignal MDSDEV name and uuid with the MDS name and uuid
1447         target_uuid = self.db.get_first_ref('target')
1448         mds = self.db.lookup(target_uuid)
1449         self.name = mds.getName()
1450         self.filesystem_uuids = mds.get_refs('filesystem')
1451         self.lmv_uuid = ''
1452         self.master_mds = ""
1453         if not self.filesystem_uuids:
1454             self.lmv_uuid = self.db.get_first_ref('lmv')
1455             if not self.lmv_uuid:
1456                 panic("ALERT: can't find lvm uuid")
1457             if self.lmv_uuid:
1458                 self.lmv = self.db.lookup(self.lmv_uuid)
1459                 if self.lmv:
1460                     self.filesystem_uuids = self.lmv.get_refs('filesystem')
1461                     self.master_mds = self.lmv_uuid
1462         # FIXME: if fstype not set, then determine based on kernel version
1463         self.format = self.db.get_val('autoformat', "no")
1464         if mds.get_val('failover', 0):
1465             self.failover_mds = 'f'
1466         else:
1467             self.failover_mds = 'n'
1468         active_uuid = get_active_target(mds)
1469         if not active_uuid:
1470             panic("No target device found:", target_uuid)
1471         if active_uuid == self.uuid:
1472             self.active = 1
1473         else:
1474             self.active = 0
1475         if self.active and config.group and config.group != mds.get_val('group'):
1476             self.active = 0
1477         self.active = 1
1478
1479         self.inode_size = self.db.get_val_int('inodesize', 0)
1480         if self.inode_size == 0:
1481             # find the LOV for this MDS
1482             lovconfig_uuid = mds.get_first_ref('lovconfig')
1483             if not lovconfig_uuid:
1484                 if not self.lmv_uuid:
1485                     panic("No LOV found for lovconfig ", lovconfig.name)
1486                 lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1487                 lovconfig = self.lmv.lookup(lovconfig_uuid)
1488                 lov_uuid = lovconfig.get_first_ref('lov')
1489                 if not lov_uuid:
1490                     panic("No LOV found for lovconfig ", lovconfig.name)
1491             else:
1492                 lovconfig = mds.lookup(lovconfig_uuid)
1493                 lov_uuid = lovconfig.get_first_ref('lov')
1494                 if not lov_uuid:
1495                     panic("No LOV found for lovconfig ", lovconfig.name)
1496                 lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1497                 lovconfig = self.lmv.lookup(lovconfig_uuid)
1498                 lov_uuid = lovconfig.get_first_ref('lov')
1499
1500             lov = LOV(self.db.lookup(lov_uuid), lov_uuid, 'FS_name', config_only = 1)
1501
1502             # default stripe count controls default inode_size
1503             stripe_count = lov.stripe_cnt
1504             if stripe_count > 77:
1505                 self.inode_size = 4096
1506             elif stripe_count > 35:
1507                 self.inode_size = 2048
1508             elif stripe_count > 13:
1509                 self.inode_size = 1024
1510             elif stripe_count > 3:
1511                 self.inode_size = 512
1512             else:
1513                 self.inode_size = 256
1514
1515         self.target_dev_uuid = self.uuid
1516         self.uuid = target_uuid
1517         # setup LMV
1518         if self.master_mds:
1519             client_uuid = generate_client_uuid(self.name)
1520             self.master = LMV(self.db.lookup(self.lmv_uuid), client_uuid, self.name, self.name)
1521             self.master_mds = self.master.name
1522         # modules
1523         self.add_lustre_module('mdc', 'mdc')
1524         self.add_lustre_module('osc', 'osc')
1525         self.add_lustre_module('lov', 'lov')
1526         self.add_lustre_module('lmv', 'lmv')
1527         self.add_lustre_module('mds', 'mds')
1528         if self.fstype:
1529             self.add_lustre_module('lvfs', 'fsfilt_%s' % (self.fstype))
1530
1531     def load_module(self):
1532         if self.active:
1533             Module.load_module(self)
1534             
1535     def prepare(self):
1536         if is_prepared(self.name):
1537             return
1538         if not self.active:
1539             debug(self.uuid, "not active")
1540             return
1541         if config.reformat:
1542             # run write_conf automatically, if --reformat used
1543             self.write_conf()
1544         self.info(self.devpath, self.fstype, self.size, self.format)
1545         run_acceptors()
1546         # prepare LMV
1547         if self.master_mds:
1548              self.master.prepare()
1549         # never reformat here
1550         blkdev = block_dev(self.devpath, self.size, self.fstype, 0,
1551                            self.format, self.journal_size, self.inode_size,
1552                            self.mkfsoptions)
1553         if not is_prepared('MDT'):
1554             lctl.newdev("mdt", 'MDT', 'MDT_UUID', setup ="")
1555         try: 
1556             lctl.newdev("mds", self.name, self.uuid,
1557                         setup ="%s %s %s %s"
1558                                 %(blkdev, self.fstype, self.name, self.master_mds))
1559         except CommandError, e:
1560             if e.rc == 2:
1561                 panic("MDS is missing the config log. Need to run " +
1562                        "lconf --write_conf.")
1563             else:
1564                 raise e
1565
1566     def write_conf(self):
1567         if is_prepared(self.name):
1568             return
1569         self.info(self.devpath, self.fstype, self.format)
1570         blkdev = block_dev(self.devpath, self.size, self.fstype,
1571                            config.reformat, self.format, self.journal_size,
1572                            self.inode_size, self.mkfsoptions)
1573         lctl.newdev("mds", self.name, self.uuid,
1574                     setup ="%s %s" %(blkdev, self.fstype))
1575  
1576         # record logs for the MDS lov
1577         for uuid in self.filesystem_uuids:
1578             log("recording clients for filesystem:", uuid)
1579             fs = self.db.lookup(uuid)
1580             obd_uuid = fs.get_first_ref('obd')
1581             client_uuid = generate_client_uuid(self.name)
1582             client = VOSC(self.db.lookup(obd_uuid), client_uuid, self.name,
1583                           self.name)
1584             config.record = 1
1585             lctl.clear_log(self.name, self.name)
1586             lctl.record(self.name, self.name)
1587             client.prepare()
1588             lctl.mount_option(self.name, client.get_name(), "")
1589             lctl.end_record()
1590
1591             config.cleanup = 1
1592             lctl.clear_log(self.name, self.name + '-clean')
1593             lctl.record(self.name, self.name + '-clean')
1594             client.cleanup()
1595             lctl.del_mount_option(self.name)
1596             lctl.end_record()
1597             config.cleanup = 0
1598             config.record = 0
1599
1600         # record logs for each client
1601         if config.ldapurl:
1602             config_options = "--ldapurl " + config.ldapurl + " --config " + config.config
1603         else:
1604             config_options = CONFIG_FILE
1605
1606         for node_db in self.db.lookup_class('node'):
1607             client_name = node_db.getName()
1608             for prof_uuid in node_db.get_refs('profile'):
1609                 prof_db = node_db.lookup(prof_uuid)
1610                 # refactor this into a funtion to test "clientness"
1611                 # of a node.
1612                 for ref_class, ref_uuid in prof_db.get_all_refs():
1613                     if ref_class in ('mountpoint','echoclient'):
1614                         debug("recording", client_name)
1615                         old_noexec = config.noexec
1616                         config.noexec = 0
1617                         noexec_opt = ('', '-n')
1618                         ret, out = run (sys.argv[0],
1619                                         noexec_opt[old_noexec == 1],
1620                                         " -v --record --nomod",
1621                                         "--record_log", client_name,
1622                                         "--record_device", self.name,
1623                                         "--node", client_name,
1624                                         config_options)
1625                         if config.verbose:
1626                             for s in out: log("record> ", string.strip(s))
1627                         ret, out = run (sys.argv[0],
1628                                         noexec_opt[old_noexec == 1],
1629                                         "--cleanup -v --record --nomod",
1630                                         "--record_log", client_name + "-clean",
1631                                         "--record_device", self.name,
1632                                         "--node", client_name,
1633                                         config_options)
1634                         if config.verbose:
1635                             for s in out: log("record> ", string.strip(s))
1636                         config.noexec = old_noexec
1637         try:
1638             lctl.cleanup(self.name, self.uuid, 0, 0)
1639         except CommandError, e:
1640             log(self.module_name, "cleanup failed: ", self.name)
1641             e.dump()
1642             cleanup_error(e.rc)
1643             Module.cleanup(self)
1644         clean_loop(self.devpath)
1645
1646     def msd_remaining(self):
1647         out = lctl.device_list()
1648         for s in out:
1649             if string.split(s)[2] in ('mds',):
1650                 return 1
1651
1652     def safe_to_clean(self):
1653         return self.active
1654
1655     def safe_to_clean_modules(self):
1656         return not self.msd_remaining()
1657
1658     def cleanup(self):
1659         if not self.active:
1660             debug(self.uuid, "not active")
1661             return
1662         self.info()
1663         if is_prepared(self.name):
1664             try:
1665                 lctl.cleanup(self.name, self.uuid, config.force,
1666                              config.failover)
1667             except CommandError, e:
1668                 log(self.module_name, "cleanup failed: ", self.name)
1669                 e.dump()
1670                 cleanup_error(e.rc)
1671                 Module.cleanup(self)
1672             # cleanup LMV
1673             if self.master_mds:
1674                 self.master.cleanup()
1675         if not self.msd_remaining() and is_prepared('MDT'):
1676             try:
1677                 lctl.cleanup("MDT", "MDT_UUID", config.force,
1678                              config.failover)
1679             except CommandError, e:
1680                 print "cleanup failed: ", self.name
1681                 e.dump()
1682                 cleanup_error(e.rc)
1683         clean_loop(self.devpath)
1684
1685     def correct_level(self, level, op=None):
1686         #if self.master_mds:
1687         #   level = level + 2
1688         return level
1689
1690 class OSD(Module):
1691     def __init__(self, db):
1692         Module.__init__(self, 'OSD', db)
1693         self.osdtype = self.db.get_val('osdtype')
1694         self.devpath = self.db.get_val('devpath', '')
1695         self.size = self.db.get_val_int('devsize', 0)
1696         self.journal_size = self.db.get_val_int('journalsize', 0)
1697         self.inode_size = self.db.get_val_int('inodesize', 0)
1698         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1699         self.fstype = self.db.get_val('fstype', '')
1700         self.nspath = self.db.get_val('nspath', '')
1701         target_uuid = self.db.get_first_ref('target')
1702         ost = self.db.lookup(target_uuid)
1703         self.name = ost.getName()
1704         self.format = self.db.get_val('autoformat', 'yes')
1705         if ost.get_val('failover', 0):
1706             self.failover_ost = 'f'
1707         else:
1708             self.failover_ost = 'n'
1709
1710         active_uuid = get_active_target(ost)
1711         if not active_uuid:
1712             panic("No target device found:", target_uuid)
1713         if active_uuid == self.uuid:
1714             self.active = 1
1715         else:
1716             self.active = 0
1717         if self.active and config.group and config.group != ost.get_val('group'):
1718             self.active = 0
1719             
1720         self.target_dev_uuid = self.uuid
1721         self.uuid = target_uuid
1722         # modules
1723         self.add_lustre_module('ost', 'ost')
1724         # FIXME: should we default to ext3 here?
1725         if self.fstype:
1726             self.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.fstype))
1727         self.add_lustre_module(self.osdtype, self.osdtype)
1728
1729     def load_module(self):
1730         if self.active:
1731             Module.load_module(self)
1732
1733     # need to check /proc/mounts and /etc/mtab before
1734     # formatting anything.
1735     # FIXME: check if device is already formatted.
1736     def prepare(self):
1737         if is_prepared(self.name):
1738             return
1739         if not self.active:
1740             debug(self.uuid, "not active")
1741             return
1742         self.info(self.osdtype, self.devpath, self.size, self.fstype,
1743                   self.format, self.journal_size, self.inode_size)
1744         run_acceptors()
1745         if self.osdtype == 'obdecho':
1746             blkdev = ''
1747         else:
1748             blkdev = block_dev(self.devpath, self.size, self.fstype,
1749                                config.reformat, self.format, self.journal_size,
1750                                self.inode_size, self.mkfsoptions)
1751         lctl.newdev(self.osdtype, self.name, self.uuid,
1752                     setup ="%s %s %s" %(blkdev, self.fstype,
1753                                            self.failover_ost))
1754         if not is_prepared('OSS'):
1755             lctl.newdev("ost", 'OSS', 'OSS_UUID', setup ="")
1756
1757     def osd_remaining(self):
1758         out = lctl.device_list()
1759         for s in out:
1760             if string.split(s)[2] in ('obdfilter', 'obdecho'):
1761                 return 1
1762
1763     def safe_to_clean(self):
1764         return self.active
1765
1766     def safe_to_clean_modules(self):
1767         return not self.osd_remaining()
1768
1769     def cleanup(self):
1770         if not self.active:
1771             debug(self.uuid, "not active")
1772             return
1773         if is_prepared(self.name):
1774             self.info()
1775             try:
1776                 lctl.cleanup(self.name, self.uuid, config.force,
1777                              config.failover)
1778             except CommandError, e:
1779                 log(self.module_name, "cleanup failed: ", self.name)
1780                 e.dump()
1781                 cleanup_error(e.rc)
1782         if not self.osd_remaining() and is_prepared('OSS'):
1783             try:
1784                 lctl.cleanup("OSS", "OSS_UUID", config.force,
1785                              config.failover)
1786             except CommandError, e:
1787                 print "cleanup failed: ", self.name
1788                 e.dump()
1789                 cleanup_error(e.rc)
1790         if not self.osdtype == 'obdecho':
1791             clean_loop(self.devpath)
1792
1793     def correct_level(self, level, op=None):
1794         return level
1795
1796 def mgmt_uuid_for_fs(mtpt_name):
1797     if not mtpt_name:
1798         return ''
1799     mtpt_db = toplevel.lookup_name(mtpt_name)
1800     fs_uuid = mtpt_db.get_first_ref('filesystem')
1801     fs = toplevel.lookup(fs_uuid)
1802     if not fs:
1803         return ''
1804     return fs.get_first_ref('mgmt')
1805
1806 # Generic client module, used by OSC and MDC
1807 class Client(Module):
1808     def __init__(self, tgtdb, uuid, module, fs_name, self_name=None,
1809                  module_dir=None):
1810         self.target_name = tgtdb.getName()
1811         self.target_uuid = tgtdb.getUUID()
1812         self.db = tgtdb
1813
1814         self.tgt_dev_uuid = get_active_target(tgtdb)
1815         if not self.tgt_dev_uuid:
1816             panic("No target device found for target(1):", self.target_name)
1817         
1818         self.kmod = kmod(config.lustre, config.portals)
1819         self._server = None
1820         self._connected = 0
1821
1822         self.module = module
1823         self.module_name = string.upper(module)
1824         if not self_name:
1825             self.name = '%s_%s_%s_%s' % (self.module_name, socket.gethostname(),
1826                                          self.target_name, fs_name)
1827         else:
1828             self.name = self_name
1829         self.uuid = uuid
1830         self.lookup_server(self.tgt_dev_uuid)
1831         mgmt_uuid = mgmt_uuid_for_fs(fs_name)
1832         if mgmt_uuid:
1833             self.mgmt_name = mgmtcli_name_for_uuid(mgmt_uuid)
1834         else:
1835             self.mgmt_name = ''
1836         self.fs_name = fs_name
1837         if not module_dir:
1838             module_dir = module
1839         self.add_lustre_module(module_dir, module)
1840
1841     def lookup_server(self, srv_uuid):
1842         """ Lookup a server's network information """
1843         self._server_nets = get_ost_net(self.db, srv_uuid)
1844         if len(self._server_nets) == 0:
1845             panic ("Unable to find a server for:", srv_uuid)
1846
1847     def get_servers(self):
1848         return self._server_nets
1849
1850     def prepare(self, ignore_connect_failure = 0):
1851         self.info(self.target_uuid)
1852         if is_prepared(self.name):
1853             self.cleanup()
1854         try:
1855             srv = choose_local_server(self.get_servers())
1856             if srv:
1857                 lctl.connect(srv)
1858             else:
1859                 routes = find_route(self.get_servers())
1860                 if len(routes) == 0:
1861                     panic ("no route to",  self.target_uuid)
1862                 for (srv, r) in routes:
1863                     lctl.add_route_host(r[0], srv.nid_uuid, r[1], r[3])
1864         except CommandError, e:
1865             if not ignore_connect_failure:
1866                 raise e
1867         if srv:
1868             if self.target_uuid in config.inactive and self.permits_inactive():
1869                 debug("%s inactive" % self.target_uuid)
1870                 inactive_p = "inactive"
1871             else:
1872                 debug("%s active" % self.target_uuid)
1873                 inactive_p = ""
1874             lctl.newdev(self.module, self.name, self.uuid,
1875                         setup ="%s %s %s %s" % (self.target_uuid, srv.nid_uuid,
1876                                                 inactive_p, self.mgmt_name))
1877
1878     def cleanup(self):
1879         if is_prepared(self.name):
1880             Module.cleanup(self)
1881             try:
1882                 srv = choose_local_server(self.get_servers())
1883                 if srv:
1884                     lctl.disconnect(srv)
1885                 else:
1886                     for (srv, r) in find_route(self.get_servers()):
1887                         lctl.del_route_host(r[0], srv.nid_uuid, r[1], r[3])
1888             except CommandError, e:
1889                 log(self.module_name, "cleanup failed: ", self.name)
1890                 e.dump()
1891                 cleanup_error(e.rc)
1892
1893     def correct_level(self, level, op=None):
1894         return level
1895
1896
1897 class MDC(Client):
1898     def __init__(self, db, uuid, fs_name):
1899          Client.__init__(self, db, uuid, 'mdc', fs_name)
1900
1901     def permits_inactive(self):
1902         return 0
1903
1904 class OSC(Client):
1905     def __init__(self, db, uuid, fs_name):
1906          Client.__init__(self, db, uuid, 'osc', fs_name)
1907
1908     def permits_inactive(self):
1909         return 1
1910
1911 def mgmtcli_name_for_uuid(uuid):
1912     return 'MGMTCLI_%s' % uuid
1913
1914 class ManagementClient(Client):
1915     def __init__(self, db, uuid):
1916         Client.__init__(self, db, uuid, 'mgmt_cli', '',
1917                         self_name = mgmtcli_name_for_uuid(db.getUUID()),
1918                         module_dir = 'mgmt')
1919             
1920 class COBD(Module):
1921     def __init__(self, db):
1922         Module.__init__(self, 'COBD', db)
1923         self.real_uuid = self.db.get_first_ref('realobd')
1924         self.cache_uuid = self.db.get_first_ref('cacheobd')
1925         self.add_lustre_module('cobd' , 'cobd')
1926
1927     # need to check /proc/mounts and /etc/mtab before
1928     # formatting anything.
1929     # FIXME: check if device is already formatted.
1930     def prepare(self):
1931         if is_prepared(self.name):
1932             return
1933         self.info(self.real_uuid, self.cache_uuid)
1934         lctl.newdev("cobd", self.name, self.uuid,
1935                     setup ="%s %s" %(self.real_uuid, self.cache_uuid))
1936
1937     def correct_level(self, level, op=None):
1938         return level
1939
1940 # virtual interface for  OSC and LOV
1941 class VOSC(Module):
1942     def __init__(self, db, uuid, fs_name, name_override = None):
1943         Module.__init__(self, 'VOSC', db)
1944         if db.get_class() == 'lov':
1945             self.osc = LOV(db, uuid, fs_name, name_override)
1946         else:
1947             self.osc = get_osc(db, uuid, fs_name)
1948     def get_uuid(self):
1949         return self.osc.uuid
1950     def get_name(self):
1951         return self.osc.name
1952     def prepare(self):
1953         self.osc.prepare()
1954     def cleanup(self):
1955         self.osc.cleanup()
1956     def load_module(self):
1957         self.osc.load_module()
1958     def cleanup_module(self):
1959         self.osc.cleanup_module()
1960     def correct_level(self, level, op=None):
1961         return self.osc.correct_level(level, op)
1962
1963 # virtual interface for MDC and LMV
1964 class VMDC(Module):
1965     def __init__(self, db, uuid, fs_name, name_override = None):
1966         Module.__init__(self, 'VMDC', db)
1967         if db.get_class() == 'lmv':
1968             self.mdc = LMV(db, uuid, fs_name)
1969         else:
1970             self.mdc = MDC(db, uuid, fs_name)
1971     def get_uuid(self):
1972         return self.mdc.uuid
1973     def get_name(self):
1974         return self.mdc.name
1975     def prepare(self):
1976         self.mdc.prepare()
1977     def cleanup(self):
1978         self.mdc.cleanup()
1979     def load_module(self):
1980         self.mdc.load_module()
1981     def cleanup_module(self):
1982         self.mdc.cleanup_module()
1983     def correct_level(self, level, op=None):
1984         return self.osc.correct_level(level, op)
1985
1986
1987 class ECHO_CLIENT(Module):
1988     def __init__(self,db):
1989         Module.__init__(self, 'ECHO_CLIENT', db)
1990         self.add_lustre_module('obdecho', 'obdecho')
1991         self.obd_uuid = self.db.get_first_ref('obd')
1992         obd = self.db.lookup(self.obd_uuid)
1993         self.uuid = generate_client_uuid(self.name)
1994         self.osc = VOSC(obd, self.uuid, self.name)
1995
1996     def prepare(self):
1997         if is_prepared(self.name):
1998             return
1999         run_acceptors()
2000         self.osc.prepare() # XXX This is so cheating. -p
2001         self.info(self.obd_uuid)
2002
2003         lctl.newdev("echo_client", self.name, self.uuid,
2004                     setup = self.osc.get_name())
2005
2006     def cleanup(self):
2007         if is_prepared(self.name):
2008             Module.cleanup(self)
2009         self.osc.cleanup()
2010
2011     def load_module(self):
2012         self.osc.load_module()
2013         Module.load_module(self)
2014
2015     def cleanup_module(self):
2016         Module.cleanup_module(self)
2017         self.osc.cleanup_module()
2018
2019     def correct_level(self, level, op=None):
2020         return level
2021
2022 def generate_client_uuid(name):
2023         client_uuid = '%05x_%.19s_%05x%05x' % (int(random.random() * 1048576),
2024                                                name,
2025                                                int(random.random() * 1048576),
2026                                                int(random.random() * 1048576))
2027         return client_uuid[:36]
2028
2029
2030 class Mountpoint(Module):
2031     def __init__(self,db):
2032         Module.__init__(self, 'MTPT', db)
2033         self.path = self.db.get_val('path')
2034         self.fs_uuid = self.db.get_first_ref('filesystem')
2035         fs = self.db.lookup(self.fs_uuid)
2036         self.mds_uuid = fs.get_first_ref('lmv')
2037         if not self.mds_uuid:
2038             self.mds_uuid = fs.get_first_ref('mds')
2039         self.obd_uuid = fs.get_first_ref('obd')
2040         self.mgmt_uuid = fs.get_first_ref('mgmt')
2041         obd = self.db.lookup(self.obd_uuid)
2042         client_uuid = generate_client_uuid(self.name)
2043         self.vosc = VOSC(obd, client_uuid, self.name)
2044         self.mds = self.db.lookup(self.mds_uuid)
2045         if not self.mds:
2046             panic("no mds: ", self.mds_uuid)
2047         self.add_lustre_module('mdc', 'mdc')
2048         self.add_lustre_module('lmv', 'lmv')
2049         self.vmdc = VMDC(self.mds, client_uuid, self.name, self.mds_uuid)
2050         self.mdc = self.vmdc.mdc
2051         self.add_lustre_module('llite', 'llite')
2052         if self.mgmt_uuid:
2053             self.mgmtcli = ManagementClient(db.lookup(self.mgmt_uuid),
2054                                             client_uuid)
2055         else:
2056             self.mgmtcli = None
2057
2058     def prepare(self):
2059         if fs_is_mounted(self.path):
2060             log(self.path, "already mounted.")
2061             return
2062         run_acceptors()
2063         if self.mgmtcli:
2064             self.mgmtcli.prepare()
2065         self.vosc.prepare()
2066         self.mdc.prepare()
2067         mdc_name = self.mdc.name
2068
2069         self.info(self.path, self.mds_uuid, self.obd_uuid)
2070         if config.record or config.lctl_dump:
2071             lctl.mount_option(local_node_name, self.vosc.get_name(), mdc_name)
2072             return
2073         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s %s %s" % \
2074               (self.vosc.get_name(), mdc_name, config.config, self.path)
2075         run("mkdir", self.path)
2076         ret, val = run(cmd)
2077         if ret:
2078             self.mdc.cleanup()            
2079             self.vosc.cleanup()
2080             panic("mount failed:", self.path, ":", string.join(val))
2081
2082     def cleanup(self):
2083         self.info(self.path, self.mds_uuid,self.obd_uuid)
2084
2085         if config.record or config.lctl_dump:
2086             lctl.del_mount_option(local_node_name)
2087         else:
2088             if fs_is_mounted(self.path):
2089                 if config.force:
2090                     (rc, out) = run("umount", "-f", self.path)
2091                 else:
2092                     (rc, out) = run("umount", self.path)
2093                 if rc:
2094                     raise CommandError('umount', out, rc)
2095
2096             if fs_is_mounted(self.path):
2097                 panic("fs is still mounted:", self.path)
2098
2099         self.mdc.cleanup()
2100         self.vosc.cleanup()
2101         if self.mgmtcli:
2102             self.mgmtcli.cleanup()
2103
2104     def load_module(self):
2105         if self.mgmtcli:
2106             self.mgmtcli.load_module()
2107         self.vosc.load_module()
2108         Module.load_module(self)
2109
2110     def cleanup_module(self):
2111         Module.cleanup_module(self)
2112         self.vosc.cleanup_module()
2113         if self.mgmtcli:
2114             self.mgmtcli.cleanup_module()
2115
2116     def correct_level(self, level, op=None):
2117         return level
2118
2119 # ============================================================
2120 # misc query functions
2121
2122 def get_ost_net(self, osd_uuid):
2123     srv_list = []
2124     if not osd_uuid:
2125         return srv_list
2126     osd = self.lookup(osd_uuid)
2127     node_uuid = osd.get_first_ref('node')
2128     node = self.lookup(node_uuid)
2129     if not node:
2130         panic("unable to find node for osd_uuid:", osd_uuid,
2131               " node_ref:", node_uuid_)
2132     for net_uuid in node.get_networks():
2133         db = node.lookup(net_uuid)
2134         srv_list.append(Network(db))
2135     return srv_list
2136
2137
2138 # the order of iniitailization is based on level. 
2139 def getServiceLevel(self):
2140     type = self.get_class()
2141     ret=0;
2142     if type in ('network',):
2143         ret = 5
2144     elif type in ('routetbl',):
2145         ret = 6
2146     elif type in ('ldlm',):
2147         ret = 20
2148     elif type in ('mgmt',):
2149         ret = 25
2150     elif type in ('osd', 'cobd'):
2151         ret = 30
2152     elif type in ('mdsdev',):
2153         ret = 40
2154     elif type in ('lmv',):
2155         ret = 45
2156     elif type in ('mountpoint', 'echoclient'):
2157         ret = 70
2158     else:
2159         panic("Unknown type: ", type)
2160
2161     if ret < config.minlevel or ret > config.maxlevel:
2162         ret = 0 
2163     return ret
2164
2165 #
2166 # return list of services in a profile. list is a list of tuples
2167 # [(level, db_object),]
2168 def getServices(self):
2169     list = []
2170     for ref_class, ref_uuid in self.get_all_refs():
2171             servdb = self.lookup(ref_uuid)
2172             if  servdb:
2173                 level = getServiceLevel(servdb)
2174                 if level > 0:
2175                     list.append((level, servdb))
2176             else:
2177                 panic('service not found: ' + ref_uuid)
2178
2179     list.sort()
2180     return list
2181
2182
2183 ############################################################
2184 # MDC UUID hack - 
2185 # FIXME: clean this mess up!
2186 #
2187 # OSC is no longer in the xml, so we have to fake it.
2188 # this is getting ugly and begging for another refactoring
2189 def get_osc(ost_db, uuid, fs_name):
2190     osc = OSC(ost_db, uuid, fs_name)
2191     return osc
2192
2193 def get_mdc(db, uuid, fs_name, mds_uuid):
2194     mds_db = db.lookup(mds_uuid);
2195     if not mds_db:
2196         error("no mds:", mds_uuid)
2197     mdc = MDC(mds_db, mds_uuid, fs_name)
2198     return mdc
2199
2200 ############################################################
2201 # routing ("rooting")
2202 # list of (nettype, cluster_id, nid)
2203 local_clusters = []
2204
2205 def find_local_clusters(node_db):
2206     global local_clusters
2207     for netuuid in node_db.get_networks():
2208         net = node_db.lookup(netuuid)
2209         srv = Network(net)
2210         debug("add_local", netuuid)
2211         local_clusters.append((srv.net_type, srv.cluster_id, srv.nid))
2212         if srv.port > 0:
2213             if acceptors.has_key(srv.port):
2214                 panic("duplicate port:", srv.port)
2215             acceptors[srv.port] = AcceptorHandler(srv.port, srv.net_type,
2216                                                   srv.send_mem, srv.recv_mem,
2217                                                   srv.irq_affinity)
2218
2219 # This node is a gateway.
2220 is_router = 0
2221 def node_is_router():
2222     return is_router
2223
2224 # If there are any routers found in the config, then this will be true
2225 # and all nodes will load kptlrouter.
2226 needs_router = 0
2227 def node_needs_router():
2228     return needs_router or is_router
2229
2230 # list of (nettype, gw, tgt_cluster_id, lo, hi)
2231 # Currently, these local routes are only added to kptlrouter route
2232 # table if they are needed to connect to a specific server.  This
2233 # should be changed so all available routes are loaded, and the
2234 # ptlrouter can make all the decisions.
2235 local_routes = []
2236
2237 def find_local_routes(lustre):
2238     """ Scan the lustre config looking for routers .  Build list of
2239     routes. """
2240     global local_routes, needs_router
2241     local_routes = []
2242     list = lustre.lookup_class('node')
2243     for router in list:
2244         if router.get_val_int('router', 0):
2245             needs_router = 1
2246             for (local_type, local_cluster_id, local_nid) in local_clusters:
2247                 gw = None
2248                 for netuuid in router.get_networks():
2249                     db = router.lookup(netuuid)
2250                     if (local_type == db.get_val('nettype') and
2251                        local_cluster_id == db.get_val('clusterid')):
2252                         gw = db.get_val('nid')
2253                         break
2254                 if gw:
2255                     debug("find_local_routes: gw is", gw)
2256                     for route in router.get_local_routes(local_type, gw):
2257                         local_routes.append(route)
2258     debug("find_local_routes:", local_routes)
2259
2260
2261 def choose_local_server(srv_list):
2262     for srv in srv_list:
2263         if local_cluster(srv.net_type, srv.cluster_id):
2264             return srv
2265
2266 def local_cluster(net_type, cluster_id):
2267     for cluster in local_clusters:
2268         if net_type == cluster[0] and cluster_id == cluster[1]:
2269             return 1
2270     return 0
2271
2272 def local_interface(net_type, cluster_id, nid):
2273     for cluster in local_clusters:
2274         if (net_type == cluster[0] and cluster_id == cluster[1]
2275             and nid == cluster[2]):
2276             return 1
2277     return 0
2278
2279 def find_route(srv_list):
2280     result = []
2281     frm_type = local_clusters[0][0]
2282     for srv in srv_list:
2283         debug("find_route: srv:", srv.nid, "type: ", srv.net_type)
2284         to_type = srv.net_type
2285         to = srv.nid
2286         cluster_id = srv.cluster_id
2287         debug ('looking for route to', to_type, to)
2288         for r in local_routes:
2289             debug("find_route: ", r)
2290             if  (r[3] <= to and to <= r[4]) and cluster_id == r[2]:
2291                 result.append((srv, r))
2292     return result
2293            
2294 def get_active_target(db):
2295     target_uuid = db.getUUID()
2296     target_name = db.getName()
2297     node_name = get_select(target_name)
2298     if node_name:
2299         tgt_dev_uuid = db.get_node_tgt_dev(node_name, target_uuid)
2300     else:
2301         tgt_dev_uuid = db.get_first_ref('active')
2302     return tgt_dev_uuid
2303
2304 def get_server_by_nid_uuid(db,  nid_uuid):
2305     for n in db.lookup_class("network"):
2306         net = Network(n)
2307         if net.nid_uuid == nid_uuid:
2308             return net
2309         
2310
2311 ############################################################
2312 # lconf level logic
2313 # Start a service.
2314 def newService(db):
2315     type = db.get_class()
2316     debug('Service:', type, db.getName(), db.getUUID())
2317     n = None
2318     if type == 'ldlm':
2319         n = LDLM(db)
2320     elif type == 'lov':
2321         n = LOV(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2322     elif type == 'network':
2323         n = Network(db)
2324     elif type == 'routetbl':
2325         n = RouteTable(db)
2326     elif type == 'osd':
2327         n = OSD(db)
2328     elif type == 'cobd':
2329         n = COBD(db)
2330     elif type == 'mdsdev':
2331         n = MDSDEV(db)
2332     elif type == 'mountpoint':
2333         n = Mountpoint(db)
2334     elif type == 'echoclient':
2335         n = ECHO_CLIENT(db)
2336     elif type == 'mgmt':
2337         n = Management(db)
2338     elif type == 'lmv':
2339         n = LMV(db)
2340     else:
2341         panic ("unknown service type:", type)
2342     return n
2343
2344 #
2345 # Prepare the system to run lustre using a particular profile
2346 # in a the configuration. 
2347 #  * load & the modules
2348 #  * setup networking for the current node
2349 #  * make sure partitions are in place and prepared
2350 #  * initialize devices with lctl
2351 # Levels is important, and needs to be enforced.
2352 def for_each_profile(db, prof_list, operation):
2353     for prof_uuid in prof_list:
2354         prof_db = db.lookup(prof_uuid)
2355         if not prof_db:
2356             panic("profile:", profile, "not found.")
2357         services = getServices(prof_db)
2358         operation(services)
2359         
2360 def doWriteconf(services):
2361     if config.nosetup:
2362         return
2363     for s in services:
2364         if s[1].get_class() == 'mdsdev':
2365             n = newService(s[1])
2366             n.write_conf()
2367
2368 def doSetup(services):
2369     if config.nosetup:
2370         return
2371     slist = []
2372     for s in services:
2373         n = newService(s[1])
2374         n.level = s[0]
2375         slist.append((n.level, n))
2376     nlist = []
2377     for n in slist:
2378         nl = n[1].correct_level(n[0])
2379         nlist.append((nl, n[1]))
2380     nlist.sort()
2381     for n in nlist:
2382         n[1].prepare()
2383     
2384 def doModules(services):
2385     if config.nomod:
2386         return
2387     for s in services:
2388         n = newService(s[1])
2389         n.load_module()
2390
2391 def doCleanup(services):
2392     if config.nosetup:
2393         return
2394     slist = []
2395     for s in services:
2396         n = newService(s[1])
2397         n.level = s[0]
2398         slist.append((n.level, n))
2399     nlist = []
2400     for n in slist:
2401         nl = n[1].correct_level(n[0])
2402         nlist.append((nl, n[1]))
2403     nlist.sort()
2404     nlist.reverse()
2405     for n in nlist:
2406         if n[1].safe_to_clean():
2407             n[1].cleanup()
2408
2409 def doUnloadModules(services):
2410     if config.nomod:
2411         return
2412     services.reverse()
2413     for s in services:
2414         n = newService(s[1])
2415         if n.safe_to_clean_modules():
2416             n.cleanup_module()
2417
2418 #
2419 # Load profile for 
2420 def doHost(lustreDB, hosts):
2421     global is_router, local_node_name
2422     node_db = None
2423     for h in hosts:
2424         node_db = lustreDB.lookup_name(h, 'node')
2425         if node_db:
2426             break
2427     if not node_db:
2428         panic('No host entry found.')
2429
2430     local_node_name = node_db.get_val('name', 0)
2431     is_router = node_db.get_val_int('router', 0)
2432     lustre_upcall = node_db.get_val('lustreUpcall', '')
2433     portals_upcall = node_db.get_val('portalsUpcall', '')
2434     timeout = node_db.get_val_int('timeout', 0)
2435     ptldebug = node_db.get_val('ptldebug', '')
2436     subsystem = node_db.get_val('subsystem', '')
2437     
2438     find_local_clusters(node_db)
2439     if not is_router:
2440         find_local_routes(lustreDB)
2441
2442     # Two step process: (1) load modules, (2) setup lustre
2443     # if not cleaning, load modules first.
2444     prof_list = node_db.get_refs('profile')
2445
2446     if config.write_conf:
2447         for_each_profile(node_db, prof_list, doModules)
2448         sys_make_devices()
2449         for_each_profile(node_db, prof_list, doWriteconf)
2450         for_each_profile(node_db, prof_list, doUnloadModules)
2451
2452     elif config.recover:
2453         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
2454             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
2455                                      "--client_uuid <UUID> --conn_uuid <UUID>")
2456         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
2457                    config.conn_uuid)
2458     elif config.cleanup:
2459         if config.force:
2460             # the command line can override this value
2461             timeout = 5
2462         # ugly hack, only need to run lctl commands for --dump
2463         if config.lctl_dump or config.record:
2464             for_each_profile(node_db, prof_list, doCleanup)
2465             return
2466
2467         sys_set_timeout(timeout)
2468         sys_set_ptldebug(ptldebug)
2469         sys_set_subsystem(subsystem)
2470         sys_set_lustre_upcall(lustre_upcall)
2471         sys_set_portals_upcall(portals_upcall)
2472
2473         for_each_profile(node_db, prof_list, doCleanup)
2474         for_each_profile(node_db, prof_list, doUnloadModules)
2475
2476     else:
2477         # ugly hack, only need to run lctl commands for --dump
2478         if config.lctl_dump or config.record:
2479             sys_set_timeout(timeout)
2480             sys_set_lustre_upcall(lustre_upcall)
2481             for_each_profile(node_db, prof_list, doSetup)
2482             return
2483
2484         sys_make_devices()
2485         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
2486         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
2487
2488         for_each_profile(node_db, prof_list, doModules)
2489
2490         sys_set_debug_path()
2491         sys_set_ptldebug(ptldebug)
2492         sys_set_subsystem(subsystem)
2493         script = config.gdb_script
2494         run(lctl.lctl, ' modules >', script)
2495         if config.gdb:
2496             log ("The GDB module script is in", script)
2497             # pause, so user has time to break and
2498             # load the script
2499             time.sleep(5)
2500         sys_set_timeout(timeout)
2501         sys_set_lustre_upcall(lustre_upcall)
2502         sys_set_portals_upcall(portals_upcall)
2503
2504         for_each_profile(node_db, prof_list, doSetup)
2505
2506 def doRecovery(db, lctl, tgt_uuid, client_uuid, nid_uuid):
2507     tgt = db.lookup(tgt_uuid)
2508     if not tgt:
2509         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
2510     new_uuid = get_active_target(tgt)
2511     if not new_uuid:
2512         raise Lustre.LconfError("doRecovery: no active target found for: " +
2513                                 tgt_uuid)
2514     net = choose_local_server(get_ost_net(db, new_uuid))
2515     if not net:
2516         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
2517
2518     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
2519     try:
2520         oldnet = get_server_by_nid_uuid(db, nid_uuid)
2521         if oldnet:
2522             lctl.disconnect(oldnet)
2523     except CommandError, e:
2524         log("recover: disconnect", nid_uuid, "failed: ")
2525         e.dump()
2526
2527     try:
2528         lctl.connect(net)
2529     except CommandError, e:
2530         log("recover: connect failed")
2531         e.dump()
2532
2533     lctl.recover(client_uuid, net.nid_uuid)
2534
2535
2536 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
2537     base = os.path.dirname(cmd)
2538     if development_mode():
2539         if not config.lustre:
2540             config.lustre = (os.path.join(base, ".."))
2541         # normalize the portals dir, using command line arg if set
2542         if config.portals:
2543             portals_dir = config.portals
2544         dir = os.path.join(config.lustre, portals_dir)
2545         config.portals = dir
2546         debug('config.portals', config.portals)
2547     elif config.lustre and config.portals:
2548         # production mode
2549         # if --lustre and --portals, normalize portals 
2550         # can ignore POTRALS_DIR here, since it is probly useless here
2551         config.portals = os.path.join(config.lustre, config.portals)
2552         debug('config.portals B', config.portals)
2553
2554 def sysctl(path, val):
2555     debug("+ sysctl", path, val)
2556     if config.noexec:
2557         return
2558     try:
2559         fp = open(os.path.join('/proc/sys', path), 'w')
2560         fp.write(str(val))
2561         fp.close()
2562     except IOError, e:
2563         panic(str(e))
2564
2565
2566 def sys_set_debug_path():
2567     sysctl('portals/debug_path', config.debug_path)
2568
2569 def sys_set_lustre_upcall(upcall):
2570     # the command overrides the value in the node config
2571     if config.lustre_upcall:
2572         upcall = config.lustre_upcall
2573     elif config.upcall:
2574         upcall = config.upcall
2575     if upcall:
2576         lctl.set_lustre_upcall(upcall)
2577
2578 def sys_set_portals_upcall(upcall):
2579     # the command overrides the value in the node config
2580     if config.portals_upcall:
2581         upcall = config.portals_upcall
2582     elif config.upcall:
2583         upcall = config.upcall
2584     if upcall:
2585         sysctl('portals/upcall', upcall)
2586
2587 def sys_set_timeout(timeout):
2588     # the command overrides the value in the node config
2589     if config.timeout and config.timeout > 0:
2590         timeout = config.timeout
2591     if timeout != None and timeout > 0:
2592         lctl.set_timeout(timeout)
2593
2594 def sys_tweak_socknal ():
2595     if config.single_socket:
2596         sysctl("socknal/typed", 0)
2597
2598 def sys_optimize_elan ():
2599     procfiles = ["/proc/elan/config/eventint_punt_loops",
2600                  "/proc/qsnet/elan3/config/eventint_punt_loops",
2601                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
2602     for p in procfiles:
2603         if os.access(p, os.R_OK):
2604             run ("echo 0 > " + p)
2605
2606 def sys_set_ptldebug(ptldebug):
2607     if config.ptldebug:
2608         ptldebug = config.ptldebug
2609     if ptldebug:
2610         try:
2611             val = eval(ptldebug, ptldebug_names)
2612             val = "0x%x" % (val)
2613             sysctl('portals/debug', val)
2614         except NameError, e:
2615             panic(str(e))
2616
2617 def sys_set_subsystem(subsystem):
2618     if config.subsystem:
2619         subsystem = config.subsystem
2620     if subsystem:
2621         try:
2622             val = eval(subsystem, subsystem_names)
2623             val = "0x%x" % (val)
2624             sysctl('portals/subsystem_debug', val)
2625         except NameError, e:
2626             panic(str(e))
2627
2628 def sys_set_netmem_max(path, max):
2629     debug("setting", path, "to at least", max)
2630     if config.noexec:
2631         return
2632     fp = open(path)
2633     str = fp.readline()
2634     fp.close()
2635     cur = int(str)
2636     if max > cur:
2637         fp = open(path, 'w')
2638         fp.write('%d\n' %(max))
2639         fp.close()
2640     
2641     
2642 def sys_make_devices():
2643     if not os.access('/dev/portals', os.R_OK):
2644         run('mknod /dev/portals c 10 240')
2645     if not os.access('/dev/obd', os.R_OK):
2646         run('mknod /dev/obd c 10 241')
2647
2648
2649 # Add dir to the global PATH, if not already there.
2650 def add_to_path(new_dir):
2651     syspath = string.split(os.environ['PATH'], ':')
2652     if new_dir in syspath:
2653         return
2654     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
2655     
2656 def default_debug_path():
2657     path = '/tmp/lustre-log'
2658     if os.path.isdir('/r'):
2659         return '/r' + path
2660     else:
2661         return path
2662
2663 def default_gdb_script():
2664     script = '/tmp/ogdb'
2665     if os.path.isdir('/r'):
2666         return '/r' + script
2667     else:
2668         return script
2669
2670
2671 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
2672 # ensure basic elements are in the system path
2673 def sanitise_path():
2674     for dir in DEFAULT_PATH:
2675         add_to_path(dir)
2676
2677 # global hack for the --select handling
2678 tgt_select = {}
2679 def init_select(args):
2680     # args = [service=nodeA,service2=nodeB service3=nodeC]
2681     global tgt_select
2682     for arg in args:
2683         list = string.split(arg, ',')
2684         for entry in list:
2685             srv, node = string.split(entry, '=')
2686             tgt_select[srv] = node
2687
2688 def get_select(srv):
2689     if tgt_select.has_key(srv):
2690         return tgt_select[srv]
2691     return None
2692
2693
2694 FLAG = Lustre.Options.FLAG
2695 PARAM = Lustre.Options.PARAM
2696 INTPARAM = Lustre.Options.INTPARAM
2697 PARAMLIST = Lustre.Options.PARAMLIST
2698 lconf_options = [
2699     ('verbose,v', "Print system commands as they are run"),
2700     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
2701     ('config', "Cluster config name used for LDAP query", PARAM),
2702     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
2703     ('node',   "Load config for <nodename>", PARAM),
2704     ('cleanup,d', "Cleans up config. (Shutdown)"),
2705     ('force,f', "Forced unmounting and/or obd detach during cleanup",
2706                FLAG, 0),
2707     ('single_socket', "socknal option: only use one socket instead of bundle",
2708                FLAG, 0),
2709     ('failover',"""Used to shut down without saving state.
2710                    This will allow this node to "give up" a service to a
2711                    another node for failover purposes. This will not
2712                    be a clean shutdown.""",
2713                FLAG, 0),
2714     ('gdb', """Prints message after creating gdb module script
2715                     and sleeps for 5 seconds."""),
2716     ('noexec,n', """Prints the commands and steps that will be run for a
2717                     config without executing them. This can used to check if a
2718                     config file is doing what it should be doing"""),
2719     ('nomod', "Skip load/unload module step."),
2720     ('nosetup', "Skip device setup/cleanup step."),
2721     ('reformat', "Reformat all devices (without question)"),
2722     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
2723     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
2724                PARAM),
2725     ('write_conf', "Save all the client config information on mds."),
2726     ('record', "Write config information on mds."),
2727     ('record_log', "Name of config record log.", PARAM),
2728     ('record_device', "MDS device name that will record the config commands",
2729               PARAM),
2730     ('minlevel', "Minimum level of services to configure/cleanup",
2731                  INTPARAM, 0),
2732     ('maxlevel', """Maximum level of services to configure/cleanup 
2733                     Levels are aproximatly like:
2734                             10 - netwrk
2735                             20 - device, ldlm
2736                             30 - osd, mdd
2737                             40 - mds, ost
2738                             70 - mountpoint, echo_client, osc, mdc, lov""",
2739                INTPARAM, 100),
2740     ('lustre', """Base directory of lustre sources. This parameter will
2741                   cause lconf to load modules from a source tree.""", PARAM),
2742     ('portals', """Portals source directory.  If this is a relative path,
2743                    then it is assumed to be relative to lustre. """, PARAM),
2744     ('timeout', "Set recovery timeout", INTPARAM),
2745     ('upcall',  "Set both portals and lustre upcall script", PARAM),
2746     ('lustre_upcall', "Set lustre upcall script", PARAM),
2747     ('portals_upcall', "Set portals upcall script", PARAM),
2748     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
2749     ('ptldebug', "Set the portals debug level",  PARAM),
2750     ('subsystem', "Set the portals debug subsystem",  PARAM),
2751     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
2752     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
2753 # Client recovery options
2754     ('recover', "Recover a device"),
2755     ('group', "The group of devices to configure or cleanup", PARAM),
2756     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
2757     ('client_uuid', "The failed client (required for recovery)", PARAM),
2758     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
2759
2760     ('inactive', """The name of an inactive service, to be ignored during
2761                     mounting (currently OST-only). Can be repeated.""",
2762                 PARAMLIST),
2763     ]      
2764
2765 def main():
2766     global lctl, config, toplevel, CONFIG_FILE
2767
2768     # in the upcall this is set to SIG_IGN
2769     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
2770     
2771     cl = Lustre.Options("lconf", "config.xml", lconf_options)
2772     try:
2773         config, args = cl.parse(sys.argv[1:])
2774     except Lustre.OptionError, e:
2775         print e
2776         sys.exit(1)
2777
2778     setupModulePath(sys.argv[0])
2779
2780     host = socket.gethostname()
2781
2782     # the PRNG is normally seeded with time(), which is not so good for starting
2783     # time-synchronized clusters
2784     input = open('/dev/urandom', 'r')
2785     if not input:
2786         print 'Unable to open /dev/urandom!'
2787         sys.exit(1)
2788     seed = input.read(32)
2789     input.close()
2790     random.seed(seed)
2791
2792     sanitise_path()
2793     
2794     init_select(config.select)
2795
2796     if len(args) > 0:
2797         if not os.access(args[0], os.R_OK):
2798             print 'File not found or readable:', args[0]
2799             sys.exit(1)
2800         try:
2801             dom = xml.dom.minidom.parse(args[0])
2802         except Exception:
2803             panic("%s does not appear to be a config file." % (args[0]))
2804             sys.exit(1) # make sure to die here, even in debug mode.
2805         CONFIG_FILE = args[0]
2806         db = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
2807         if not config.config:
2808             config.config = os.path.basename(args[0])# use full path?
2809             if config.config[-4:] == '.xml':
2810                 config.config = config.config[:-4]
2811     elif config.ldapurl:
2812         if not config.config:
2813             panic("--ldapurl requires --config name")
2814         dn = "config=%s,fs=lustre" % (config.config)
2815         db = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
2816     elif config.ptldebug or config.subsystem:
2817         sys_set_ptldebug(None)
2818         sys_set_subsystem(None)
2819         sys.exit(0)
2820     else:
2821         print 'Missing config file or ldap URL.'
2822         print 'see lconf --help for command summary'
2823         sys.exit(1)
2824
2825     toplevel = db
2826
2827     ver = db.get_version()
2828     if not ver:
2829         panic("No version found in config data, please recreate.")
2830     if ver != Lustre.CONFIG_VERSION:
2831         panic("Config version", ver, "does not match lconf version",
2832               Lustre.CONFIG_VERSION)
2833
2834     node_list = []
2835     if config.node:
2836         node_list.append(config.node)
2837     else:
2838         if len(host) > 0:
2839             node_list.append(host)
2840         node_list.append('localhost')
2841
2842     debug("configuring for host: ", node_list)
2843
2844     if len(host) > 0:
2845         config.debug_path = config.debug_path + '-' + host
2846         config.gdb_script = config.gdb_script + '-' + host
2847
2848     lctl = LCTLInterface('lctl')
2849
2850     if config.lctl_dump:
2851         lctl.use_save_file(config.lctl_dump)
2852
2853     if config.record:
2854         if not (config.record_device and config.record_log):
2855             panic("When recording, both --record_log and --record_device must be specified.")
2856         lctl.clear_log(config.record_device, config.record_log)
2857         lctl.record(config.record_device, config.record_log)
2858
2859     doHost(db, node_list)
2860
2861     if config.record:
2862         lctl.end_record()
2863
2864 if __name__ == "__main__":
2865     try:
2866         main()
2867     except Lustre.LconfError, e:
2868         print e
2869 #        traceback.print_exc(file=sys.stdout)
2870         sys.exit(1)
2871     except CommandError, e:
2872         e.dump()
2873         sys.exit(e.rc)
2874
2875     if first_cleanup_error:
2876         sys.exit(first_cleanup_error)