Whamcloud - gitweb
* lustre updates for recent portals HEAD changes
[fs/lustre-release.git] / lustre / utils / lconf
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002-2003 Cluster File Systems, Inc.
4 #   Authors: Robert Read <rread@clusterfs.com>
5 #            Mike Shaver <shaver@clusterfs.com>
6 #   This file is part of Lustre, http://www.lustre.org.
7 #
8 #   Lustre is free software; you can redistribute it and/or
9 #   modify it under the terms of version 2 of the GNU General Public
10 #   License as published by the Free Software Foundation.
11 #
12 #   Lustre is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with Lustre; if not, write to the Free Software
19 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #
21 # lconf - lustre configuration tool
22 #
23 # lconf is the main driver script for starting and stopping
24 # lustre filesystem services.
25 #
26 # Based in part on the XML obdctl modifications done by Brian Behlendorf 
27
28 import sys, getopt, types
29 import string, os, stat, popen2, socket, time, random, fcntl, select
30 import re, exceptions, signal, traceback
31 import xml.dom.minidom
32
33 if sys.version[0] == '1':
34     from FCNTL import F_GETFL, F_SETFL
35 else:
36     from fcntl import F_GETFL, F_SETFL
37
38 PYMOD_DIR = "/usr/lib/lustre/python"
39
40 def development_mode():
41     base = os.path.dirname(sys.argv[0])
42     if os.access(base+"/Makefile", os.R_OK):
43         return 1
44     return 0
45
46 if development_mode():
47     sys.path.append('../utils')
48 else:
49     sys.path.append(PYMOD_DIR)
50
51 import Lustre
52
53 # Global parameters
54 MAXTCPBUF = 16777216
55 DEFAULT_TCPBUF = 8388608
56 DEFAULT_PORT = 988
57 #
58 # Maximum number of devices to search for.
59 # (the /dev/loop* nodes need to be created beforehand)
60 MAX_LOOP_DEVICES = 256
61 PORTALS_DIR = '../portals'
62
63 # Needed to call lconf --record
64 CONFIG_FILE = "" 
65
66 # Please keep these in sync with the values in portals/kp30.h
67 ptldebug_names = { 
68     "trace" :     (1 << 0),
69     "inode" :     (1 << 1),
70     "super" :     (1 << 2),
71     "ext2" :      (1 << 3),
72     "malloc" :    (1 << 4),
73     "cache" :     (1 << 5),
74     "info" :      (1 << 6),
75     "ioctl" :     (1 << 7),
76     "blocks" :    (1 << 8),
77     "net" :       (1 << 9),
78     "warning" :   (1 << 10),
79     "buffs" :     (1 << 11),
80     "other" :     (1 << 12),
81     "dentry" :    (1 << 13),
82     "portals" :   (1 << 14),
83     "page" :      (1 << 15),
84     "dlmtrace" :  (1 << 16),
85     "error" :     (1 << 17),
86     "emerg" :     (1 << 18),
87     "ha" :        (1 << 19),
88     "rpctrace" :  (1 << 20),
89     "vfstrace" :  (1 << 21),
90     "reada" :     (1 << 22),
91     "mmap" :      (1 << 23),
92     "config" :    (1 << 24),
93 }
94
95 subsystem_names = {
96     "undefined" :    (1 << 0),
97     "mdc" :          (1 << 1),
98     "mds" :          (1 << 2),
99     "osc" :          (1 << 3),
100     "ost" :          (1 << 4),
101     "class" :        (1 << 5),
102     "log" :          (1 << 6),
103     "llite" :        (1 << 7),
104     "rpc" :          (1 << 8),
105     "mgmt" :         (1 << 9),
106     "portals" :      (1 << 10),
107     "nal" :          (1 << 11),
108     "pinger" :       (1 << 12),
109     "filter" :       (1 << 13),
110     "ptlbd" :        (1 << 14),
111     "echo" :         (1 << 15),
112     "ldlm" :         (1 << 16),
113     "lov" :          (1 << 17),
114     "ptlrouter" :    (1 << 18),
115     "cobd" :         (1 << 19),
116     "sm" :           (1 << 20),
117     "asobd" :        (1 << 21),
118     "confobd" :      (1 << 22),
119     "lmv" :          (1 << 23),
120     "cmobd" :        (1 << 24),
121     }
122
123
124 first_cleanup_error = 0
125 def cleanup_error(rc):
126     global first_cleanup_error
127     if not first_cleanup_error:
128         first_cleanup_error = rc
129
130 # ============================================================ 
131 # debugging and error funcs
132
133 def fixme(msg = "this feature"):
134     raise Lustre.LconfError, msg + ' not implemented yet.'
135
136 def panic(*args):
137     msg = string.join(map(str,args))
138     if not config.noexec:
139         raise Lustre.LconfError(msg)
140     else:
141         print "! " + msg
142
143 def log(*args):
144     msg = string.join(map(str,args))
145     print msg
146
147 def logall(msgs):
148     for s in msgs:
149         print string.strip(s)
150
151 def debug(*args):
152     if config.verbose:
153         msg = string.join(map(str,args))
154         print msg
155
156 # ack, python's builtin int() does not support '0x123' syntax.
157 # eval can do it, although what a hack!
158 def my_int(s):
159     try:
160         if s[0:2] == '0x':
161             return eval(s, {}, {})
162         else:
163             return int(s)
164     except SyntaxError, e:
165         raise ValueError("not a number")
166     except NameError, e:
167         raise ValueError("not a number")
168
169 # ============================================================
170 # locally defined exceptions
171 class CommandError (exceptions.Exception):
172     def __init__(self, cmd_name, cmd_err, rc=None):
173         self.cmd_name = cmd_name
174         self.cmd_err = cmd_err
175         self.rc = rc
176
177     def dump(self):
178         import types
179         if type(self.cmd_err) == types.StringType:
180             if self.rc:
181                 print "! %s (%d): %s" % (self.cmd_name, self.rc, self.cmd_err)
182             else:
183                 print "! %s: %s" % (self.cmd_name, self.cmd_err)
184         elif type(self.cmd_err) == types.ListType:
185             if self.rc:
186                 print "! %s (error %d):" % (self.cmd_name, self.rc)
187             else:
188                 print "! %s:" % (self.cmd_name)
189             for s in self.cmd_err:
190                 print "> %s" %(string.strip(s))
191         else:
192             print self.cmd_err
193
194
195 # ============================================================
196 # handle daemons, like the acceptor
197 class DaemonHandler:
198     """ Manage starting and stopping a daemon. Assumes daemon manages
199     it's own pid file. """
200
201     def __init__(self, cmd):
202         self.command = cmd
203         self.path =""
204
205     def start(self):
206         if self.running():
207             log(self.command, "already running.")
208         if not self.path:
209             self.path = find_prog(self.command)
210             if not self.path:
211                 panic(self.command, "not found.")
212         ret, out = runcmd(self.path +' '+ self.command_line())
213         if ret:
214             raise CommandError(self.path, out, ret)
215
216     def stop(self):
217         if self.running():
218             pid = self.read_pidfile()
219             try:
220                 if pid != 1:
221                     log ("killing process", pid)
222                     os.kill(pid, 15)
223                 else:
224                     log("was unable to find pid of " + self.command)
225                 #time.sleep(1) # let daemon die
226             except OSError, e:
227                 log("unable to kill", self.command, e)
228             if self.running():
229                 log("unable to kill", self.command)
230
231     def running(self):
232         pid = self.read_pidfile()
233         if pid:
234             try:
235                 if pid != 1:
236                     os.kill(pid, 0)
237                 else:
238                     log("was unable to find pid of " + self.command)
239             except OSError:
240                 self.clean_pidfile()
241             else:
242                 return 1
243         return 0
244
245     def read_pidfile(self):
246         try:
247             fp = open(self.pidfile(), 'r')
248             val = fp.read()
249             if val == '':
250                 val = '1'
251             pid = int(val)
252             fp.close()
253             return pid
254         except IOError:
255             return 0
256         
257     def clean_pidfile(self):
258         """ Remove a stale pidfile """
259         log("removing stale pidfile:", self.pidfile())
260         try:
261             os.unlink(self.pidfile())
262         except OSError, e:
263             log(self.pidfile(), e)
264             
265 class AcceptorHandler(DaemonHandler):
266     def __init__(self, port, net_type):
267         DaemonHandler.__init__(self, "acceptor")
268         self.port = port
269         self.flags = ''
270
271     def pidfile(self):
272         return "/var/run/%s-%d.pid" % (self.command, self.port)
273
274     def command_line(self):
275         return string.join(map(str,(self.flags, self.port)))
276     
277 acceptors = {}
278
279 # start the acceptors
280 def run_acceptors():
281     if config.lctl_dump or config.record:
282         return
283     for port in acceptors.keys():
284         daemon = acceptors[port]
285         if not daemon.running():
286             daemon.start()
287
288 def run_one_acceptor(port):
289     if config.lctl_dump or config.record:
290         return
291     if acceptors.has_key(port):
292         daemon = acceptors[port]
293         if not daemon.running():
294             daemon.start()
295     else:
296          panic("run_one_acceptor: No acceptor defined for port:", port)   
297         
298 def stop_acceptor(port):
299     if acceptors.has_key(port):
300         daemon = acceptors[port]
301         if daemon.running():
302             daemon.stop()
303         
304
305 # ============================================================
306 # handle lctl interface
307 class LCTLInterface:
308     """
309     Manage communication with lctl
310     """
311
312     def __init__(self, cmd):
313         """
314         Initialize close by finding the lctl binary.
315         """
316         self.lctl = find_prog(cmd)
317         self.save_file = ''
318         self.record_device = ''
319         if not self.lctl:
320             if config.noexec:
321                 debug('! lctl not found')
322                 self.lctl = 'lctl'
323             else:
324                 raise CommandError('lctl', "unable to find lctl binary.")
325
326     def use_save_file(self, file):
327         self.save_file = file
328         
329     def record(self, dev_name, logname):
330         log("Recording log", logname, "on", dev_name)
331         self.record_device = dev_name
332         self.record_log = logname
333
334     def end_record(self):
335         log("End recording log", self.record_log, "on", self.record_device)
336         self.record_device = None
337         self.record_log = None
338
339     def set_nonblock(self, fd):
340         fl = fcntl.fcntl(fd, F_GETFL)
341         fcntl.fcntl(fd, F_SETFL, fl | os.O_NDELAY)
342
343     def run(self, cmds):
344         """
345         run lctl
346         the cmds are written to stdin of lctl
347         lctl doesn't return errors when run in script mode, so
348         stderr is checked
349         should modify command line to accept multiple commands, or
350         create complex command line options
351         """
352         cmd_line = self.lctl
353         if self.save_file:
354             cmds = '\n  dump ' + self.save_file + '\n' + cmds
355         elif self.record_device:
356             cmds = """
357     device $%s
358     record %s
359     %s""" % (self.record_device, self.record_log, cmds)
360             
361         debug("+", cmd_line, cmds)
362         if config.noexec: return (0, [])
363
364         child = popen2.Popen3(cmd_line, 1) # Capture stdout and stderr from command
365         child.tochild.write(cmds + "\n")
366         child.tochild.close()
367 #       print "LCTL:", cmds
368
369         # From "Python Cookbook" from O'Reilly
370         outfile = child.fromchild
371         outfd = outfile.fileno()
372         self.set_nonblock(outfd)
373         errfile = child.childerr
374         errfd = errfile.fileno()
375         self.set_nonblock(errfd)
376
377         outdata = errdata = ''
378         outeof = erreof = 0
379         while 1:
380             ready = select.select([outfd,errfd],[],[]) # Wait for input
381             if outfd in ready[0]:
382                 outchunk = outfile.read()
383                 if outchunk == '': outeof = 1
384                 outdata = outdata + outchunk
385             if errfd in ready[0]:
386                 errchunk = errfile.read()
387                 if errchunk == '': erreof = 1
388                 errdata = errdata + errchunk
389             if outeof and erreof: break
390         # end of "borrowed" code
391
392         ret = child.wait()
393         if os.WIFEXITED(ret):
394             rc = os.WEXITSTATUS(ret)
395         else:
396             rc = 0
397         if rc or len(errdata):
398             raise CommandError(self.lctl, errdata, rc)
399         return rc, outdata
400
401     def runcmd(self, *args):
402         """
403         run lctl using the command line
404         """
405         cmd = string.join(map(str,args))
406         debug("+", self.lctl, cmd)
407         rc, out = run(self.lctl, cmd)
408         if rc:
409             raise CommandError(self.lctl, out, rc)
410         return rc, out
411
412     def clear_log(self, dev, log):
413         """ clear an existing log """
414         cmds =  """
415   device $%s
416   probe
417   clear_log %s
418   quit """ % (dev, log)
419         self.run(cmds)
420
421     def root_squash(self, name, uid, nid):
422         cmds = """
423   device $%s
424   root_squash %s %s
425   quit""" % (name, uid, nid)
426         self.run(cmds)
427
428     def network(self, net, nid):
429         """ set mynid """
430         cmds =  """
431   network %s
432   mynid %s
433   quit """ % (net, nid)
434         self.run(cmds)
435
436     # add an interface
437     def add_interface(self, net, ip, netmask = ""):
438         """ add an interface """
439         cmds = """
440   network %s
441   add_interface %s %s
442   quit """ % (net, ip, netmask)
443         self.run(cmds)
444
445     # delete an interface
446     def del_interface(self, net, ip):
447         """ delete an interface """
448         cmds = """
449   network %s
450   del_interface %s
451   quit """ % (net, ip)
452         self.run(cmds)
453
454     # create a new connection
455     def add_uuid(self, net_type, uuid, nid):
456         cmds = "\n  add_uuid %s %s %s" %(uuid, nid, net_type)
457         self.run(cmds)
458
459     def add_peer(self, net_type, nid, hostaddr, port):
460         if net_type  in ('tcp','openib','ra') and not config.lctl_dump:
461             cmds =  """
462   network %s
463   add_peer %s %s %d
464   quit""" % (net_type,
465              nid, hostaddr, port )
466             self.run(cmds)
467         elif net_type in ('iib',) and not config.lctl_dump:
468             cmds =  """
469   network %s
470   add_peer %s
471   quit""" % (net_type,
472              nid )
473             self.run(cmds)
474         elif net_type in ('vib',) and not config.lctl_dump:
475             cmds =  """
476   network %s
477   add_peer %s %s
478   quit""" % (net_type,
479              nid, hostaddr )
480             self.run(cmds)
481     
482     def connect(self, srv):
483         self.add_uuid(srv.net_type, srv.nid_uuid, srv.nid)
484         if srv.net_type  in ('tcp','openib','iib','vib','ra') and not config.lctl_dump:
485             if srv.hostaddr[0]:
486                 hostaddr = string.split(srv.hostaddr[0], '/')[0]
487             self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port)
488
489     # Recover a device
490     def recover(self, dev_name, new_conn):
491         cmds = """
492     device $%s
493     recover %s""" %(dev_name, new_conn)
494         self.run(cmds)
495                 
496     # add a route to a range
497     def add_route(self, net, gw, lo, hi):
498         cmds =  """
499   network %s
500   add_route %s %s %s
501   quit  """ % (net,
502                gw, lo, hi)
503         try:
504             self.run(cmds)
505         except CommandError, e:
506             log ("ignore: ")
507             e.dump()
508                 
509     def del_route(self, net, gw, lo, hi):
510         cmds =  """
511   ignore_errors
512   network %s
513   del_route %s %s %s
514   quit  """ % (net, gw, lo, hi)
515         self.run(cmds)
516
517     # add a route to a host
518     def add_route_host(self, net, uuid, gw, tgt):
519         self.add_uuid(net, uuid, tgt)
520         cmds =  """
521   network %s
522   add_route %s %s
523   quit """ % (net,
524               gw, tgt)
525         try:
526             self.run(cmds)
527         except CommandError, e:
528             log ("ignore: ")
529             e.dump()
530
531     # add a route to a range
532     def del_route_host(self, net, uuid, gw, tgt):
533         self.del_uuid(uuid)
534         cmds =  """
535   ignore_errors
536   network %s
537   del_route %s %s
538   quit  """ % (net, gw, tgt)
539         self.run(cmds)
540
541
542     def del_peer(self, net_type, nid, hostaddr):
543         if net_type  in ('tcp',) and not config.lctl_dump:
544                 cmds =  """
545   ignore_errors
546   network %s
547   del_peer %s %s single_share
548   quit""" % (net_type,
549              nid, hostaddr)
550                 self.run(cmds)
551         elif net_type  in ('openib','iib','vib','ra') and not config.lctl_dump:
552                 cmds =  """
553   ignore_errors
554   network %s
555   del_peer %s single_share
556   quit""" % (net_type,
557              nid)
558                 self.run(cmds)
559         
560     # disconnect one connection
561     def disconnect(self, srv):
562         self.del_uuid(srv.nid_uuid)
563         if srv.net_type  in ('tcp','openib','iib','vib','ra') and not config.lctl_dump:
564             if srv.hostaddr[0]:
565                 hostaddr = string.split(srv.hostaddr[0], '/')[0]
566             self.del_peer(srv.net_type, srv.nid, hostaddr)
567
568     def del_uuid(self, uuid):
569         cmds =  """
570   ignore_errors
571   del_uuid %s
572   quit""" % (uuid,)
573         self.run(cmds)
574
575     # disconnect all
576     def disconnectAll(self, net):
577         cmds =  """
578   ignore_errors
579   network %s
580   disconnect
581   quit""" % (net)
582         self.run(cmds)
583
584     def attach(self, type, name, uuid):
585         cmds = """
586   attach %s %s %s
587   quit""" % (type, name, uuid)
588         self.run(cmds)
589         
590     def setup(self, name, setup = ""):
591         cmds = """
592   cfg_device %s
593   setup %s
594   quit""" % (name, setup)
595         self.run(cmds)
596         
597     def add_conn(self, name, conn_uuid):
598         cmds = """
599   cfg_device %s
600   add_conn %s
601   quit""" % (name, conn_uuid)
602         self.run(cmds)
603
604
605     # create a new device with lctl
606     def newdev(self, type, name, uuid, setup = ""):
607         self.attach(type, name, uuid);
608         try:
609             self.setup(name, setup)
610         except CommandError, e:
611             self.cleanup(name, uuid, 0)
612             raise e
613         
614
615     # cleanup a device
616     def cleanup(self, name, uuid, force, failover = 0):
617         if failover: force = 1
618         cmds = """
619   ignore_errors
620   cfg_device $%s
621   cleanup %s %s
622   detach
623   quit""" % (name, ('', 'force')[force],
624              ('', 'failover')[failover])
625         self.run(cmds)
626
627     # create an lov
628     def lov_setup(self, name, uuid, desc_uuid, stripe_cnt,
629                   stripe_sz, stripe_off, pattern, devlist = None):
630         cmds = """
631   attach lov %s %s
632   lov_setup %s %d %d %d %s %s
633   quit""" % (name, uuid, desc_uuid, stripe_cnt, stripe_sz, stripe_off,
634              pattern, devlist)
635         self.run(cmds)
636
637     # add an OBD to a LOV
638     def lov_add_obd(self, name, uuid, obd_uuid, index, gen):
639         cmds = """
640   lov_modify_tgts add %s %s %s %s
641   quit""" % (name, obd_uuid, index, gen)
642         self.run(cmds)
643
644     # create an lmv
645     def lmv_setup(self, name, uuid, desc_uuid, devlist):
646         cmds = """
647   attach lmv %s %s
648   lmv_setup %s %s
649   quit""" % (name, uuid, desc_uuid, devlist)
650         self.run(cmds)
651
652     # delete an OBD from a LOV
653     def lov_del_obd(self, name, uuid, obd_uuid, index, gen):
654         cmds = """
655   lov_modify_tgts del %s %s %s %s
656   quit""" % (name, obd_uuid, index, gen)
657         self.run(cmds)
658
659     # deactivate an OBD
660     def deactivate(self, name):
661         cmds = """
662   device $%s
663   deactivate
664   quit""" % (name)
665         self.run(cmds)
666
667     # dump the log file
668     def dump(self, dump_file):
669         cmds = """
670   debug_kernel %s 1
671   quit""" % (dump_file)
672         self.run(cmds)
673
674     # get list of devices
675     def device_list(self):
676         devices = '/proc/fs/lustre/devices'
677         ret = []
678         if os.access(devices, os.R_OK):
679             try:
680                 fp = open(devices, 'r')
681                 ret =  fp.readlines()
682                 fp.close()
683             except IOError, e:
684                 log(e)
685         return ret
686
687     # get lustre version
688     def lustre_version(self):
689         rc, out = self.runcmd('version')
690         return out
691
692     # dump mount options
693     def mount_option(self, profile, osc, mdc):
694         cmds = """
695   mount_option %s %s %s
696   quit""" % (profile, osc, mdc)
697         self.run(cmds)
698
699     # delete mount options
700     def del_mount_option(self, profile):
701         cmds = """
702   del_mount_option %s
703   quit""" % (profile,)
704         self.run(cmds)
705
706     def set_timeout(self, timeout):
707         cmds = """
708   set_timeout %s
709   quit""" % (timeout,)
710         self.run(cmds)
711
712     def set_lustre_upcall(self, upcall):
713         cmds = """
714   set_lustre_upcall %s
715   quit""" % (upcall,)
716         self.run(cmds)
717 # ============================================================
718 # Various system-level functions
719 # (ideally moved to their own module)
720
721 # Run a command and return the output and status.
722 # stderr is sent to /dev/null, could use popen3 to
723 # save it if necessary
724 def runcmd(cmd):
725     debug ("+", cmd)
726     if config.noexec: return (0, [])
727     f = os.popen(cmd + ' 2>&1')
728     out = f.readlines()
729     ret = f.close()
730     if ret:
731         ret = ret >> 8
732     else:
733         ret = 0
734     return (ret, out)
735
736 def run(*args):
737     cmd = string.join(map(str,args))
738     return runcmd(cmd)
739
740 # Run a command in the background.
741 def run_daemon(*args):
742     cmd = string.join(map(str,args))
743     debug ("+", cmd)
744     if config.noexec: return 0
745     f = os.popen(cmd + ' 2>&1')
746     ret = f.close()
747     if ret:
748         ret = ret >> 8
749     else:
750         ret = 0
751     return ret
752
753 # Determine full path to use for an external command
754 # searches dirname(argv[0]) first, then PATH
755 def find_prog(cmd):
756     syspath = string.split(os.environ['PATH'], ':')
757     cmdpath = os.path.dirname(sys.argv[0])
758     syspath.insert(0, cmdpath);
759     if config.portals:
760         syspath.insert(0, os.path.join(config.portals, 'utils/'))
761     for d in syspath:
762         prog = os.path.join(d,cmd)
763         if os.access(prog, os.X_OK):
764             return prog
765     return ''
766
767 # Recursively look for file starting at base dir
768 def do_find_file(base, mod):
769     fullname = os.path.join(base, mod)
770     if os.access(fullname, os.R_OK):
771         return fullname
772     for d in os.listdir(base):
773         dir = os.path.join(base,d)
774         if os.path.isdir(dir):
775             module = do_find_file(dir, mod)
776             if module:
777                 return module
778
779 # is the path a block device?
780 def is_block(path):
781     s = ()
782     try:
783         s =  os.stat(path)
784     except OSError:
785         return 0
786     return stat.S_ISBLK(s[stat.ST_MODE])
787
788 # find the journal device from mkfs options
789 def jdev(opts):
790     if opts == None:
791         return ''
792     x=string.split(opts)
793     i=0
794     while i < len(x) - 1:
795         if x[i] == '-J' and x[i+1].startswith('device='):
796             str=x[i+1]
797             return str[7:]
798         i=i+1
799     return ''
800
801 # build fs according to type
802 # fixme: dangerous
803 def mkfs(dev, devsize, fstype, jsize, isize, mkfsoptions, isblock=1):
804     block_cnt = ''
805     jopt = ''
806     iopt = ''
807     if devsize:
808         if devsize < 8000:
809             panic("size of filesystem on '%s' must be larger than 8MB, but is set to %s"%
810                   (dev, devsize))
811         # devsize is in 1k, and fs block count is in 4k
812         block_cnt = devsize/4
813
814     if fstype in ('ext3', 'extN', 'ldiskfs'):
815         # ext3 journal size is in megabytes
816         # but don't set jsize if mkfsoptions indicates a separate journal device
817         if jsize == 0 and jdev(mkfsoptions) == '':
818             if devsize == 0:
819                 if not is_block(dev):
820                     ret, out = runcmd("ls -l %s" %dev)
821                     devsize = int(string.split(out[0])[4]) / 1024
822                 else:
823                     # sfdisk works for symlink, hardlink, and realdev
824                     ret, out = runcmd("sfdisk -s %s" %dev)
825                     if not ret:
826                         devsize = int(out[0])
827                     else:
828                         # sfdisk -s will fail for too large block device,
829                         # then, read the size of partition from /proc/partitions
830
831                         # get the realpath of the device
832                         # it may be the real device, such as /dev/hda7
833                         # or the hardlink created via mknod for a device
834                         if 'realpath' in dir(os.path):
835                             real_dev = os.path.realpath(dev)
836                         else:
837                             real_dev = dev
838                             link_count = 0
839                             while os.path.islink(real_dev) and (link_count < 20):
840                                 link_count = link_count + 1
841                                 dev_link = os.readlink(real_dev)
842                                 if os.path.isabs(dev_link):
843                                     real_dev = dev_link
844                                 else:
845                                     real_dev = os.path.join(os.path.dirname(real_dev), dev_link)
846                                 if link_count > 19:
847                                     panic("Entountered too many symbolic links resolving block device:", dev)
848
849                         # get the major and minor number of the realpath via ls
850                         # it seems python(os.stat) does not return 
851                         # the st_rdev member of the stat structure
852                         ret, out = runcmd("ls -l %s" %real_dev)
853                         major = string.split(string.split(out[0])[4], ",")[0]
854                         minor = string.split(out[0])[5]
855
856                         # get the devsize from /proc/partitions with the major and minor number
857                         ret, out = runcmd("cat /proc/partitions")
858                         for line in out:
859                             if len(line) > 1:
860                                 if string.split(line)[0] == major and string.split(line)[1] == minor:
861                                     devsize = int(string.split(line)[2])
862                                     break
863
864             if devsize > 1024 * 1024:
865                 jsize = ((devsize / 102400) * 4)
866             if jsize > 400:
867                 jsize = 400        
868         if jsize:  jopt = "-J size=%d" %(jsize,)
869         if isize:  iopt = "-I %d" %(isize,)
870         mkfs = 'mkfs.ext2 -j -b 4096 '
871         if not isblock or config.force:
872             mkfs = mkfs + ' -F '
873         if jdev(mkfsoptions) != '':
874             jmkfs = 'mkfs.ext2 -b 4096 -O journal_dev '
875             if config.force:
876                 jmkfs = jmkfs + '-F '
877             jmkfs = jmkfs + jdev(mkfsoptions)                
878             (ret, out) = run (jmkfs)
879             if ret:
880                 panic("Unable format journal device:", jdev(mkfsoptions), string.join(out))
881     elif fstype == 'reiserfs':
882         # reiserfs journal size is in blocks
883         if jsize:  jopt = "--journal_size %d" %(jsize,)
884         mkfs = 'mkreiserfs -ff'
885     else:
886         panic('unsupported fs type: ', fstype)
887
888     if config.mkfsoptions != None:
889         mkfs = mkfs + ' ' + config.mkfsoptions
890     if mkfsoptions != None:
891         mkfs = mkfs + ' ' + mkfsoptions
892     (ret, out) = run (mkfs, jopt, iopt, dev, block_cnt)
893     if ret:
894         panic("Unable to build fs:", dev, string.join(out))
895     # enable hash tree indexing on fsswe
896     if fstype in ('ext3', 'extN', 'ldiskfs'):
897         htree = 'echo "feature FEATURE_C5" | debugfs -w'
898         (ret, out) = run (htree, dev)
899         if ret:
900             panic("Unable to enable htree:", dev)
901
902 # some systems use /dev/loopN, some /dev/loop/N
903 def loop_base():
904     import re
905     loop = '/dev/loop'
906     if not os.access(loop + str(0), os.R_OK):
907         loop = loop + '/'
908         if not os.access(loop + str(0), os.R_OK):
909             panic ("can't access loop devices")
910     return loop
911     
912 # find loop device assigned to the file
913 def find_assigned_loop(file):
914     loop = loop_base()
915     for n in xrange(0, MAX_LOOP_DEVICES):
916         dev = loop + str(n)
917         if os.access(dev, os.R_OK):
918             (stat, out) = run('losetup', dev)
919             if out and stat == 0:
920                 m = re.search(r'\((.*)\)', out[0])
921                 if m and file == m.group(1):
922                     return dev
923         else:
924             break
925     return ''
926
927 # create file if necessary and assign the first free loop device
928 def init_loop(file, size, fstype, journal_size, inode_size, 
929               mkfsoptions, reformat, autoformat, backfstype, backfile):
930     if fstype == 'smfs':
931         realfile = backfile
932         realfstype = backfstype
933         if is_block(backfile):
934             if reformat or (need_format(realfstype, backfile) and autoformat == 'yes'):
935                 mkfs(realfile, size, realfstype, journal_size, inode_size, mkfsoptions, isblock=0)
936             return realfile
937     else:
938         realfile = file
939         realfstype = fstype
940             
941     dev = find_assigned_loop(realfile)
942     if dev:
943         print 'WARNING: file ', realfile, 'already mapped to', dev
944         return dev
945             
946     if reformat or not os.access(realfile, os.R_OK | os.W_OK):
947         if size < 8000:
948             panic("size of loopback file '%s' must be larger than 8MB, but is set to %s" % (realfile, size))
949         (ret, out) = run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size, realfile))
950         if ret:
951             panic("Unable to create backing store:", realfile)
952             
953         mkfs(realfile, size, realfstype, journal_size, inode_size, 
954              mkfsoptions, isblock=0)
955
956     loop = loop_base()
957     # find next free loop
958     for n in xrange(0, MAX_LOOP_DEVICES):
959         dev = loop + str(n)
960         if os.access(dev, os.R_OK):
961             (stat, out) = run('losetup', dev)
962             if stat:
963                 print "attach " + realfile + " <-> " + dev                  
964                 run('losetup', dev, realfile)
965                 return dev
966         else:
967             print "out of loop devices"
968             return ''
969     print "out of loop devices"
970     return ''
971
972 # undo loop assignment
973 def clean_loop(dev, fstype, backfstype, backdev):
974     if fstype == 'smfs':
975         realfile = backdev
976     else:
977         realfile = dev
978     if not is_block(realfile):
979         dev = find_assigned_loop(realfile)
980         if dev:
981             print "detach " + dev + " <-> " + realfile
982             ret, out = run('losetup -d', dev)
983             if ret:
984                 log('unable to clean loop device:', dev, 'for file:', realfile)
985                 logall(out)
986
987 # finilizes passed device
988 def clean_dev(dev, fstype, backfstype, backdev):
989     if fstype == 'smfs' or not is_block(dev):
990         clean_loop(dev, fstype, backfstype, backdev)
991         
992 # determine if dev is formatted as a <fstype> filesystem
993 def need_format(fstype, dev):
994     # FIXME don't know how to implement this    
995     return 0
996
997 # initialize a block device if needed
998 def block_dev(dev, size, fstype, reformat, autoformat, journal_size,
999               inode_size, mkfsoptions, backfstype, backdev):
1000     if config.noexec: 
1001         return dev
1002         
1003     if fstype == 'smfs' or not is_block(dev):
1004         dev = init_loop(dev, size, fstype, journal_size, inode_size,
1005                         mkfsoptions, reformat, autoformat, backfstype, backdev)
1006     elif reformat or (need_format(fstype, dev) and autoformat == 'yes'):
1007         mkfs(dev, size, fstype, journal_size, inode_size, mkfsoptions,
1008              isblock=0)
1009 #    else:
1010 #        panic("device:", dev,
1011 #              "not prepared, and autoformat is not set.\n",
1012 #              "Rerun with --reformat option to format ALL filesystems")
1013         
1014     return dev
1015
1016 def if2addr(iface):
1017     """lookup IP address for an interface"""
1018     rc, out = run("/sbin/ifconfig", iface)
1019     if rc or not out:
1020        return None
1021     addr = string.split(out[1])[1]
1022     ip = string.split(addr, ':')[1]
1023     return ip
1024
1025 def def_mount_options(fstype, target):
1026     """returns deafult mount options for passed fstype and target (mds, ost)"""
1027     if fstype == 'ext3' or fstype == 'ldiskfs':
1028         mountfsoptions = "errors=remount-ro"
1029         if target == 'ost' and sys_get_branch() == '2.4':
1030             mountfsoptions = "%s,asyncdel" % (mountfsoptions)
1031         return mountfsoptions
1032     return ""
1033         
1034 def sys_get_elan_position_file():
1035     procfiles = ["/proc/elan/device0/position",
1036                  "/proc/qsnet/elan4/device0/position",
1037                  "/proc/qsnet/elan3/device0/position"]
1038     for p in procfiles:
1039         if os.access(p, os.R_OK):
1040             return p
1041     return ""
1042
1043 def sys_get_local_nid(net_type, wildcard, cluster_id):
1044     """Return the local nid."""
1045     local = ""
1046     if sys_get_elan_position_file():
1047         local = sys_get_local_address('elan', '*', cluster_id)
1048     else:
1049         local = sys_get_local_address(net_type, wildcard, cluster_id)
1050     return local
1051         
1052 def sys_get_local_address(net_type, wildcard, cluster_id):
1053     """Return the local address for the network type."""
1054     local = ""
1055     if net_type in ('tcp','openib','iib','vib','ra'):
1056         if  ':' in wildcard:
1057             iface, star = string.split(wildcard, ':')
1058             local = if2addr(iface)
1059             if not local:
1060                 panic ("unable to determine ip for:", wildcard)
1061         else:
1062             host = socket.gethostname()
1063             local = socket.gethostbyname(host)
1064     elif net_type == 'elan':
1065         # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()'
1066         f = sys_get_elan_position_file()
1067         if not f:
1068             panic ("unable to determine local Elan ID")
1069         try:
1070             fp = open(f, 'r')
1071             lines = fp.readlines()
1072             fp.close()
1073             for l in lines:
1074                 a = string.split(l)
1075                 if a[0] == 'NodeId':
1076                     elan_id = a[1]
1077                     break
1078             try:
1079                 nid = my_int(cluster_id) + my_int(elan_id) 
1080                 local = "%d" % (nid)
1081             except ValueError, e:
1082                 local = elan_id
1083         except IOError, e:
1084             log(e)
1085     elif net_type == 'lo':
1086         fixme("automatic local address for loopback")
1087     elif net_type == 'gm':
1088         fixme("automatic local address for GM")
1089
1090     return local
1091
1092 def sys_get_branch():
1093     """Returns kernel release"""
1094     try:
1095         fp = open('/proc/sys/kernel/osrelease')
1096         lines = fp.readlines()
1097         fp.close()
1098         
1099         for l in lines:
1100             version = string.split(l)
1101             a = string.split(version[0], '.')
1102             return a[0] + '.' + a[1]
1103     except IOError, e:
1104         log(e)
1105     return ""
1106
1107 # XXX: instead of device_list, ask for $name and see what we get
1108 def is_prepared(name):
1109     """Return true if a device exists for the name"""
1110     if config.lctl_dump:
1111         return 0
1112     if (config.noexec or config.record) and config.cleanup:
1113         return 1
1114     try:
1115         # expect this format:
1116         # 1 UP ldlm ldlm ldlm_UUID 2
1117         out = lctl.device_list()
1118         for s in out:
1119             if name == string.split(s)[3]:
1120                 return 1
1121     except CommandError, e:
1122         e.dump()
1123     return 0
1124
1125 def net_is_prepared():
1126     """If the any device exists, then assume that all networking
1127        has been configured"""
1128     out = lctl.device_list()
1129     return len(out) > 0
1130
1131 def fs_is_mounted(path):
1132     """Return true if path is a mounted lustre filesystem"""
1133     try:
1134         fp = open('/proc/mounts')
1135         lines = fp.readlines()
1136         fp.close()
1137         for l in lines:
1138             a = string.split(l)
1139             if a[1] == path and a[2] == 'lustre_lite':
1140                 return 1
1141     except IOError, e:
1142         log(e)
1143     return 0
1144
1145 def kmod_find(src_dir, dev_dir, modname):
1146     modbase = src_dir +'/'+ dev_dir +'/'+ modname
1147     for modext in '.ko', '.o':
1148         module = modbase + modext
1149         try:
1150             if os.access(module, os.R_OK):
1151                 return module
1152         except OSError:
1153                pass
1154     return None
1155
1156 def kmod_info(modname):
1157     """Returns reference count for passed module name."""
1158     try:
1159         fp = open('/proc/modules')
1160         lines = fp.readlines()
1161         fp.close()
1162         
1163         # please forgive my tired fingers for this one
1164         ret = filter(lambda word, mod = modname: word[0] == mod,
1165                      map(lambda line: string.split(line), lines))
1166         if not ret:
1167             return ''
1168         return ret[0]
1169     except Exception, e:
1170         return 0
1171
1172 class kmod:
1173     """Presents kernel module"""
1174     def __init__(self, src_dir, dev_dir, name):
1175         self.src_dir = src_dir
1176         self.dev_dir = dev_dir
1177         self.name = name
1178
1179     def load(self):
1180         """Load module"""
1181         log ('loading module:', self.name, 'srcdir',
1182              self.src_dir, 'devdir', self.dev_dir)
1183         if self.src_dir:
1184             module = kmod_find(self.src_dir, self.dev_dir,
1185                                self.name)
1186             if not module:
1187                 panic('module not found:', self.name)
1188             (rc, out)  = run('/sbin/insmod', module)
1189             if rc:
1190                 raise CommandError('insmod', out, rc)
1191         else:
1192             (rc, out) = run('/sbin/modprobe', self.name)
1193             if rc:
1194                 raise CommandError('modprobe', out, rc)
1195
1196     def cleanup(self):
1197         """Unload module"""
1198         log('unloading module:', self.name)
1199         (rc, out) = run('/sbin/rmmod', self.name)
1200         if rc:
1201             log('unable to unload module:', self.name +
1202                 "(" + self.refcount() + ")")
1203             logall(out)
1204
1205     def info(self):
1206         """Returns module info if any."""
1207         return kmod_info(self.name)
1208
1209     def loaded(self):
1210         """Returns 1 if module is loaded. Otherwise 0 is returned."""
1211         if self.info():
1212             return 1
1213         else:
1214             return 0
1215
1216     def refcount(self):
1217         """Returns module refcount."""
1218         info = self.info()
1219         if not info:
1220             return ''
1221         return info[2]
1222
1223     def used(self):
1224         """Returns 1 if module is used, otherwise 0 is returned."""
1225         info = self.info()
1226         if not info:
1227             return 0
1228         if len(info) > 3:
1229             users = info[3]
1230             if users and users != '(unused)' and users != '-':
1231                 return 1
1232             else:
1233                 return 0
1234         else:
1235             return 0
1236
1237     def busy(self):
1238         """Returns 1 if module is busy, otherwise 0 is returned."""
1239         if self.loaded() and (self.used() or self.refcount() != '0'):
1240             return 1
1241         else:
1242             return 0
1243
1244 class kmod_manager:
1245     """Manage kernel modules"""
1246     def __init__(self, lustre_dir, portals_dir):
1247         self.lustre_dir = lustre_dir
1248         self.portals_dir = portals_dir
1249         self.kmodule_list = []
1250
1251     def find_module(self, modname):
1252         """Find module by module name"""
1253         for mod in self.kmodule_list:
1254             if mod.name == modname:
1255                 return mod
1256         return ''
1257         
1258     def add_portals_module(self, dev_dir, modname):
1259         """Append a module to list of modules to load."""
1260
1261         mod = self.find_module(modname)
1262         if not mod:
1263             mod = kmod(self.portals_dir, dev_dir, modname)
1264             self.kmodule_list.append(mod)
1265
1266     def add_lustre_module(self, dev_dir, modname):
1267         """Append a module to list of modules to load."""
1268
1269         mod = self.find_module(modname)
1270         if not mod:
1271             mod = kmod(self.lustre_dir, dev_dir, modname)
1272             self.kmodule_list.append(mod)
1273         
1274     def load_modules(self):
1275         """Load all the modules in the list in the order they appear."""
1276         for mod in self.kmodule_list:
1277             if mod.loaded() and not config.noexec:
1278                 continue
1279             mod.load()
1280
1281     def cleanup_modules(self):
1282         """Unload the modules in the list in reverse order."""
1283         rev = self.kmodule_list
1284         rev.reverse()
1285         for mod in rev:
1286             if (not mod.loaded() or mod.busy()) and not config.noexec:
1287                 continue
1288             # debug hack
1289             if mod.name == 'portals' and config.dump:
1290                 lctl.dump(config.dump)
1291             mod.cleanup()
1292             
1293 # ============================================================
1294 # Classes to prepare and cleanup the various objects
1295 #
1296 class Module:
1297     """ Base class for the rest of the modules. The default cleanup method is
1298     defined here, as well as some utilitiy funcs.
1299     """
1300     def __init__(self, module_name, db):
1301         self.db = db
1302         self.module_name = module_name
1303         self.name = self.db.getName()
1304         self.uuid = self.db.getUUID()
1305         self._server = None
1306         self._connected = 0
1307
1308     def info(self, *args):
1309         msg = string.join(map(str,args))
1310         print self.module_name + ":", self.name, self.uuid, msg
1311
1312     def cleanup(self):
1313         """ default cleanup, used for most modules """
1314         self.info()
1315         try:
1316             lctl.cleanup(self.name, self.uuid, config.force)
1317         except CommandError, e:
1318             log(self.module_name, "cleanup failed: ", self.name)
1319             e.dump()
1320             cleanup_error(e.rc)
1321
1322     def add_module(self, manager):
1323         """Adds all needed modules in the order they appear."""
1324         return
1325
1326     def safe_to_clean(self):
1327         return 1
1328
1329     def safe_to_clean_modules(self):
1330         return self.safe_to_clean()
1331         
1332 class Network(Module):
1333     def __init__(self,db):
1334         Module.__init__(self, 'NETWORK', db)
1335         self.net_type = self.db.get_val('nettype')
1336         self.nid = self.db.get_val('nid', '*')
1337         self.cluster_id = self.db.get_val('clusterid', "0")
1338         self.port = self.db.get_val_int('port', 0)
1339
1340         if '*' in self.nid:
1341             self.nid = sys_get_local_nid(self.net_type, self.nid, self.cluster_id)
1342             if not self.nid:
1343                 panic("unable to set nid for", self.net_type, self.nid, cluster_id)
1344             self.generic_nid = 1
1345             debug("nid:", self.nid)
1346         else:
1347             self.generic_nid = 0
1348
1349         self.nid_uuid = self.nid_to_uuid(self.nid)
1350         self.hostaddr = self.db.get_hostaddr()
1351         if len(self.hostaddr) == 0:
1352             self.hostaddr.append(self.nid)
1353         if '*' in self.hostaddr[0]:
1354             self.hostaddr[0] = sys_get_local_address(self.net_type, self.hostaddr[0], self.cluster_id)
1355             if not self.hostaddr[0]:
1356                 panic("unable to set hostaddr for", self.net_type, self.hostaddr[0], self.cluster_id)
1357             debug("hostaddr:", self.hostaddr[0])
1358
1359     def add_module(self, manager):
1360         manager.add_portals_module("libcfs", 'libcfs')
1361         manager.add_portals_module("portals", 'portals')
1362         if node_needs_router():
1363             manager.add_portals_module("router", 'kptlrouter')
1364         if self.net_type == 'tcp':
1365             manager.add_portals_module("knals/socknal", 'ksocknal')
1366         if self.net_type == 'elan':
1367             manager.add_portals_module("knals/qswnal", 'kqswnal')
1368         if self.net_type == 'gm':
1369             manager.add_portals_module("knals/gmnal", 'kgmnal')
1370         if self.net_type == 'openib':
1371             manager.add_portals_module("knals/openibnal", 'kopenibnal')
1372         if self.net_type == 'iib':
1373             manager.add_portals_module("knals/iibnal", 'kiibnal')
1374         if self.net_type == 'vib':
1375             self.add_portals_module("knals/vibnal", 'kvibnal')
1376         if self.net_type == 'lo':
1377             manager.add_portals_module("knals/lonal", 'klonal')
1378         if self.net_type == 'ra':
1379             manager.add_portals_module("knals/ranal", 'kranal')
1380
1381     def nid_to_uuid(self, nid):
1382         return "NID_%s_UUID" %(nid,)
1383
1384     def prepare(self):
1385         if not config.record and net_is_prepared():
1386             return
1387         self.info(self.net_type, self.nid, self.port)
1388         if not (config.record and self.generic_nid):
1389             lctl.network(self.net_type, self.nid)
1390         if self.net_type == 'tcp':
1391             sys_tweak_socknal()
1392             for hostaddr in self.db.get_hostaddr():
1393                 ip = string.split(hostaddr, '/')[0]
1394                 if len(string.split(hostaddr, '/')) == 2:
1395                     netmask = string.split(hostaddr, '/')[1]
1396                 else:
1397                     netmask = ""
1398                 lctl.add_interface(self.net_type, ip, netmask)
1399         if self.net_type == 'elan':
1400             sys_optimize_elan()
1401         if self.port and  node_is_router():
1402             run_one_acceptor(self.port)
1403             self.connect_peer_gateways()
1404
1405     def connect_peer_gateways(self):
1406         for router in self.db.lookup_class('node'):
1407             if router.get_val_int('router', 0):
1408                 for netuuid in router.get_networks():
1409                     net = self.db.lookup(netuuid)
1410                     gw = Network(net)
1411                     if (gw.cluster_id == self.cluster_id and
1412                         gw.net_type == self.net_type):
1413                         if gw.nid != self.nid:
1414                             lctl.connect(gw)
1415
1416     def disconnect_peer_gateways(self):
1417         for router in self.db.lookup_class('node'):
1418             if router.get_val_int('router', 0):
1419                 for netuuid in router.get_networks():
1420                     net = self.db.lookup(netuuid)
1421                     gw = Network(net)
1422                     if (gw.cluster_id == self.cluster_id and
1423                         gw.net_type == self.net_type):
1424                         if gw.nid != self.nid:
1425                             try:
1426                                 lctl.disconnect(gw)
1427                             except CommandError, e:
1428                                 print "disconnect failed: ", self.name
1429                                 e.dump()
1430                                 cleanup_error(e.rc)
1431
1432     def safe_to_clean(self):
1433         return not net_is_prepared()
1434
1435     def cleanup(self):
1436         self.info(self.net_type, self.nid, self.port)
1437         if self.port:
1438             stop_acceptor(self.port)
1439         if  node_is_router():
1440             self.disconnect_peer_gateways()
1441         if self.net_type == 'tcp':
1442             for hostaddr in self.db.get_hostaddr():
1443                 ip = string.split(hostaddr, '/')[0]
1444                 lctl.del_interface(self.net_type, ip)
1445
1446     def correct_level(self, level, op=None):
1447         return level
1448
1449 class RouteTable(Module):
1450     def __init__(self,db):
1451         Module.__init__(self, 'ROUTES', db)
1452
1453     def server_for_route(self, net_type, gw, gw_cluster_id, tgt_cluster_id,
1454                          lo, hi):
1455         # only setup connections for tcp, openib, and iib NALs
1456         srvdb = None
1457         if not net_type in ('tcp','openib','iib','vib','ra'):
1458             return None
1459
1460         # connect to target if route is to single node and this node is the gw
1461         if lo == hi and local_interface(net_type, gw_cluster_id, gw):
1462             if not local_cluster(net_type, tgt_cluster_id):
1463                 panic("target", lo, " not on the local cluster")
1464             srvdb = self.db.nid2server(lo, net_type, gw_cluster_id)
1465         # connect to gateway if this node is not the gw
1466         elif (local_cluster(net_type, gw_cluster_id)
1467               and not local_interface(net_type, gw_cluster_id, gw)):
1468             srvdb = self.db.nid2server(gw, net_type, gw_cluster_id)
1469         else:
1470             return None
1471
1472         if not srvdb:
1473             panic("no server for nid", lo)
1474             return None
1475
1476         return Network(srvdb)
1477         
1478     def prepare(self):
1479         if not config.record and net_is_prepared():
1480             return
1481         self.info()
1482         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1483             lctl.add_route(net_type, gw, lo, hi)
1484             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1485             if srv:
1486                 lctl.connect(srv)
1487
1488     def safe_to_clean(self):
1489         return not net_is_prepared()
1490
1491     def cleanup(self):
1492         if net_is_prepared():
1493             # the network is still being used, don't clean it up
1494             return
1495         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1496             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1497             if srv:
1498                 try:
1499                     lctl.disconnect(srv)
1500                 except CommandError, e:
1501                     print "disconnect failed: ", self.name
1502                     e.dump()
1503                     cleanup_error(e.rc)
1504
1505             try:
1506                 lctl.del_route(net_type, gw, lo, hi)
1507             except CommandError, e:
1508                 print "del_route failed: ", self.name
1509                 e.dump()
1510                 cleanup_error(e.rc)
1511
1512 class Management(Module):
1513     def __init__(self, db):
1514         Module.__init__(self, 'MGMT', db)
1515
1516     def add_module(self, manager):
1517         manager.add_lustre_module('lvfs', 'lvfs')
1518         manager.add_lustre_module('obdclass', 'obdclass')
1519         manager.add_lustre_module('ptlrpc', 'ptlrpc')
1520         manager.add_lustre_module('mgmt', 'mgmt_svc')
1521
1522     def prepare(self):
1523         if not config.record and is_prepared(self.name):
1524             return
1525         self.info()
1526         lctl.newdev("mgmt", self.name, self.uuid)
1527
1528     def safe_to_clean(self):
1529         return 1
1530
1531     def cleanup(self):
1532         if is_prepared(self.name):
1533             Module.cleanup(self)
1534
1535     def correct_level(self, level, op=None):
1536         return level
1537
1538 # This is only needed to load the modules; the LDLM device
1539 # is now created automatically.
1540 class LDLM(Module):
1541     def __init__(self,db):
1542         Module.__init__(self, 'LDLM', db)
1543
1544     def add_module(self, manager):
1545         manager.add_lustre_module('lvfs', 'lvfs')
1546         manager.add_lustre_module('obdclass', 'obdclass')
1547         manager.add_lustre_module('ptlrpc', 'ptlrpc')
1548
1549     def prepare(self):
1550         return
1551
1552     def cleanup(self):
1553         return
1554
1555     def correct_level(self, level, op=None):
1556         return level
1557
1558 class LOV(Module):
1559     def __init__(self, db, uuid, fs_name, name_override = None, config_only = None):
1560         Module.__init__(self, 'LOV', db)
1561         if name_override != None:
1562             self.name = "lov_%s" % name_override
1563         self.mds_uuid = self.db.get_first_ref('mds')
1564         self.stripe_sz = self.db.get_val_int('stripesize', 1048576)
1565         self.stripe_off = self.db.get_val_int('stripeoffset', 0)
1566         self.pattern = self.db.get_val_int('stripepattern', 0)
1567         self.devlist = self.db.get_lov_tgts('lov_tgt')
1568         self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist))
1569         self.osclist = []
1570         self.obdlist = []
1571         self.desc_uuid = self.uuid
1572         self.uuid = generate_client_uuid(self.name)
1573         self.fs_name = fs_name
1574         if config_only:
1575             self.config_only = 1
1576             return
1577         self.config_only = None
1578         mds = self.db.lookup(self.mds_uuid)
1579         self.mds_name = mds.getName()
1580         for (obd_uuid, index, gen, active) in self.devlist:
1581             if obd_uuid == '':
1582                 continue
1583             self.obdlist.append(obd_uuid)
1584             obd = self.db.lookup(obd_uuid)
1585             osc = get_osc(obd, self.uuid, fs_name)
1586             if osc:
1587                 self.osclist.append((osc, index, gen, active))
1588             else:
1589                 panic('osc not found:', obd_uuid)
1590     def get_uuid(self):
1591         return self.uuid
1592     def get_name(self):
1593         return self.name
1594     def prepare(self):
1595         if not config.record and is_prepared(self.name):
1596             return
1597         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
1598                   self.stripe_off, self.pattern, self.devlist,
1599                   self.mds_name)
1600         lctl.lov_setup(self.name, self.uuid, self.desc_uuid,  self.stripe_cnt,
1601                        self.stripe_sz, self.stripe_off, self.pattern,
1602                        string.join(self.obdlist))
1603         for (osc, index, gen, active) in self.osclist:
1604             target_uuid = osc.target_uuid
1605             try:
1606                 # Only ignore connect failures with --force, which
1607                 # isn't implemented here yet.
1608                 osc.active = active
1609                 osc.prepare(ignore_connect_failure=0)
1610             except CommandError, e:
1611                 print "Error preparing OSC %s\n" % osc.uuid
1612                 raise e
1613             lctl.lov_add_obd(self.name, self.uuid, target_uuid, index, gen)
1614
1615     def cleanup(self):
1616         for (osc, index, gen, active) in self.osclist:
1617             target_uuid = osc.target_uuid
1618             osc.cleanup()
1619         if is_prepared(self.name):
1620             Module.cleanup(self)
1621         if self.config_only:
1622             panic("Can't clean up config_only LOV ", self.name)
1623
1624     def add_module(self, manager):
1625         if self.config_only:
1626             panic("Can't load modules for config_only LOV ", self.name)
1627         for (osc, index, gen, active) in self.osclist:
1628             osc.add_module(manager)
1629             break
1630         manager.add_lustre_module('lov', 'lov')
1631
1632     def correct_level(self, level, op=None):
1633         return level
1634
1635 class LMV(Module):
1636     def __init__(self, db, uuid, fs_name, name_override = None):
1637         Module.__init__(self, 'LMV', db)
1638         if name_override != None:
1639             self.name = "lmv_%s" % name_override
1640             
1641         self.devlist = self.db.get_lmv_tgts('lmv_tgt')
1642         if self.devlist == None:
1643             self.devlist = self.db.get_refs('mds')
1644             
1645         self.mdclist = []
1646         self.desc_uuid = self.uuid
1647         self.uuid = uuid
1648         self.fs_name = fs_name
1649         for mds_uuid in self.devlist:
1650             mds = self.db.lookup(mds_uuid)
1651             if not mds:
1652                 panic("MDS not found!")
1653             mdc = MDC(mds, self.uuid, fs_name)
1654             if mdc:
1655                  self.mdclist.append(mdc)
1656             else:
1657                  panic('mdc not found:', mds_uuid)
1658
1659     def prepare(self):
1660         if is_prepared(self.name):
1661             return
1662             
1663         self.info();
1664         for mdc in self.mdclist:
1665             try:
1666                 # Only ignore connect failures with --force, which
1667                 # isn't implemented here yet.
1668                 mdc.prepare(ignore_connect_failure=0)
1669             except CommandError, e:
1670                 print "Error preparing LMV %s\n" % mdc.uuid
1671                 raise e
1672         
1673         lctl.lmv_setup(self.name, self.uuid, self.desc_uuid,
1674                        string.join(self.devlist))
1675
1676     def cleanup(self):
1677         for mdc in self.mdclist:
1678             mdc.cleanup()
1679         if is_prepared(self.name):
1680             Module.cleanup(self)
1681
1682     def add_module(self, manager):
1683         for mdc in self.mdclist:
1684             mdc.add_module(manager)
1685             break
1686         manager.add_lustre_module('lmv', 'lmv')
1687
1688     def correct_level(self, level, op=None):
1689         return level
1690
1691 class MDSDEV(Module):
1692     def __init__(self,db):
1693         Module.__init__(self, 'MDSDEV', db)
1694         self.devpath = self.db.get_val('devpath','')
1695         self.backdevpath = self.db.get_val('backdevpath','')
1696         self.size = self.db.get_val_int('devsize', 0)
1697         self.journal_size = self.db.get_val_int('journalsize', 0)
1698         self.fstype = self.db.get_val('fstype', '')
1699         self.backfstype = self.db.get_val('backfstype', '')
1700         self.nspath = self.db.get_val('nspath', '')
1701         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1702         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
1703         self.obdtype = self.db.get_val('obdtype', '')
1704         self.root_squash = self.db.get_val('root_squash', '')
1705         self.no_root_squash = self.db.get_val('no_root_squash', '')
1706         # overwrite the orignal MDSDEV name and uuid with the MDS name and uuid
1707         target_uuid = self.db.get_first_ref('target')
1708         self.mds = self.db.lookup(target_uuid)
1709         self.name = self.mds.getName()
1710         self.client_uuids = self.mds.get_refs('client')
1711         
1712         self.lmv = None
1713         self.master = None
1714         
1715         lmv_uuid = self.db.get_first_ref('lmv')
1716         if lmv_uuid != None:
1717             self.lmv = self.db.lookup(lmv_uuid)
1718             if self.lmv != None:
1719                 self.client_uuids = self.lmv.get_refs('client')
1720
1721         # FIXME: if fstype not set, then determine based on kernel version
1722         self.format = self.db.get_val('autoformat', "no")
1723         if self.mds.get_val('failover', 0):
1724             self.failover_mds = 'f'
1725         else:
1726             self.failover_mds = 'n'
1727         active_uuid = get_active_target(self.mds)
1728         if not active_uuid:
1729             panic("No target device found:", target_uuid)
1730         if active_uuid == self.uuid:
1731             self.active = 1
1732         else:
1733             self.active = 0
1734         if self.active and config.group and config.group != self.mds.get_val('group'):
1735             self.active = 0
1736
1737         # default inode inode for case when neither LOV either 
1738         # LMV is accessible.
1739         self.inode_size = 256
1740         
1741         inode_size = self.db.get_val_int('inodesize', 0)
1742         if not inode_size == 0:
1743             self.inode_size = inode_size
1744         else:
1745             # find the LOV for this MDS
1746             lovconfig_uuid = self.mds.get_first_ref('lovconfig')
1747             if lovconfig_uuid or self.lmv != None:
1748                 if self.lmv != None:
1749                     lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1750                     lovconfig = self.lmv.lookup(lovconfig_uuid)
1751                     lov_uuid = lovconfig.get_first_ref('lov')
1752                     if lov_uuid == None:
1753                         panic(self.mds.getName() + ": No LOV found for lovconfig ", 
1754                               lovconfig.name)
1755                 else:
1756                     lovconfig = self.mds.lookup(lovconfig_uuid)
1757                     lov_uuid = lovconfig.get_first_ref('lov')
1758                     if lov_uuid == None:
1759                        panic(self.mds.getName() + ": No LOV found for lovconfig ", 
1760                              lovconfig.name)
1761
1762                     if self.lmv != None:
1763                         lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1764                         lovconfig = self.lmv.lookup(lovconfig_uuid)
1765                         lov_uuid = lovconfig.get_first_ref('lov')
1766
1767                 lov = LOV(self.db.lookup(lov_uuid), lov_uuid, self.name, 
1768                           config_only = 1)
1769
1770                 # default stripe count controls default inode_size
1771                 stripe_count = lov.stripe_cnt
1772                 if stripe_count > 77:
1773                     self.inode_size = 4096
1774                 elif stripe_count > 35:
1775                     self.inode_size = 2048
1776                 elif stripe_count > 13:
1777                     self.inode_size = 1024
1778                 elif stripe_count > 3:
1779                     self.inode_size = 512
1780                 else:
1781                     self.inode_size = 256
1782
1783         self.target_dev_uuid = self.uuid
1784         self.uuid = target_uuid
1785
1786         # setup LMV
1787         if self.lmv != None:
1788             client_uuid = self.name + "_lmv_UUID"
1789             self.master = LMV(self.lmv, client_uuid, 
1790                               self.name, self.name)
1791
1792     def add_module(self, manager):
1793         if self.active:
1794             manager.add_lustre_module('mdc', 'mdc')
1795             manager.add_lustre_module('osc', 'osc')
1796             manager.add_lustre_module('ost', 'ost')
1797             manager.add_lustre_module('lov', 'lov')
1798             manager.add_lustre_module('mds', 'mds')
1799
1800             if self.fstype == 'smfs' or self.fstype == 'ldiskfs':
1801                 manager.add_lustre_module(self.fstype, self.fstype)
1802                 
1803             if self.fstype:
1804                 manager.add_lustre_module('lvfs', 'fsfilt_%s' % (self.fstype))
1805             
1806             # if fstype is smfs, then we should also take care about backing 
1807             # store fs.
1808             if self.fstype == 'smfs':
1809                 manager.add_lustre_module(self.backfstype, self.backfstype)
1810                 manager.add_lustre_module('lvfs', 'fsfilt_%s' % (self.backfstype))
1811
1812             for option in string.split(self.mountfsoptions, ','):
1813                 if option == 'snap':
1814                     if not self.fstype == 'smfs':
1815                         panic("mountoptions has 'snap', but fstype is not smfs.")
1816                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.fstype))
1817                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.backfstype))
1818
1819         # add LMV modules
1820         if self.master != None:
1821             self.master.add_module(manager)
1822             
1823     def get_mount_options(self, blkdev):
1824         options = def_mount_options(self.fstype, 'mds')
1825             
1826         if config.mountfsoptions:
1827             if options:
1828                 options = "%s,%s" %(options, config.mountfsoptions)
1829             else:
1830                 options = config.mountfsoptions
1831             if self.mountfsoptions:
1832                 options = "%s,%s" %(options, self.mountfsoptions)
1833         else:
1834             if self.mountfsoptions:
1835                 if options:
1836                     options = "%s,%s" %(options, self.mountfsoptions)
1837                 else:
1838                     options = self.mountfsoptions
1839             
1840         if self.fstype == 'smfs':
1841             if options:
1842                 options = "%s,type=%s,dev=%s" %(options, 
1843                            self.backfstype, blkdev)
1844             else:
1845                 options = "type=%s,dev=%s" %(self.backfstype, blkdev)
1846         return options
1847         
1848     def prepare(self):
1849         if not config.record and is_prepared(self.name):
1850             return
1851         if not self.active:
1852             debug(self.uuid, "not active")
1853             return
1854         if config.reformat:
1855             # run write_conf automatically, if --reformat used
1856             self.write_conf()
1857         run_acceptors()
1858         
1859         # prepare LMV
1860         if self.master != None:
1861              self.master.prepare()
1862              
1863         # never reformat here
1864         blkdev = block_dev(self.devpath, self.size, self.fstype, 0,
1865                            self.format, self.journal_size, self.inode_size,
1866                            self.mkfsoptions, self.backfstype, self.backdevpath)
1867         
1868         if not is_prepared('MDT'):
1869             lctl.newdev("mdt", 'MDT', 'MDT_UUID', setup ="")
1870         try: 
1871             if self.fstype == 'smfs':
1872                 realdev = self.fstype
1873             else:
1874                 realdev = blkdev
1875                 
1876             if self.obdtype == None:
1877                 self.obdtype = 'dumb'
1878                 
1879             if self.master == None:
1880                 master_name = 'dumb'
1881             else:
1882                 master_name = self.master.name
1883                 
1884             if self.client_uuids == None:
1885                 profile_name = 'dumb'
1886             else:
1887                 profile_name = self.name
1888             
1889             mountfsoptions = self.get_mount_options(blkdev)
1890
1891             self.info("mds", realdev, mountfsoptions, self.fstype, self.size, 
1892                       self.format, master_name, profile_name, self.obdtype)
1893             
1894             lctl.newdev("mds", self.name, self.uuid,
1895                         setup = "%s %s %s %s %s %s" %(realdev, 
1896                             self.fstype, profile_name, mountfsoptions,
1897                             master_name, self.obdtype))
1898
1899             if development_mode():
1900                 procentry = "/proc/fs/lustre/mds/grp_hash_upcall"
1901                 upcall = os.path.abspath(os.path.dirname(sys.argv[0]) + "/l_getgroups")
1902                 if not (os.access(procentry, os.R_OK) and os.access(upcall, os.R_OK)):
1903                     print "MDS Warning: failed to set group-hash upcall"
1904                 else:
1905                     run("echo ", upcall, " > ", procentry)
1906
1907         except CommandError, e:
1908             if e.rc == 2:
1909                 panic("MDS is missing the config log. Need to run " +
1910                        "lconf --write_conf.")
1911             else:
1912                 raise e
1913         
1914         if config.root_squash == None:
1915             config.root_squash = self.root_squash
1916         if config.no_root_squash == None:
1917             config.no_root_squash = self.no_root_squash
1918         if config.root_squash:
1919             if config.no_root_squash:
1920                 nsnid = config.no_root_squash
1921             else:
1922                 nsnid = "0"
1923             lctl.root_squash(self.name, config.root_squash, nsnid)
1924
1925     def write_conf(self):
1926         if not self.client_uuids:
1927             return 0
1928             
1929         do_cleanup = 0
1930         if not is_prepared(self.name):
1931             blkdev = block_dev(self.devpath, self.size, self.fstype,
1932                                config.reformat, self.format, self.journal_size,
1933                                self.inode_size, self.mkfsoptions,
1934                                self.backfstype, self.backdevpath)
1935
1936             if self.fstype == 'smfs':
1937                 realdev = self.fstype
1938             else:
1939                 realdev = blkdev
1940             
1941             # Even for writing logs we mount mds with supplied mount options
1942             # because it will not mount smfs (if used) otherwise.
1943             mountfsoptions = self.get_mount_options(blkdev)
1944
1945             if self.obdtype == None:
1946                 self.obdtype = 'dumb'
1947                 
1948             self.info("mds", realdev, mountfsoptions, self.fstype, self.size, 
1949                       self.format, "dumb", "dumb", self.obdtype)
1950             
1951             lctl.newdev("mds", self.name, self.uuid,
1952                         setup ="%s %s %s %s %s %s" %(realdev, self.fstype, 
1953                                                      'dumb', mountfsoptions,
1954                                                      'dumb', self.obdtype))
1955             do_cleanup = 1
1956
1957         # record logs for all MDS clients
1958         for obd_uuid in self.client_uuids:
1959             log("recording client:", obd_uuid)
1960
1961             client_uuid = generate_client_uuid(self.name)
1962             client = VOSC(self.db.lookup(obd_uuid), client_uuid, 
1963                           self.name, self.name)
1964             config.record = 1
1965             lctl.clear_log(self.name, self.name)
1966             lctl.record(self.name, self.name)
1967             client.prepare()
1968             lctl.mount_option(self.name, client.get_name(), "")
1969             lctl.end_record()
1970             process_updates(self.db, self.name, self.name, client)
1971
1972             config.cleanup = 1
1973             lctl.clear_log(self.name, self.name + '-clean')
1974             lctl.record(self.name, self.name + '-clean')
1975             client.cleanup()
1976             lctl.del_mount_option(self.name)
1977             lctl.end_record()
1978             process_updates(self.db, self.name, self.name + '-clean', client)
1979             config.cleanup = 0
1980             config.record = 0
1981
1982         # record logs for each client
1983         if config.noexec:
1984             noexec_opt = '-n'
1985         else:
1986             noexec_opt = ''
1987         if config.ldapurl:
1988             config_options = "--ldapurl " + config.ldapurl + " --config " + config.config
1989         else:
1990             config_options = CONFIG_FILE
1991
1992         for node_db in self.db.lookup_class('node'):
1993             client_name = node_db.getName()
1994             for prof_uuid in node_db.get_refs('profile'):
1995                 prof_db = node_db.lookup(prof_uuid)
1996                 # refactor this into a funtion to test "clientness"
1997                 # of a node.
1998                 for ref_class, ref_uuid in prof_db.get_all_refs():
1999                     if ref_class in ('mountpoint','echoclient'):
2000                         debug("recording", client_name)
2001                         old_noexec = config.noexec
2002                         config.noexec = 0
2003                         ret, out = run (sys.argv[0], noexec_opt,
2004                                         " -v --record --nomod",
2005                                         "--record_log", client_name,
2006                                         "--record_device", self.name,
2007                                         "--node", client_name,
2008                                         config_options)
2009                         if config.verbose:
2010                             for s in out: log("record> ", string.strip(s))
2011                         ret, out = run (sys.argv[0], noexec_opt,
2012                                         "--cleanup -v --record --nomod",
2013                                         "--record_log", client_name + "-clean",
2014                                         "--record_device", self.name,
2015                                         "--node", client_name,
2016                                         config_options)
2017                         if config.verbose:
2018                             for s in out: log("record> ", string.strip(s))
2019                         config.noexec = old_noexec
2020         if do_cleanup:
2021             try:
2022                 lctl.cleanup(self.name, self.uuid, 0, 0)
2023             except CommandError, e:
2024                 log(self.module_name, "cleanup failed: ", self.name)
2025                 e.dump()
2026                 cleanup_error(e.rc)
2027                 Module.cleanup(self)
2028
2029             clean_dev(self.devpath, self.fstype, self.backfstype, 
2030                       self.backdevpath)
2031
2032     def msd_remaining(self):
2033         out = lctl.device_list()
2034         for s in out:
2035             if string.split(s)[2] in ('mds',):
2036                 return 1
2037
2038     def safe_to_clean(self):
2039         return self.active
2040
2041     def safe_to_clean_modules(self):
2042         return not self.msd_remaining()
2043         
2044     def cleanup(self):
2045         if not self.active:
2046             debug(self.uuid, "not active")
2047             return
2048         self.info()
2049         if is_prepared(self.name):
2050             try:
2051                 lctl.cleanup(self.name, self.uuid, config.force,
2052                              config.failover)
2053             except CommandError, e:
2054                 log(self.module_name, "cleanup failed: ", self.name)
2055                 e.dump()
2056                 cleanup_error(e.rc)
2057                 Module.cleanup(self)
2058             # cleanup LMV
2059             if self.master != None:
2060                 self.master.cleanup()
2061         if not self.msd_remaining() and is_prepared('MDT'):
2062             try:
2063                 lctl.cleanup("MDT", "MDT_UUID", config.force,
2064                              config.failover)
2065             except CommandError, e:
2066                 print "cleanup failed: ", self.name
2067                 e.dump()
2068                 cleanup_error(e.rc)
2069         
2070         clean_dev(self.devpath, self.fstype, self.backfstype, 
2071                   self.backdevpath)
2072
2073     def correct_level(self, level, op=None):
2074         #if self.master != None:
2075         #   level = level + 2
2076         return level
2077
2078 class OSD(Module):
2079     def __init__(self, db):
2080         Module.__init__(self, 'OSD', db)
2081         self.osdtype = self.db.get_val('osdtype')
2082         self.devpath = self.db.get_val('devpath', '')
2083         self.backdevpath = self.db.get_val('backdevpath', '')
2084         self.size = self.db.get_val_int('devsize', 0)
2085         self.journal_size = self.db.get_val_int('journalsize', 0)
2086         self.inode_size = self.db.get_val_int('inodesize', 0)
2087         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
2088         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
2089         self.fstype = self.db.get_val('fstype', '')
2090         self.backfstype = self.db.get_val('backfstype', '')
2091         self.nspath = self.db.get_val('nspath', '')
2092         target_uuid = self.db.get_first_ref('target')
2093         ost = self.db.lookup(target_uuid)
2094         self.name = ost.getName()
2095         self.format = self.db.get_val('autoformat', 'yes')
2096         if ost.get_val('failover', 0):
2097             self.failover_ost = 'f'
2098         else:
2099             self.failover_ost = 'n'
2100
2101         active_uuid = get_active_target(ost)
2102         if not active_uuid:
2103             panic("No target device found:", target_uuid)
2104         if active_uuid == self.uuid:
2105             self.active = 1
2106         else:
2107             self.active = 0
2108         if self.active and config.group and config.group != ost.get_val('group'):
2109             self.active = 0
2110
2111         self.target_dev_uuid = self.uuid
2112         self.uuid = target_uuid
2113     
2114     def add_module(self, manager):
2115         if self.active:
2116             manager.add_lustre_module('ost', 'ost')
2117             
2118             if self.fstype == 'smfs' or self.fstype == 'ldiskfs':
2119                 manager.add_lustre_module(self.fstype, self.fstype)
2120                 
2121             if self.fstype:
2122                 manager.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.fstype))
2123
2124             if self.fstype == 'smfs':
2125                 manager.add_lustre_module(self.backfstype, self.backfstype)
2126                 manager.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.backfstype))
2127
2128             for option in self.mountfsoptions:
2129                 if option == 'snap':
2130                     if not self.fstype == 'smfs':
2131                         panic("mountoptions with snap, but fstype is not smfs\n")
2132                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.fstype))
2133                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.backfstype))
2134
2135             manager.add_lustre_module(self.osdtype, self.osdtype)
2136
2137     def get_mount_options(self, blkdev):
2138         options = def_mount_options(self.fstype, 'ost')
2139             
2140         if config.mountfsoptions:
2141             if options:
2142                 options = "%s,%s" %(options, config.mountfsoptions)
2143             else:
2144                 options = config.mountfsoptions
2145             if self.mountfsoptions:
2146                 options = "%s,%s" %(options, self.mountfsoptions)
2147         else:
2148             if self.mountfsoptions:
2149                 if options:
2150                     options = "%s,%s" %(options, self.mountfsoptions)
2151                 else:
2152                     options = self.mountfsoptions
2153             
2154         if self.fstype == 'smfs':
2155             if options:
2156                 options = "%s,type=%s,dev=%s" %(options, 
2157                     self.backfstype, blkdev)
2158             else:
2159                 options = "type=%s,dev=%s" %(self.backfstype, 
2160                     blkdev)
2161         return options
2162         
2163     # need to check /proc/mounts and /etc/mtab before
2164     # formatting anything.
2165     # FIXME: check if device is already formatted.
2166     def prepare(self):
2167         if is_prepared(self.name):
2168             return
2169         if not self.active:
2170             debug(self.uuid, "not active")
2171             return
2172         run_acceptors()
2173         if self.osdtype == 'obdecho':
2174             blkdev = ''
2175         else:
2176             blkdev = block_dev(self.devpath, self.size, self.fstype,
2177                                config.reformat, self.format, self.journal_size,
2178                                self.inode_size, self.mkfsoptions, self.backfstype,
2179                                self.backdevpath)
2180
2181         if self.fstype == 'smfs':
2182             realdev = self.fstype
2183         else:
2184             realdev = blkdev
2185
2186         mountfsoptions = self.get_mount_options(blkdev)
2187         
2188         self.info(self.osdtype, realdev, mountfsoptions, self.fstype, 
2189                   self.size, self.format, self.journal_size, self.inode_size)
2190                   
2191         lctl.newdev(self.osdtype, self.name, self.uuid,
2192                     setup ="%s %s %s %s" %(realdev, self.fstype,
2193                                            self.failover_ost, 
2194                                            mountfsoptions))
2195         if not is_prepared('OSS'):
2196             lctl.newdev("ost", 'OSS', 'OSS_UUID', setup ="")
2197
2198     def osd_remaining(self):
2199         out = lctl.device_list()
2200         for s in out:
2201             if string.split(s)[2] in ('obdfilter', 'obdecho'):
2202                 return 1
2203
2204     def safe_to_clean(self):
2205         return self.active
2206
2207     def safe_to_clean_modules(self):
2208         return not self.osd_remaining()
2209
2210     def cleanup(self):
2211         if not self.active:
2212             debug(self.uuid, "not active")
2213             return
2214         if is_prepared(self.name):
2215             self.info()
2216             try:
2217                 lctl.cleanup(self.name, self.uuid, config.force,
2218                              config.failover)
2219             except CommandError, e:
2220                 log(self.module_name, "cleanup failed: ", self.name)
2221                 e.dump()
2222                 cleanup_error(e.rc)
2223         if not self.osd_remaining() and is_prepared('OSS'):
2224             try:
2225                 lctl.cleanup("OSS", "OSS_UUID", config.force,
2226                              config.failover)
2227             except CommandError, e:
2228                 print "cleanup failed: ", self.name
2229                 e.dump()
2230                 cleanup_error(e.rc)
2231         if not self.osdtype == 'obdecho':
2232             clean_dev(self.devpath, self.fstype, self.backfstype, 
2233                       self.backdevpath)
2234
2235     def correct_level(self, level, op=None):
2236         return level
2237
2238 def mgmt_uuid_for_fs(mtpt_name):
2239     if not mtpt_name:
2240         return ''
2241     mtpt_db = toplustreDB.lookup_name(mtpt_name)
2242     fs_uuid = mtpt_db.get_first_ref('filesystem')
2243     fs = toplustreDB.lookup(fs_uuid)
2244     if not fs:
2245         return ''
2246     return fs.get_first_ref('mgmt')
2247
2248 # Generic client module, used by OSC and MDC
2249 class Client(Module):
2250     def __init__(self, tgtdb, uuid, module, fs_name, self_name=None,
2251                  module_dir=None):
2252         self.target_name = tgtdb.getName()
2253         self.target_uuid = tgtdb.getUUID()
2254         self.module_dir = module_dir
2255         self.module = module
2256         self.db = tgtdb
2257         self.active = 1
2258
2259         self.tgt_dev_uuid = get_active_target(tgtdb)
2260         if not self.tgt_dev_uuid:
2261             panic("No target device found for target(1):", self.target_name)
2262
2263         self._server = None
2264         self._connected = 0
2265
2266         self.module = module
2267         self.module_name = string.upper(module)
2268         if not self_name:
2269             self.name = '%s_%s_%s_%s' % (self.module_name, socket.gethostname(),
2270                                          self.target_name, fs_name)
2271         else:
2272             self.name = self_name
2273         self.uuid = uuid
2274         self.lookup_server(self.tgt_dev_uuid)
2275         mgmt_uuid = mgmt_uuid_for_fs(fs_name)
2276         if mgmt_uuid:
2277             self.mgmt_name = mgmtcli_name_for_uuid(mgmt_uuid)
2278         else:
2279             self.mgmt_name = ''
2280         self.fs_name = fs_name
2281         if not self.module_dir:
2282             self.module_dir = module
2283
2284     def add_module(self, manager):
2285         manager.add_lustre_module(self.module_dir, self.module)
2286
2287     def lookup_server(self, srv_uuid):
2288         """ Lookup a server's network information """
2289         self._server_nets = get_ost_net(self.db, srv_uuid)
2290         if len(self._server_nets) == 0:
2291             panic ("Unable to find a server for:", srv_uuid)
2292             
2293     def get_name(self):
2294         return self.name
2295
2296     def get_servers(self):
2297         return self._server_nets
2298
2299     def prepare(self, ignore_connect_failure = 0):
2300         self.info(self.target_uuid)
2301         if not config.record and is_prepared(self.name):
2302             self.cleanup()
2303         try:
2304             srv = choose_local_server(self.get_servers())
2305             if srv:
2306                 lctl.connect(srv)
2307             else:
2308                 routes = find_route(self.get_servers())
2309                 if len(routes) == 0:
2310                     panic ("no route to",  self.target_uuid)
2311                 for (srv, r) in routes:
2312                     lctl.add_route_host(r[0], srv.nid_uuid, r[1], r[3])
2313         except CommandError, e:
2314             if not ignore_connect_failure:
2315                 raise e
2316         if srv:
2317             if self.permits_inactive() and (self.target_uuid in config.inactive or self.active == 0):
2318                 debug("%s inactive" % self.target_uuid)
2319                 inactive_p = "inactive"
2320             else:
2321                 debug("%s active" % self.target_uuid)
2322                 inactive_p = ""
2323             lctl.newdev(self.module, self.name, self.uuid,
2324                         setup ="%s %s %s %s" % (self.target_uuid, srv.nid_uuid,
2325                                                 inactive_p, self.mgmt_name))
2326
2327     def cleanup(self):
2328         if is_prepared(self.name):
2329             Module.cleanup(self)
2330             try:
2331                 srv = choose_local_server(self.get_servers())
2332                 if srv:
2333                     lctl.disconnect(srv)
2334                 else:
2335                     for (srv, r) in find_route(self.get_servers()):
2336                         lctl.del_route_host(r[0], srv.nid_uuid, r[1], r[3])
2337             except CommandError, e:
2338                 log(self.module_name, "cleanup failed: ", self.name)
2339                 e.dump()
2340                 cleanup_error(e.rc)
2341
2342     def correct_level(self, level, op=None):
2343         return level
2344
2345     def deactivate(self):
2346         try:
2347             lctl.deactivate(self.name)
2348         except CommandError, e:
2349             log(self.module_name, "deactivate failed: ", self.name)
2350             e.dump()
2351             cleanup_error(e.rc)
2352
2353 class MDC(Client):
2354     def __init__(self, db, uuid, fs_name):
2355          Client.__init__(self, db, uuid, 'mdc', fs_name)
2356
2357     def permits_inactive(self):
2358         return 0
2359
2360 class OSC(Client):
2361     def __init__(self, db, uuid, fs_name):
2362          Client.__init__(self, db, uuid, 'osc', fs_name)
2363
2364     def permits_inactive(self):
2365         return 1
2366
2367 def mgmtcli_name_for_uuid(uuid):
2368     return 'MGMTCLI_%s' % uuid
2369
2370 class ManagementClient(Client):
2371     def __init__(self, db, uuid):
2372         Client.__init__(self, db, uuid, 'mgmt_cli', '',
2373                         self_name = mgmtcli_name_for_uuid(db.getUUID()),
2374                         module_dir = 'mgmt')
2375
2376 class CMOBD(Module):
2377     def __init__(self, db):
2378         Module.__init__(self, 'CMOBD', db)
2379         self.name = self.db.getName(); 
2380         self.uuid = generate_client_uuid(self.name)
2381         self.master_uuid = self.db.get_first_ref('masterobd')
2382         self.cache_uuid = self.db.get_first_ref('cacheobd')
2383
2384         master_obd = self.db.lookup(self.master_uuid)
2385         if not master_obd:
2386             panic('master obd not found:', self.master_uuid)
2387
2388         cache_obd = self.db.lookup(self.cache_uuid)
2389         if not cache_obd:
2390             panic('cache obd not found:', self.cache_uuid)
2391             
2392         self.master = None
2393         self.cache = None
2394             
2395         master_class = master_obd.get_class()
2396         cache_class = cache_obd.get_class()
2397
2398         if master_class == 'ost' or master_class == 'lov':
2399             client_uuid = "%s_lov_master_UUID" % (self.name)
2400             self.master = LOV(master_obd, client_uuid, self.name);
2401         elif master_class == 'mds':
2402             self.master = get_mdc(db, self.name, self.master_uuid) 
2403         elif master_class == 'lmv':
2404             client_uuid = "%s_lmv_master_UUID" % (self.name)
2405             self.master = LMV(master_obd, client_uuid, self.name);
2406         else:
2407             panic("unknown master obd class '%s'" %(master_class))
2408             
2409         if cache_class == 'ost' or cache_class == 'lov':
2410             client_uuid = "%s_lov_cache_UUID" % (self.name)
2411             self.cache = LOV(cache_obd, client_uuid, self.name);
2412         elif cache_class == 'mds':
2413             self.cache = get_mdc(db, self.name, self.cache_uuid)
2414         elif cache_class == 'lmv':
2415             client_uuid = "%s_lmv_cache_UUID" % (self.name)
2416             self.cache = LMV(cache_obd, client_uuid, self.name);
2417         else:
2418             panic("unknown cache obd class '%s'" %(cache_class))
2419
2420     def prepare(self):
2421         self.master.prepare()
2422         if not config.record and is_prepared(self.name):
2423             return
2424         self.info(self.master_uuid, self.cache_uuid)
2425         lctl.newdev("cmobd", self.name, self.uuid,
2426                     setup ="%s %s" %(self.master.uuid,
2427                                      self.cache.uuid))
2428
2429     def get_uuid(self):
2430         return self.uuid
2431         
2432     def get_name(self):
2433         return self.name
2434         
2435     def get_master_name(self):
2436         return self.master.name
2437             
2438     def get_cache_name(self):
2439         return self.cache.name
2440
2441     def cleanup(self):
2442         if is_prepared(self.name):
2443             Module.cleanup(self)
2444         if self.master:
2445             self.master.cleanup()
2446
2447     def add_module(self, manager):
2448         manager.add_lustre_module('cmobd', 'cmobd')
2449         self.master.add_module(manager)
2450
2451     def correct_level(self, level, op=None):
2452         return level
2453
2454 class COBD(Module):
2455     def __init__(self, db, uuid, name):
2456         Module.__init__(self, 'COBD', db)
2457         self.name = self.db.getName(); 
2458         self.uuid = generate_client_uuid(self.name)
2459         self.master_uuid = self.db.get_first_ref('masterobd')
2460         self.cache_uuid = self.db.get_first_ref('cacheobd')
2461
2462         master_obd = self.db.lookup(self.master_uuid)
2463         if not master_obd:
2464             panic('master obd not found:', self.master_uuid)
2465
2466         cache_obd = self.db.lookup(self.cache_uuid)
2467         if not cache_obd:
2468             panic('cache obd not found:', self.cache_uuid)
2469             
2470         self.master = None
2471         self.cache = None
2472
2473         master_class = master_obd.get_class()
2474         cache_class = cache_obd.get_class()
2475
2476         if master_class == 'ost' or master_class == 'lov':
2477             client_uuid = "%s_lov_master_UUID" % (self.name)
2478             self.master = LOV(master_obd, client_uuid, name);
2479         elif master_class == 'mds':
2480             self.master = get_mdc(db, name, self.master_uuid) 
2481         elif master_class == 'lmv':
2482             client_uuid = "%s_lmv_master_UUID" % (self.name)
2483             self.master = LMV(master_obd, client_uuid, self.name);
2484         else:
2485             panic("unknown master obd class '%s'" %(master_class))
2486             
2487         if cache_class == 'ost' or cache_class == 'lov':
2488             client_uuid = "%s_lov_cache_UUID" % (self.name)
2489             self.cache = LOV(cache_obd, client_uuid, name);
2490         elif cache_class == 'mds':
2491             self.cache = get_mdc(db, name, self.cache_uuid)
2492         elif cache_class == 'lmv':
2493             client_uuid = "%s_lmv_cache_UUID" % (self.name)
2494             self.cache = LMV(cache_obd, client_uuid, self.name);
2495         else:
2496             panic("unknown cache obd class '%s'" %(cache_class))
2497             
2498     def get_uuid(self):
2499         return self.uuid
2500
2501     def get_name(self):
2502         return self.name
2503
2504     def get_master_name(self):
2505         return self.master.name
2506
2507     def get_cache_name(self):
2508         return self.cache.name
2509
2510     def prepare(self):
2511         self.master.prepare()
2512         self.cache.prepare()
2513         if not config.record and is_prepared(self.name):
2514             return
2515         self.info(self.master_uuid, self.cache_uuid)
2516         lctl.newdev("cobd", self.name, self.uuid,
2517                     setup ="%s %s" %(self.master.name,
2518                                      self.cache.name))
2519
2520     def cleanup(self):
2521         if is_prepared(self.name):
2522             Module.cleanup(self)
2523         self.master.cleanup()
2524         self.cache.cleanup()
2525
2526     def add_module(self, manager):
2527         manager.add_lustre_module('cobd', 'cobd')
2528         self.master.add_module(manager)
2529
2530 # virtual interface for  OSC and LOV
2531 class VOSC(Module):
2532     def __init__(self, db, client_uuid, name, name_override = None):
2533         Module.__init__(self, 'VOSC', db)
2534         if db.get_class() == 'lov':
2535             self.osc = LOV(db, client_uuid, name, name_override)
2536             self.type = 'lov'
2537         elif db.get_class() == 'cobd':
2538             self.osc = COBD(db, client_uuid, name)
2539             self.type = 'cobd'
2540         else:
2541             self.osc = OSC(db, client_uuid, name)
2542             self.type = 'osc'
2543             
2544     def get_uuid(self):
2545         return self.osc.get_uuid()
2546
2547     def get_name(self):
2548         return self.osc.get_name()
2549
2550     def prepare(self):
2551         self.osc.prepare()
2552         
2553     def cleanup(self):
2554         self.osc.cleanup()
2555         
2556     def add_module(self, manager):
2557         self.osc.add_module(manager)
2558         
2559     def correct_level(self, level, op=None):
2560         return self.osc.correct_level(level, op)
2561
2562 # virtual interface for MDC and LMV
2563 class VMDC(Module):
2564     def __init__(self, db, client_uuid, name, name_override = None):
2565         Module.__init__(self, 'VMDC', db)
2566         if db.get_class() == 'lmv':
2567             self.mdc = LMV(db, client_uuid, name, name_override)
2568         elif db.get_class() == 'cobd':
2569             self.mdc = COBD(db, client_uuid, name)
2570         else:
2571             self.mdc = MDC(db, client_uuid, name)
2572             
2573     def get_uuid(self):
2574         return self.mdc.uuid
2575
2576     def get_name(self):
2577         return self.mdc.name
2578
2579     def prepare(self):
2580         self.mdc.prepare()
2581         
2582     def cleanup(self):
2583         self.mdc.cleanup()
2584         
2585     def add_module(self, manager):
2586         self.mdc.add_module(manager)
2587         
2588     def correct_level(self, level, op=None):
2589         return self.mdc.correct_level(level, op)
2590
2591 class ECHO_CLIENT(Module):
2592     def __init__(self,db):
2593         Module.__init__(self, 'ECHO_CLIENT', db)
2594         self.obd_uuid = self.db.get_first_ref('obd')
2595         obd = self.db.lookup(self.obd_uuid)
2596         self.uuid = generate_client_uuid(self.name)
2597         self.osc = VOSC(obd, self.uuid, self.name)
2598
2599     def prepare(self):
2600         if not config.record and is_prepared(self.name):
2601             return
2602         run_acceptors()
2603         self.osc.prepare() # XXX This is so cheating. -p
2604         self.info(self.obd_uuid)
2605
2606         lctl.newdev("echo_client", self.name, self.uuid,
2607                     setup = self.osc.get_name())
2608
2609     def cleanup(self):
2610         if is_prepared(self.name):
2611             Module.cleanup(self)
2612         self.osc.cleanup()
2613
2614     def add_module(self, manager):
2615         self.osc.add_module(manager)
2616         manager.add_lustre_module('obdecho', 'obdecho')
2617
2618     def correct_level(self, level, op=None):
2619         return level
2620
2621 def generate_client_uuid(name):
2622         client_uuid = '%05x_%.19s_%05x%05x' % (int(random.random() * 1048576),
2623                                                name,
2624                                                int(random.random() * 1048576),
2625                                                int(random.random() * 1048576))
2626         return client_uuid[:36]
2627
2628 class Mountpoint(Module):
2629     def __init__(self,db):
2630         Module.__init__(self, 'MTPT', db)
2631         self.path = self.db.get_val('path')
2632         self.clientoptions = self.db.get_val('clientoptions', '')
2633         self.fs_uuid = self.db.get_first_ref('filesystem')
2634         fs = self.db.lookup(self.fs_uuid)
2635         self.mds_uuid = fs.get_first_ref('lmv')
2636         if not self.mds_uuid:
2637             self.mds_uuid = fs.get_first_ref('mds')
2638         self.obd_uuid = fs.get_first_ref('obd')
2639         self.mgmt_uuid = fs.get_first_ref('mgmt')
2640         client_uuid = generate_client_uuid(self.name)
2641
2642         ost = self.db.lookup(self.obd_uuid)
2643         if not ost:
2644             panic("no ost: ", self.obd_uuid)
2645             
2646         mds = self.db.lookup(self.mds_uuid)
2647         if not mds:
2648             panic("no mds: ", self.mds_uuid)
2649        
2650         self.vosc = VOSC(ost, client_uuid, self.name, self.name)
2651         self.vmdc = VMDC(mds, client_uuid, self.name, self.name)
2652         
2653         if self.mgmt_uuid:
2654             self.mgmtcli = ManagementClient(db.lookup(self.mgmt_uuid),
2655                                             client_uuid)
2656         else:
2657             self.mgmtcli = None
2658
2659     def prepare(self):
2660         if not config.record and fs_is_mounted(self.path):
2661             log(self.path, "already mounted.")
2662             return
2663         run_acceptors()
2664         if self.mgmtcli:
2665             self.mgmtcli.prepare()
2666         self.vosc.prepare()
2667         self.vmdc.prepare()
2668         vmdc_name = self.vmdc.get_name()
2669
2670         self.info(self.path, self.mds_uuid, self.obd_uuid)
2671         if config.record or config.lctl_dump:
2672             lctl.mount_option(local_node_name, self.vosc.get_name(), vmdc_name)
2673             return
2674
2675         if config.clientoptions:
2676             if self.clientoptions:
2677                 self.clientoptions = self.clientoptions + ',' + \
2678                                      config.clientoptions
2679             else:
2680                 self.clientoptions = config.clientoptions
2681         if self.clientoptions:
2682             self.clientoptions = ',' + self.clientoptions
2683             # Linux kernel will deal with async and not pass it to ll_fill_super,
2684             # so replace it with Lustre async
2685             self.clientoptions = string.replace(self.clientoptions, "async", 
2686                                                 "lasync")
2687
2688         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s%s %s %s" % \
2689               (self.vosc.get_name(), vmdc_name, self.clientoptions, 
2690                config.config, self.path)
2691         run("mkdir", self.path)
2692         ret, val = run(cmd)
2693         if ret:
2694             self.vmdc.cleanup()            
2695             self.vosc.cleanup()
2696             panic("mount failed:", self.path, ":", string.join(val))
2697
2698     def cleanup(self):
2699         self.info(self.path, self.mds_uuid,self.obd_uuid)
2700
2701         if config.record or config.lctl_dump:
2702             lctl.del_mount_option(local_node_name)
2703         else:
2704             if fs_is_mounted(self.path):
2705                 if config.force:
2706                     (rc, out) = run("umount", "-f", self.path)
2707                 else:
2708                     (rc, out) = run("umount", self.path)
2709                 if rc:
2710                     raise CommandError('umount', out, rc)
2711
2712             if fs_is_mounted(self.path):
2713                 panic("fs is still mounted:", self.path)
2714
2715         self.vmdc.cleanup()
2716         self.vosc.cleanup()
2717         if self.mgmtcli:
2718             self.mgmtcli.cleanup()
2719
2720     def add_module(self, manager):
2721         manager.add_lustre_module('mdc', 'mdc')
2722         
2723         if self.mgmtcli:
2724             self.mgmtcli.add_module(manager)
2725         
2726         self.vosc.add_module(manager)
2727         self.vmdc.add_module(manager)
2728
2729         manager.add_lustre_module('llite', 'llite')
2730
2731     def correct_level(self, level, op=None):
2732         return level
2733
2734 # ============================================================
2735 # misc query functions
2736
2737 def get_ost_net(self, osd_uuid):
2738     srv_list = []
2739     if not osd_uuid:
2740         return srv_list
2741     osd = self.lookup(osd_uuid)
2742     node_uuid = osd.get_first_ref('node')
2743     node = self.lookup(node_uuid)
2744     if not node:
2745         panic("unable to find node for osd_uuid:", osd_uuid,
2746               " node_ref:", node_uuid_)
2747     for net_uuid in node.get_networks():
2748         db = node.lookup(net_uuid)
2749         srv_list.append(Network(db))
2750     return srv_list
2751
2752
2753 # the order of iniitailization is based on level. 
2754 def getServiceLevel(self):
2755     type = self.get_class()
2756     ret=0;
2757     if type in ('network',):
2758         ret = 5
2759     elif type in ('routetbl',):
2760         ret = 6
2761     elif type in ('ldlm',):
2762         ret = 20
2763     elif type in ('osd', 'cobd'):
2764         ret = 30
2765     elif type in ('mdsdev',):
2766         ret = 40
2767     elif type in ('lmv',):
2768         ret = 45
2769     elif type in ('cmobd',):
2770         ret = 50
2771     elif type in ('mountpoint', 'echoclient'):
2772         ret = 70
2773     else:
2774         panic("Unknown type: ", type)
2775
2776     if ret < config.minlevel or ret > config.maxlevel:
2777         ret = 0
2778     return ret
2779
2780 #
2781 # return list of services in a profile. list is a list of tuples
2782 # [(level, db_object),]
2783 def getServices(self):
2784     list = []
2785     for ref_class, ref_uuid in self.get_all_refs():
2786             servdb = self.lookup(ref_uuid)
2787             if  servdb:
2788                 level = getServiceLevel(servdb)
2789                 if level > 0:
2790                     list.append((level, servdb))
2791             else:
2792                 panic('service not found: ' + ref_uuid)
2793
2794     list.sort()
2795     return list
2796
2797
2798 ############################################################
2799 # MDC UUID hack -
2800 # FIXME: clean this mess up!
2801 #
2802 # OSC is no longer in the xml, so we have to fake it.
2803 # this is getting ugly and begging for another refactoring
2804 def get_osc(ost_db, uuid, fs_name):
2805     osc = OSC(ost_db, uuid, fs_name)
2806     return osc
2807
2808 def get_mdc(db, fs_name, mds_uuid):
2809     mds_db = db.lookup(mds_uuid);
2810     if not mds_db:
2811         error("no mds:", mds_uuid)
2812     mdc = MDC(mds_db, mds_uuid, fs_name)
2813     return mdc
2814
2815 ############################################################
2816 # routing ("rooting")
2817
2818 # list of (nettype, cluster_id, nid)
2819 local_clusters = []
2820
2821 def find_local_clusters(node_db):
2822     global local_clusters
2823     for netuuid in node_db.get_networks():
2824         net = node_db.lookup(netuuid)
2825         srv = Network(net)
2826         debug("add_local", netuuid)
2827         local_clusters.append((srv.net_type, srv.cluster_id, srv.nid))
2828         if srv.port > 0:
2829             if acceptors.has_key(srv.port):
2830                 panic("duplicate port:", srv.port)
2831             acceptors[srv.port] = AcceptorHandler(srv.port, srv.net_type)
2832
2833 # This node is a gateway.
2834 is_router = 0
2835 def node_is_router():
2836     return is_router
2837
2838 # If there are any routers found in the config, then this will be true
2839 # and all nodes will load kptlrouter.
2840 needs_router = 0
2841 def node_needs_router():
2842     return needs_router or is_router
2843
2844 # list of (nettype, gw, tgt_cluster_id, lo, hi)
2845 # Currently, these local routes are only added to kptlrouter route
2846 # table if they are needed to connect to a specific server.  This
2847 # should be changed so all available routes are loaded, and the
2848 # ptlrouter can make all the decisions.
2849 local_routes = []
2850
2851 def find_local_routes(lustre):
2852     """ Scan the lustre config looking for routers .  Build list of
2853     routes. """
2854     global local_routes, needs_router
2855     local_routes = []
2856     list = lustre.lookup_class('node')
2857     for router in list:
2858         if router.get_val_int('router', 0):
2859             needs_router = 1
2860             for (local_type, local_cluster_id, local_nid) in local_clusters:
2861                 gw = None
2862                 for netuuid in router.get_networks():
2863                     db = router.lookup(netuuid)
2864                     if (local_type == db.get_val('nettype') and
2865                        local_cluster_id == db.get_val('clusterid')):
2866                         gw = db.get_val('nid')
2867                         break
2868                 if gw:
2869                     debug("find_local_routes: gw is", gw)
2870                     for route in router.get_local_routes(local_type, gw):
2871                         local_routes.append(route)
2872     debug("find_local_routes:", local_routes)
2873
2874
2875 def choose_local_server(srv_list):
2876     for srv in srv_list:
2877         if local_cluster(srv.net_type, srv.cluster_id):
2878             return srv
2879
2880 def local_cluster(net_type, cluster_id):
2881     for cluster in local_clusters:
2882         if net_type == cluster[0] and cluster_id == cluster[1]:
2883             return 1
2884     return 0
2885
2886 def local_interface(net_type, cluster_id, nid):
2887     for cluster in local_clusters:
2888         if (net_type == cluster[0] and cluster_id == cluster[1]
2889             and nid == cluster[2]):
2890             return 1
2891     return 0
2892
2893 def find_route(srv_list):
2894     result = []
2895     frm_type = local_clusters[0][0]
2896     for srv in srv_list:
2897         debug("find_route: srv:", srv.nid, "type: ", srv.net_type)
2898         to_type = srv.net_type
2899         to = srv.nid
2900         cluster_id = srv.cluster_id
2901         debug ('looking for route to', to_type, to)
2902         for r in local_routes:
2903             debug("find_route: ", r)
2904             if  (r[3] <= to and to <= r[4]) and cluster_id == r[2]:
2905                 result.append((srv, r))
2906     return result
2907            
2908 def get_active_target(db):
2909     target_uuid = db.getUUID()
2910     target_name = db.getName()
2911     node_name = get_select(target_name)
2912     if node_name:
2913         tgt_dev_uuid = db.get_node_tgt_dev(node_name, target_uuid)
2914     else:
2915         tgt_dev_uuid = db.get_first_ref('active')
2916     return tgt_dev_uuid
2917
2918 def get_server_by_nid_uuid(db,  nid_uuid):
2919     for n in db.lookup_class("network"):
2920         net = Network(n)
2921         if net.nid_uuid == nid_uuid:
2922             return net
2923         
2924
2925 ############################################################
2926 # lconf level logic
2927 # Start a service.
2928 def newService(db):
2929     type = db.get_class()
2930     debug('Service:', type, db.getName(), db.getUUID())
2931     n = None
2932     if type == 'ldlm':
2933         n = LDLM(db)
2934     elif type == 'lov':
2935         n = LOV(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2936     elif type == 'network':
2937         n = Network(db)
2938     elif type == 'routetbl':
2939         n = RouteTable(db)
2940     elif type == 'osd':
2941         n = OSD(db)
2942     elif type == 'cobd':
2943         n = COBD(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2944     elif type == 'cmobd':
2945         n = CMOBD(db)
2946     elif type == 'mdsdev':
2947         n = MDSDEV(db)
2948     elif type == 'mountpoint':
2949         n = Mountpoint(db)
2950     elif type == 'echoclient':
2951         n = ECHO_CLIENT(db)
2952     elif type == 'lmv':
2953         n = LMV(db)
2954     else:
2955         panic ("unknown service type:", type)
2956     return n
2957
2958 #
2959 # Prepare the system to run lustre using a particular profile
2960 # in a the configuration. 
2961 #  * load & the modules
2962 #  * setup networking for the current node
2963 #  * make sure partitions are in place and prepared
2964 #  * initialize devices with lctl
2965 # Levels is important, and needs to be enforced.
2966 def for_each_profile(db, prof_list, operation):
2967     for prof_uuid in prof_list:
2968         prof_db = db.lookup(prof_uuid)
2969         if not prof_db:
2970             panic("profile:", prof_uuid, "not found.")
2971         services = getServices(prof_db)
2972         operation(services)
2973
2974 def magic_get_osc(db, rec, lov):
2975     if lov:
2976         lov_uuid = lov.get_uuid()
2977         lov_name = lov.osc.fs_name
2978     else:
2979         lov_uuid = rec.getAttribute('lov_uuidref')
2980         # FIXME: better way to find the mountpoint?
2981         filesystems = db.root_node.getElementsByTagName('filesystem')
2982         fsuuid = None
2983         for fs in filesystems:
2984             ref = fs.getElementsByTagName('obd_ref')
2985             if ref[0].getAttribute('uuidref') == lov_uuid:
2986                 fsuuid = fs.getAttribute('uuid')
2987                 break
2988
2989         if not fsuuid:
2990             panic("malformed xml: lov uuid '" + lov_uuid + "' referenced in 'add' record is not used by any filesystems.")
2991
2992         mtpts = db.root_node.getElementsByTagName('mountpoint')
2993         lov_name = None
2994         for fs in mtpts:
2995             ref = fs.getElementsByTagName('filesystem_ref')
2996             if ref[0].getAttribute('uuidref') == fsuuid:
2997                 lov_name = fs.getAttribute('name')
2998                 break
2999
3000         if not lov_name:
3001             panic("malformed xml: 'add' record references lov uuid '" + lov_uuid + "', which references filesystem uuid '" + fsuuid + "', which does not reference a mountpoint.")
3002
3003     print "lov_uuid: " + lov_uuid + "; lov_name: " + lov_name
3004
3005     ost_uuid = rec.getAttribute('ost_uuidref')
3006     obd = db.lookup(ost_uuid)
3007
3008     if not obd:
3009         panic("malformed xml: 'add' record references ost uuid '" + ost_uuid + "' which cannot be found.")
3010
3011     osc = get_osc(obd, lov_uuid, lov_name)
3012     if not osc:
3013         panic('osc not found:', obd_uuid)
3014     return osc
3015
3016 # write logs for update records.  sadly, logs of all types -- and updates in
3017 # particular -- are something of an afterthought.  lconf needs rewritten with
3018 # these as core concepts.  so this is a pretty big hack.
3019 def process_update_record(db, update, lov):
3020     for rec in update.childNodes:
3021         if rec.nodeType != rec.ELEMENT_NODE:
3022             continue
3023
3024         log("found "+rec.nodeName+" record in update version " +
3025             str(update.getAttribute('version')))
3026
3027         lov_uuid = rec.getAttribute('lov_uuidref')
3028         ost_uuid = rec.getAttribute('ost_uuidref')
3029         index = rec.getAttribute('index')
3030         gen = rec.getAttribute('generation')
3031
3032         if not lov_uuid or not ost_uuid or not index or not gen:
3033             panic("malformed xml: 'update' record requires lov_uuid, ost_uuid, index, and generation.")
3034
3035         if not lov:
3036             tmplov = db.lookup(lov_uuid)
3037             if not tmplov:
3038                 panic("malformed xml: 'delete' record contains lov UUID '" + lov_uuid + "', which cannot be located.")
3039             lov_name = tmplov.getName()
3040         else:
3041             lov_name = lov.osc.name
3042
3043         # ------------------------------------------------------------- add
3044         if rec.nodeName == 'add':
3045             if config.cleanup:
3046                 lctl.lov_del_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3047                 continue
3048
3049             osc = magic_get_osc(db, rec, lov)
3050
3051             try:
3052                 # Only ignore connect failures with --force, which
3053                 # isn't implemented here yet.
3054                 osc.prepare(ignore_connect_failure=0)
3055             except CommandError, e:
3056                 print "Error preparing OSC %s\n" % osc.uuid
3057                 raise e
3058
3059             lctl.lov_add_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3060
3061         # ------------------------------------------------------ deactivate
3062         elif rec.nodeName == 'deactivate':
3063             if config.cleanup:
3064                 continue
3065
3066             osc = magic_get_osc(db, rec, lov)
3067
3068             try:
3069                 osc.deactivate()
3070             except CommandError, e:
3071                 print "Error deactivating OSC %s\n" % osc.uuid
3072                 raise e
3073
3074         # ---------------------------------------------------------- delete
3075         elif rec.nodeName == 'delete':
3076             if config.cleanup:
3077                 continue
3078
3079             osc = magic_get_osc(db, rec, lov)
3080
3081             try:
3082                 config.cleanup = 1
3083                 osc.cleanup()
3084                 config.cleanup = 0
3085             except CommandError, e:
3086                 print "Error cleaning up OSC %s\n" % osc.uuid
3087                 raise e
3088
3089             lctl.lov_del_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3090
3091 def process_updates(db, log_device, log_name, lov = None):
3092     updates = db.root_node.getElementsByTagName('update')
3093     for u in updates:
3094         if not u.childNodes:
3095             log("ignoring empty update record (version " +
3096                 str(u.getAttribute('version')) + ")")
3097             continue
3098
3099         version = u.getAttribute('version')
3100         real_name = "%s-%s" % (log_name, version)
3101         lctl.clear_log(log_device, real_name)
3102         lctl.record(log_device, real_name)
3103
3104         process_update_record(db, u, lov)
3105
3106         lctl.end_record()
3107
3108 def doWriteconf(services):
3109     #if config.nosetup:
3110     #    return
3111     for s in services:
3112         if s[1].get_class() == 'mdsdev':
3113             n = newService(s[1])
3114             n.write_conf()
3115
3116 def doSetup(services):
3117     if config.nosetup:
3118         return
3119     slist = []
3120     for s in services:
3121         n = newService(s[1])
3122         n.level = s[0]
3123         slist.append((n.level, n))
3124     nlist = []
3125     for n in slist:
3126         nl = n[1].correct_level(n[0])
3127         nlist.append((nl, n[1]))
3128     nlist.sort()
3129     for n in nlist:
3130         n[1].prepare()
3131
3132 def doLoadModules(services):
3133     if config.nomod:
3134         return
3135     
3136     # adding all needed modules from all services
3137     for s in services:
3138         n = newService(s[1])
3139         n.add_module(mod_manager)
3140     
3141     # loading all registered modules
3142     mod_manager.load_modules()
3143
3144 def doUnloadModules(services):
3145     if config.nomod:
3146         return
3147         
3148     # adding all needed modules from all services
3149     for s in services:
3150         n = newService(s[1])
3151         if n.safe_to_clean_modules():
3152             n.add_module(mod_manager)
3153     
3154     # unloading all registered modules
3155     mod_manager.cleanup_modules()
3156
3157 def doCleanup(services):
3158     if config.nosetup:
3159         return
3160     slist = []
3161
3162     for s in services:
3163         n = newService(s[1])
3164         n.level = s[0]
3165         slist.append((n.level, n))
3166     nlist = []
3167     for n in slist:
3168         nl = n[1].correct_level(n[0])
3169         nlist.append((nl, n[1]))
3170     nlist.sort()
3171     nlist.reverse()
3172
3173     for n in nlist:
3174         if n[1].safe_to_clean():
3175             n[1].cleanup()
3176
3177 #
3178 # Load profile for 
3179 def doHost(lustreDB, hosts):
3180     global is_router, local_node_name
3181     node_db = None
3182     for h in hosts:
3183         node_db = lustreDB.lookup_name(h, 'node')
3184         if node_db:
3185             break
3186     if not node_db:
3187         panic('No host entry found.')
3188
3189     local_node_name = node_db.get_val('name', 0)
3190     is_router = node_db.get_val_int('router', 0)
3191     lustre_upcall = node_db.get_val('lustreUpcall', '')
3192     portals_upcall = node_db.get_val('portalsUpcall', '')
3193     timeout = node_db.get_val_int('timeout', 0)
3194     ptldebug = node_db.get_val('ptldebug', '')
3195     subsystem = node_db.get_val('subsystem', '')
3196     
3197     find_local_clusters(node_db)
3198     if not is_router:
3199         find_local_routes(lustreDB)
3200
3201     # Two step process: (1) load modules, (2) setup lustre
3202     # if not cleaning, load modules first.
3203     prof_list = node_db.get_refs('profile')
3204
3205     if config.write_conf:
3206         for_each_profile(node_db, prof_list, doLoadModules)
3207         sys_make_devices()
3208         for_each_profile(node_db, prof_list, doWriteconf)
3209         for_each_profile(node_db, prof_list, doUnloadModules)
3210         lustreDB.close()
3211
3212     elif config.recover:
3213         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
3214             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
3215                                      "--client_uuid <UUID> --conn_uuid <UUID>")
3216         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
3217                    config.conn_uuid)
3218     elif config.cleanup:
3219         if config.force:
3220             # the command line can override this value
3221             timeout = 5
3222         # ugly hack, only need to run lctl commands for --dump
3223         if config.lctl_dump or config.record:
3224             for_each_profile(node_db, prof_list, doCleanup)
3225             return
3226
3227         sys_set_timeout(timeout)
3228         sys_set_ptldebug(ptldebug)
3229         sys_set_subsystem(subsystem)
3230         sys_set_lustre_upcall(lustre_upcall)
3231         sys_set_portals_upcall(portals_upcall)
3232
3233         for_each_profile(node_db, prof_list, doCleanup)
3234         for_each_profile(node_db, prof_list, doUnloadModules)
3235         lustreDB.close()
3236
3237     else:
3238         # ugly hack, only need to run lctl commands for --dump
3239         if config.lctl_dump or config.record:
3240             sys_set_timeout(timeout)
3241             sys_set_lustre_upcall(lustre_upcall)
3242             for_each_profile(node_db, prof_list, doSetup)
3243             return
3244
3245         sys_make_devices()
3246         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
3247         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
3248
3249         for_each_profile(node_db, prof_list, doLoadModules)
3250
3251         sys_set_debug_path()
3252         sys_set_ptldebug(ptldebug)
3253         sys_set_subsystem(subsystem)
3254         script = config.gdb_script
3255         run(lctl.lctl, ' modules >', script)
3256         if config.gdb:
3257             log ("The GDB module script is in", script)
3258             # pause, so user has time to break and
3259             # load the script
3260             time.sleep(5)
3261         sys_set_timeout(timeout)
3262         sys_set_lustre_upcall(lustre_upcall)
3263         sys_set_portals_upcall(portals_upcall)
3264
3265         for_each_profile(node_db, prof_list, doSetup)
3266         lustreDB.close()
3267
3268 def doRecovery(lustreDB, lctl, tgt_uuid, client_uuid, nid_uuid):
3269     tgt = lustreDB.lookup(tgt_uuid)
3270     if not tgt:
3271         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
3272     new_uuid = get_active_target(tgt)
3273     if not new_uuid:
3274         raise Lustre.LconfError("doRecovery: no active target found for: " +
3275                                 tgt_uuid)
3276     net = choose_local_server(get_ost_net(lustreDB, new_uuid))
3277     if not net:
3278         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
3279
3280     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
3281     try:
3282         oldnet = get_server_by_nid_uuid(lustreDB, nid_uuid)
3283         lustreDB.close()
3284         if oldnet:
3285             lctl.disconnect(oldnet)
3286     except CommandError, e:
3287         log("recover: disconnect", nid_uuid, "failed: ")
3288         e.dump()
3289
3290     try:
3291         lctl.connect(net)
3292     except CommandError, e:
3293         log("recover: connect failed")
3294         e.dump()
3295
3296     lctl.recover(client_uuid, net.nid_uuid)
3297
3298
3299 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
3300     base = os.path.dirname(cmd)
3301     if development_mode():
3302         if not config.lustre:
3303             debug('using objdir module paths')            
3304             config.lustre = (os.path.join(base, ".."))
3305         # normalize the portals dir, using command line arg if set
3306         if config.portals:
3307             portals_dir = config.portals
3308         dir = os.path.join(config.lustre, portals_dir)
3309         config.portals = dir
3310         debug('config.portals', config.portals)
3311     elif config.lustre and config.portals:
3312         # production mode
3313         # if --lustre and --portals, normalize portals 
3314         # can ignore POTRALS_DIR here, since it is probly useless here
3315         config.portals = os.path.join(config.lustre, config.portals)
3316         debug('config.portals B', config.portals)
3317
3318 def sysctl(path, val):
3319     debug("+ sysctl", path, val)
3320     if config.noexec:
3321         return
3322     try:
3323         fp = open(os.path.join('/proc/sys', path), 'w')
3324         fp.write(str(val))
3325         fp.close()
3326     except IOError, e:
3327         panic(str(e))
3328
3329
3330 def sys_set_debug_path():
3331     sysctl('portals/debug_path', config.debug_path)
3332
3333 def sys_set_lustre_upcall(upcall):
3334     # the command overrides the value in the node config
3335     if config.lustre_upcall:
3336         upcall = config.lustre_upcall
3337     elif config.upcall:
3338         upcall = config.upcall
3339     if upcall:
3340         lctl.set_lustre_upcall(upcall)
3341
3342 def sys_set_portals_upcall(upcall):
3343     # the command overrides the value in the node config
3344     if config.portals_upcall:
3345         upcall = config.portals_upcall
3346     elif config.upcall:
3347         upcall = config.upcall
3348     if upcall:
3349         sysctl('portals/upcall', upcall)
3350
3351 def sys_set_timeout(timeout):
3352     # the command overrides the value in the node config
3353     if config.timeout and config.timeout > 0:
3354         timeout = config.timeout
3355     if timeout != None and timeout > 0:
3356         lctl.set_timeout(timeout)
3357
3358 def sys_tweak_socknal ():
3359     # reserve at least 8MB, or we run out of RAM in skb_alloc under read
3360     if sys_get_branch() == '2.6':
3361         fp = open('/proc/meminfo')
3362         lines = fp.readlines()
3363         fp.close()
3364         memtotal = 131072
3365         for l in lines:
3366             a = string.split(l)
3367             if a[0] == 'MemTotal:':
3368                 memtotal = a[1]
3369                 debug("memtotal" + memtotal)
3370         if int(memtotal) < 262144:
3371             minfree = int(memtotal) / 16
3372         else:
3373             minfree = 32768
3374         debug("+ minfree ", minfree)
3375         sysctl("vm/min_free_kbytes", minfree)
3376     if config.single_socket:
3377         sysctl("socknal/typed", 0)
3378
3379 def sys_optimize_elan ():
3380     procfiles = ["/proc/elan/config/eventint_punt_loops",
3381                  "/proc/qsnet/elan3/config/eventint_punt_loops",
3382                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
3383     for p in procfiles:
3384         if os.access(p, os.W_OK):
3385             run ("echo 1 > " + p)
3386
3387 def sys_set_ptldebug(ptldebug):
3388     if config.ptldebug:
3389         ptldebug = config.ptldebug
3390     if ptldebug:
3391         try:
3392             val = eval(ptldebug, ptldebug_names)
3393             val = "0x%x" % (val)
3394             sysctl('portals/debug', val)
3395         except NameError, e:
3396             panic(str(e))
3397
3398 def sys_set_subsystem(subsystem):
3399     if config.subsystem:
3400         subsystem = config.subsystem
3401     if subsystem:
3402         try:
3403             val = eval(subsystem, subsystem_names)
3404             val = "0x%x" % (val)
3405             sysctl('portals/subsystem_debug', val)
3406         except NameError, e:
3407             panic(str(e))
3408
3409 def sys_set_netmem_max(path, max):
3410     debug("setting", path, "to at least", max)
3411     if config.noexec:
3412         return
3413     fp = open(path)
3414     str = fp.readline()
3415     fp.close()
3416     cur = int(str)
3417     if max > cur:
3418         fp = open(path, 'w')
3419         fp.write('%d\n' %(max))
3420         fp.close()
3421     
3422     
3423 def sys_make_devices():
3424     if not os.access('/dev/portals', os.R_OK):
3425         run('mknod /dev/portals c 10 240')
3426     if not os.access('/dev/obd', os.R_OK):
3427         run('mknod /dev/obd c 10 241')
3428
3429
3430 # Add dir to the global PATH, if not already there.
3431 def add_to_path(new_dir):
3432     syspath = string.split(os.environ['PATH'], ':')
3433     if new_dir in syspath:
3434         return
3435     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
3436     
3437 def default_debug_path():
3438     path = '/tmp/lustre-log'
3439     if os.path.isdir('/r'):
3440         return '/r' + path
3441     else:
3442         return path
3443
3444 def default_gdb_script():
3445     script = '/tmp/ogdb'
3446     if os.path.isdir('/r'):
3447         return '/r' + script
3448     else:
3449         return script
3450
3451
3452 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
3453 # ensure basic elements are in the system path
3454 def sanitise_path():
3455     for dir in DEFAULT_PATH:
3456         add_to_path(dir)
3457
3458 # global hack for the --select handling
3459 tgt_select = {}
3460 def init_select(args):
3461     # args = [service=nodeA,service2=nodeB service3=nodeC]
3462     global tgt_select
3463     for arg in args:
3464         list = string.split(arg, ',')
3465         for entry in list:
3466             srv, node = string.split(entry, '=')
3467             tgt_select[srv] = node
3468
3469 def get_select(srv):
3470     if tgt_select.has_key(srv):
3471         return tgt_select[srv]
3472     return None
3473
3474
3475 FLAG = Lustre.Options.FLAG
3476 PARAM = Lustre.Options.PARAM
3477 INTPARAM = Lustre.Options.INTPARAM
3478 PARAMLIST = Lustre.Options.PARAMLIST
3479 lconf_options = [
3480     ('verbose,v', "Print system commands as they are run"),
3481     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
3482     ('config', "Cluster config name used for LDAP query", PARAM),
3483     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
3484     ('node',   "Load config for <nodename>", PARAM),
3485     ('cleanup,d', "Cleans up config. (Shutdown)"),
3486     ('force,f', "Forced unmounting and/or obd detach during cleanup",
3487                FLAG, 0),
3488     ('single_socket', "socknal option: only use one socket instead of bundle",
3489                FLAG, 0),
3490     ('failover',"""Used to shut down without saving state.
3491                    This will allow this node to "give up" a service to a
3492                    another node for failover purposes. This will not
3493                    be a clean shutdown.""",
3494                FLAG, 0),
3495     ('gdb', """Prints message after creating gdb module script
3496                     and sleeps for 5 seconds."""),
3497     ('noexec,n', """Prints the commands and steps that will be run for a
3498                     config without executing them. This can used to check if a
3499                     config file is doing what it should be doing"""),
3500     ('nomod', "Skip load/unload module step."),
3501     ('nosetup', "Skip device setup/cleanup step."),
3502     ('reformat', "Reformat all devices (without question)"),
3503     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
3504     ('mountfsoptions', "Additional options for mount fs command line", PARAM),
3505     ('clientoptions', "Additional options for Lustre", PARAM),
3506     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
3507                PARAM),
3508     ('write_conf', "Save all the client config information on mds."),
3509     ('record', "Write config information on mds."),
3510     ('record_log', "Name of config record log.", PARAM),
3511     ('record_device', "MDS device name that will record the config commands",
3512               PARAM),
3513     ('root_squash', "MDS squash root to appointed uid",
3514               PARAM),
3515     ('no_root_squash', "Don't squash root for appointed nid",
3516               PARAM),
3517     ('minlevel', "Minimum level of services to configure/cleanup",
3518                  INTPARAM, 0),
3519     ('maxlevel', """Maximum level of services to configure/cleanup 
3520                     Levels are aproximatly like:
3521                             10 - netwrk
3522                             20 - device, ldlm
3523                             30 - osd, mdd
3524                             40 - mds, ost
3525                             70 - mountpoint, echo_client, osc, mdc, lov""",
3526                INTPARAM, 100),
3527     ('lustre', """Base directory of lustre sources. This parameter will
3528                   cause lconf to load modules from a source tree.""", PARAM),
3529     ('portals', """Portals source directory.  If this is a relative path,
3530                    then it is assumed to be relative to lustre. """, PARAM),
3531     ('timeout', "Set recovery timeout", INTPARAM),
3532     ('upcall',  "Set both portals and lustre upcall script", PARAM),
3533     ('lustre_upcall', "Set lustre upcall script", PARAM),
3534     ('portals_upcall', "Set portals upcall script", PARAM),
3535     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
3536     ('ptldebug', "Set the portals debug level",  PARAM),
3537     ('subsystem', "Set the portals debug subsystem",  PARAM),
3538     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
3539     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
3540 # Client recovery options
3541     ('recover', "Recover a device"),
3542     ('group', "The group of devices to configure or cleanup", PARAM),
3543     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
3544     ('client_uuid', "The failed client (required for recovery)", PARAM),
3545     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
3546
3547     ('inactive', """The name of an inactive service, to be ignored during
3548                     mounting (currently OST-only). Can be repeated.""",
3549                 PARAMLIST),
3550     ]      
3551
3552 def main():
3553     global lctl, config, toplustreDB, CONFIG_FILE, mod_manager
3554
3555     # in the upcall this is set to SIG_IGN
3556     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
3557     
3558     cl = Lustre.Options("lconf", "config.xml", lconf_options)
3559     try:
3560         config, args = cl.parse(sys.argv[1:])
3561     except Lustre.OptionError, e:
3562         print e
3563         sys.exit(1)
3564
3565     setupModulePath(sys.argv[0])
3566
3567     host = socket.gethostname()
3568
3569     # the PRNG is normally seeded with time(), which is not so good for starting
3570     # time-synchronized clusters
3571     input = open('/dev/urandom', 'r')
3572     if not input:
3573         print 'Unable to open /dev/urandom!'
3574         sys.exit(1)
3575     seed = input.read(32)
3576     input.close()
3577     random.seed(seed)
3578
3579     sanitise_path()
3580     
3581     init_select(config.select)
3582
3583     if len(args) > 0:
3584         # allow config to be fetched via HTTP, but only with python2
3585         if sys.version[0] != '1' and args[0].startswith('http://'):
3586             import urllib2
3587             try:
3588                 config_file = urllib2.urlopen(args[0])
3589             except (urllib2.URLError, socket.error), err:
3590                 if hasattr(err, 'args'):
3591                     err = err.args[1]
3592                 print "Could not access '%s': %s" %(args[0], err)
3593                 sys.exit(1)
3594         elif not os.access(args[0], os.R_OK):
3595             print 'File not found or readable:', args[0]
3596             sys.exit(1)
3597         else:
3598             # regular file
3599             config_file = open(args[0], 'r')
3600         try:
3601             dom = xml.dom.minidom.parse(config_file)
3602         except Exception:
3603             panic("%s does not appear to be a config file." % (args[0]))
3604             sys.exit(1) # make sure to die here, even in debug mode.
3605         config_file.close()
3606         CONFIG_FILE = args[0]
3607         lustreDB = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
3608         if not config.config:
3609             config.config = os.path.basename(args[0])# use full path?
3610             if config.config[-4:] == '.xml':
3611                 config.config = config.config[:-4]
3612     elif config.ldapurl:
3613         if not config.config:
3614             panic("--ldapurl requires --config name")
3615         dn = "config=%s,fs=lustre" % (config.config)
3616         lustreDB = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
3617     elif config.ptldebug or config.subsystem:
3618         sys_set_ptldebug(None)
3619         sys_set_subsystem(None)
3620         sys.exit(0)
3621     else:
3622         print 'Missing config file or ldap URL.'
3623         print 'see lconf --help for command summary'
3624         sys.exit(1)
3625
3626     toplustreDB = lustreDB
3627
3628     ver = lustreDB.get_version()
3629     if not ver:
3630         panic("No version found in config data, please recreate.")
3631     if ver != Lustre.CONFIG_VERSION:
3632         panic("Config version", ver, "does not match lconf version",
3633               Lustre.CONFIG_VERSION)
3634
3635     node_list = []
3636     if config.node:
3637         node_list.append(config.node)
3638     else:
3639         if len(host) > 0:
3640             node_list.append(host)
3641         node_list.append('localhost')
3642
3643     debug("configuring for host: ", node_list)
3644
3645     if len(host) > 0:
3646         config.debug_path = config.debug_path + '-' + host
3647         config.gdb_script = config.gdb_script + '-' + host
3648
3649     lctl = LCTLInterface('lctl')
3650
3651     if config.lctl_dump:
3652         lctl.use_save_file(config.lctl_dump)
3653
3654     if config.record:
3655         if not (config.record_device and config.record_log):
3656             panic("When recording, both --record_log and --record_device must be specified.")
3657         lctl.clear_log(config.record_device, config.record_log)
3658         lctl.record(config.record_device, config.record_log)
3659
3660     # init module manager
3661     mod_manager = kmod_manager(config.lustre, config.portals)
3662
3663     doHost(lustreDB, node_list)
3664
3665     if not config.record:
3666         return
3667
3668     lctl.end_record()
3669
3670     process_updates(lustreDB, config.record_device, config.record_log)
3671
3672 if __name__ == "__main__":
3673     try:
3674         main()
3675     except Lustre.LconfError, e:
3676         print e
3677 #        traceback.print_exc(file=sys.stdout)
3678         sys.exit(1)
3679     except CommandError, e:
3680         e.dump()
3681         sys.exit(e.rc)
3682
3683     if first_cleanup_error:
3684         sys.exit(first_cleanup_error)