Whamcloud - gitweb
- find_assigned_loop() should check all loop devices and do not return after first...
[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     return ''
924
925 # find free loop device
926 def find_free_loop(file):
927     loop = loop_base()
928     
929     # find next free loop
930     for n in xrange(0, MAX_LOOP_DEVICES):
931         dev = loop + str(n)
932         if os.access(dev, os.R_OK):
933             (stat, out) = run('losetup', dev)
934             if stat:
935                 return dev
936     return ''
937
938 # create file if necessary and assign the first free loop device
939 def init_loop(file, size, fstype, journal_size, inode_size, 
940               mkfsoptions, reformat, autoformat, backfstype, backfile):
941     if fstype == 'smfs':
942         realfile = backfile
943         realfstype = backfstype
944         if is_block(backfile):
945             if reformat or (need_format(realfstype, backfile) and autoformat == 'yes'):
946                 mkfs(realfile, size, realfstype, journal_size, inode_size, mkfsoptions, isblock=0)
947             return realfile
948     else:
949         realfile = file
950         realfstype = fstype
951             
952     dev = find_assigned_loop(realfile)
953     if dev:
954         print 'WARNING: file', realfile, 'already mapped to', dev
955         return dev
956             
957     if reformat or not os.access(realfile, os.R_OK | os.W_OK):
958         (ret, out) = run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size, realfile))
959         if ret:
960             panic("Unable to create backing store:", realfile)
961         mkfs(realfile, size, realfstype, journal_size, inode_size, 
962              mkfsoptions, isblock=0)
963
964     dev = find_free_loop(realfile)
965     if dev:
966         print "attach " + realfile + " <-> " + dev                  
967         run('losetup', dev, realfile)
968         return dev
969
970     print "out of loop devices"
971     return ''
972
973 # undo loop assignment
974 def clean_loop(dev, fstype, backfstype, backdev):
975     if fstype == 'smfs':
976         realfile = backdev
977     else:
978         realfile = dev
979     if not is_block(realfile):
980         dev = find_assigned_loop(realfile)
981         if dev:
982             print "detach " + dev + " <-> " + realfile
983             ret, out = run('losetup -d', dev)
984             if ret:
985                 log('unable to clean loop device:', dev, 'for file:', realfile)
986                 logall(out)
987
988 # finilizes passed device
989 def clean_dev(dev, fstype, backfstype, backdev):
990     if fstype == 'smfs' or not is_block(dev):
991         clean_loop(dev, fstype, backfstype, backdev)
992         
993 # determine if dev is formatted as a <fstype> filesystem
994 def need_format(fstype, dev):
995     # FIXME don't know how to implement this    
996     return 0
997
998 # initialize a block device if needed
999 def block_dev(dev, size, fstype, reformat, autoformat, journal_size,
1000               inode_size, mkfsoptions, backfstype, backdev):
1001     if config.noexec: 
1002         return dev
1003         
1004     if fstype == 'smfs' or not is_block(dev):
1005         dev = init_loop(dev, size, fstype, journal_size, inode_size,
1006                         mkfsoptions, reformat, autoformat, backfstype, backdev)
1007     elif reformat or (need_format(fstype, dev) and autoformat == 'yes'):
1008         mkfs(dev, size, fstype, journal_size, inode_size, mkfsoptions,
1009              isblock=0)
1010 #    else:
1011 #        panic("device:", dev,
1012 #              "not prepared, and autoformat is not set.\n",
1013 #              "Rerun with --reformat option to format ALL filesystems")
1014         
1015     return dev
1016
1017 def if2addr(iface):
1018     """lookup IP address for an interface"""
1019     rc, out = run("/sbin/ifconfig", iface)
1020     if rc or not out:
1021        return None
1022     addr = string.split(out[1])[1]
1023     ip = string.split(addr, ':')[1]
1024     return ip
1025
1026 def def_mount_options(fstype, target):
1027     """returns deafult mount options for passed fstype and target (mds, ost)"""
1028     if fstype == 'ext3' or fstype == 'ldiskfs':
1029         mountfsoptions = "errors=remount-ro"
1030         if target == 'ost' and sys_get_branch() == '2.4':
1031             mountfsoptions = "%s,asyncdel" % (mountfsoptions)
1032         return mountfsoptions
1033     return ""
1034         
1035 def sys_get_elan_position_file():
1036     procfiles = ["/proc/elan/device0/position",
1037                  "/proc/qsnet/elan4/device0/position",
1038                  "/proc/qsnet/elan3/device0/position"]
1039     for p in procfiles:
1040         if os.access(p, os.R_OK):
1041             return p
1042     return ""
1043
1044 def sys_get_local_nid(net_type, wildcard, cluster_id):
1045     """Return the local nid."""
1046     local = ""
1047     if sys_get_elan_position_file():
1048         local = sys_get_local_address('elan', '*', cluster_id)
1049     else:
1050         local = sys_get_local_address(net_type, wildcard, cluster_id)
1051     return local
1052         
1053 def sys_get_local_address(net_type, wildcard, cluster_id):
1054     """Return the local address for the network type."""
1055     local = ""
1056     if net_type in ('tcp','openib','iib','vib','ra'):
1057         if  ':' in wildcard:
1058             iface, star = string.split(wildcard, ':')
1059             local = if2addr(iface)
1060             if not local:
1061                 panic ("unable to determine ip for:", wildcard)
1062         else:
1063             host = socket.gethostname()
1064             local = socket.gethostbyname(host)
1065     elif net_type == 'elan':
1066         # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()'
1067         f = sys_get_elan_position_file()
1068         if not f:
1069             panic ("unable to determine local Elan ID")
1070         try:
1071             fp = open(f, 'r')
1072             lines = fp.readlines()
1073             fp.close()
1074             for l in lines:
1075                 a = string.split(l)
1076                 if a[0] == 'NodeId':
1077                     elan_id = a[1]
1078                     break
1079             try:
1080                 nid = my_int(cluster_id) + my_int(elan_id) 
1081                 local = "%d" % (nid)
1082             except ValueError, e:
1083                 local = elan_id
1084         except IOError, e:
1085             log(e)
1086     elif net_type == 'lo':
1087         fixme("automatic local address for loopback")
1088     elif net_type == 'gm':
1089         fixme("automatic local address for GM")
1090
1091     return local
1092
1093 def sys_get_branch():
1094     """Returns kernel release"""
1095     try:
1096         fp = open('/proc/sys/kernel/osrelease')
1097         lines = fp.readlines()
1098         fp.close()
1099         
1100         for l in lines:
1101             version = string.split(l)
1102             a = string.split(version[0], '.')
1103             return a[0] + '.' + a[1]
1104     except IOError, e:
1105         log(e)
1106     return ""
1107
1108 # XXX: instead of device_list, ask for $name and see what we get
1109 def is_prepared(name):
1110     """Return true if a device exists for the name"""
1111     if config.lctl_dump:
1112         return 0
1113     if (config.noexec or config.record) and config.cleanup:
1114         return 1
1115     try:
1116         # expect this format:
1117         # 1 UP ldlm ldlm ldlm_UUID 2
1118         out = lctl.device_list()
1119         for s in out:
1120             if name == string.split(s)[3]:
1121                 return 1
1122     except CommandError, e:
1123         e.dump()
1124     return 0
1125
1126 def net_is_prepared():
1127     """If the any device exists, then assume that all networking
1128        has been configured"""
1129     out = lctl.device_list()
1130     return len(out) > 0
1131
1132 def fs_is_mounted(path):
1133     """Return true if path is a mounted lustre filesystem"""
1134     try:
1135         fp = open('/proc/mounts')
1136         lines = fp.readlines()
1137         fp.close()
1138         for l in lines:
1139             a = string.split(l)
1140             if a[1] == path and a[2] == 'lustre_lite':
1141                 return 1
1142     except IOError, e:
1143         log(e)
1144     return 0
1145
1146 def kmod_find(src_dir, dev_dir, modname):
1147     modbase = src_dir +'/'+ dev_dir +'/'+ modname
1148     for modext in '.ko', '.o':
1149         module = modbase + modext
1150         try:
1151             if os.access(module, os.R_OK):
1152                 return module
1153         except OSError:
1154                pass
1155     return None
1156
1157 def kmod_info(modname):
1158     """Returns reference count for passed module name."""
1159     try:
1160         fp = open('/proc/modules')
1161         lines = fp.readlines()
1162         fp.close()
1163         
1164         # please forgive my tired fingers for this one
1165         ret = filter(lambda word, mod = modname: word[0] == mod,
1166                      map(lambda line: string.split(line), lines))
1167         if not ret:
1168             return ''
1169         return ret[0]
1170     except Exception, e:
1171         return 0
1172
1173 class kmod:
1174     """Presents kernel module"""
1175     def __init__(self, src_dir, dev_dir, name):
1176         self.src_dir = src_dir
1177         self.dev_dir = dev_dir
1178         self.name = name
1179
1180     def load(self):
1181         """Load module"""
1182         log ('loading module:', self.name, 'srcdir',
1183              self.src_dir, 'devdir', self.dev_dir)
1184         if self.src_dir:
1185             module = kmod_find(self.src_dir, self.dev_dir,
1186                                self.name)
1187             if not module:
1188                 panic('module not found:', self.name)
1189             (rc, out)  = run('/sbin/insmod', module)
1190             if rc:
1191                 raise CommandError('insmod', out, rc)
1192         else:
1193             (rc, out) = run('/sbin/modprobe', self.name)
1194             if rc:
1195                 raise CommandError('modprobe', out, rc)
1196
1197     def cleanup(self):
1198         """Unload module"""
1199         log('unloading module:', self.name)
1200         (rc, out) = run('/sbin/rmmod', self.name)
1201         if rc:
1202             log('unable to unload module:', self.name +
1203                 "(" + self.refcount() + ")")
1204             logall(out)
1205
1206     def info(self):
1207         """Returns module info if any."""
1208         return kmod_info(self.name)
1209
1210     def loaded(self):
1211         """Returns 1 if module is loaded. Otherwise 0 is returned."""
1212         if self.info():
1213             return 1
1214         else:
1215             return 0
1216
1217     def refcount(self):
1218         """Returns module refcount."""
1219         info = self.info()
1220         if not info:
1221             return ''
1222         return info[2]
1223
1224     def used(self):
1225         """Returns 1 if module is used, otherwise 0 is returned."""
1226         info = self.info()
1227         if not info:
1228             return 0
1229         if len(info) > 3:
1230             users = info[3]
1231             if users and users != '(unused)' and users != '-':
1232                 return 1
1233             else:
1234                 return 0
1235         else:
1236             return 0
1237
1238     def busy(self):
1239         """Returns 1 if module is busy, otherwise 0 is returned."""
1240         if self.loaded() and (self.used() or self.refcount() != '0'):
1241             return 1
1242         else:
1243             return 0
1244
1245 class kmod_manager:
1246     """Manage kernel modules"""
1247     def __init__(self, lustre_dir, portals_dir):
1248         self.lustre_dir = lustre_dir
1249         self.portals_dir = portals_dir
1250         self.kmodule_list = []
1251
1252     def find_module(self, modname):
1253         """Find module by module name"""
1254         for mod in self.kmodule_list:
1255             if mod.name == modname:
1256                 return mod
1257         return ''
1258         
1259     def add_portals_module(self, dev_dir, modname):
1260         """Append a module to list of modules to load."""
1261
1262         mod = self.find_module(modname)
1263         if not mod:
1264             mod = kmod(self.portals_dir, dev_dir, modname)
1265             self.kmodule_list.append(mod)
1266
1267     def add_lustre_module(self, dev_dir, modname):
1268         """Append a module to list of modules to load."""
1269
1270         mod = self.find_module(modname)
1271         if not mod:
1272             mod = kmod(self.lustre_dir, dev_dir, modname)
1273             self.kmodule_list.append(mod)
1274         
1275     def load_modules(self):
1276         """Load all the modules in the list in the order they appear."""
1277         for mod in self.kmodule_list:
1278             if mod.loaded() and not config.noexec:
1279                 continue
1280             mod.load()
1281
1282     def cleanup_modules(self):
1283         """Unload the modules in the list in reverse order."""
1284         rev = self.kmodule_list
1285         rev.reverse()
1286         for mod in rev:
1287             if (not mod.loaded() or mod.busy()) and not config.noexec:
1288                 continue
1289             # debug hack
1290             if mod.name == 'portals' and config.dump:
1291                 lctl.dump(config.dump)
1292             mod.cleanup()
1293             
1294 # ============================================================
1295 # Classes to prepare and cleanup the various objects
1296 #
1297 class Module:
1298     """ Base class for the rest of the modules. The default cleanup method is
1299     defined here, as well as some utilitiy funcs.
1300     """
1301     def __init__(self, module_name, db):
1302         self.db = db
1303         self.module_name = module_name
1304         self.name = self.db.getName()
1305         self.uuid = self.db.getUUID()
1306         self._server = None
1307         self._connected = 0
1308
1309     def info(self, *args):
1310         msg = string.join(map(str,args))
1311         print self.module_name + ":", self.name, self.uuid, msg
1312
1313     def cleanup(self):
1314         """ default cleanup, used for most modules """
1315         self.info()
1316         try:
1317             lctl.cleanup(self.name, self.uuid, config.force)
1318         except CommandError, e:
1319             log(self.module_name, "cleanup failed: ", self.name)
1320             e.dump()
1321             cleanup_error(e.rc)
1322
1323     def add_module(self, manager):
1324         """Adds all needed modules in the order they appear."""
1325         return
1326
1327     def safe_to_clean(self):
1328         return 1
1329
1330     def safe_to_clean_modules(self):
1331         return self.safe_to_clean()
1332         
1333 class Network(Module):
1334     def __init__(self,db):
1335         Module.__init__(self, 'NETWORK', db)
1336         self.net_type = self.db.get_val('nettype')
1337         self.nid = self.db.get_val('nid', '*')
1338         self.cluster_id = self.db.get_val('clusterid', "0")
1339         self.port = self.db.get_val_int('port', 0)
1340
1341         if '*' in self.nid:
1342             self.nid = sys_get_local_nid(self.net_type, self.nid, self.cluster_id)
1343             if not self.nid:
1344                 panic("unable to set nid for", self.net_type, self.nid, cluster_id)
1345             self.generic_nid = 1
1346             debug("nid:", self.nid)
1347         else:
1348             self.generic_nid = 0
1349
1350         self.nid_uuid = self.nid_to_uuid(self.nid)
1351         self.hostaddr = self.db.get_hostaddr()
1352         if len(self.hostaddr) == 0:
1353             self.hostaddr.append(self.nid)
1354         if '*' in self.hostaddr[0]:
1355             self.hostaddr[0] = sys_get_local_address(self.net_type, self.hostaddr[0], self.cluster_id)
1356             if not self.hostaddr[0]:
1357                 panic("unable to set hostaddr for", self.net_type, self.hostaddr[0], self.cluster_id)
1358             debug("hostaddr:", self.hostaddr[0])
1359
1360     def add_module(self, manager):
1361         manager.add_portals_module("libcfs", 'libcfs')
1362         manager.add_portals_module("portals", 'portals')
1363         if node_needs_router():
1364             manager.add_portals_module("router", 'kptlrouter')
1365         if self.net_type == 'tcp':
1366             manager.add_portals_module("knals/socknal", 'ksocknal')
1367         if self.net_type == 'elan':
1368             manager.add_portals_module("knals/qswnal", 'kqswnal')
1369         if self.net_type == 'gm':
1370             manager.add_portals_module("knals/gmnal", 'kgmnal')
1371         if self.net_type == 'openib':
1372             manager.add_portals_module("knals/openibnal", 'kopenibnal')
1373         if self.net_type == 'iib':
1374             manager.add_portals_module("knals/iibnal", 'kiibnal')
1375         if self.net_type == 'vib':
1376             self.add_portals_module("knals/vibnal", 'kvibnal')
1377         if self.net_type == 'lo':
1378             manager.add_portals_module("knals/lonal", 'klonal')
1379         if self.net_type == 'ra':
1380             manager.add_portals_module("knals/ranal", 'kranal')
1381
1382     def nid_to_uuid(self, nid):
1383         return "NID_%s_UUID" %(nid,)
1384
1385     def prepare(self):
1386         if not config.record and net_is_prepared():
1387             return
1388         self.info(self.net_type, self.nid, self.port)
1389         if not (config.record and self.generic_nid):
1390             lctl.network(self.net_type, self.nid)
1391         if self.net_type == 'tcp':
1392             sys_tweak_socknal()
1393             for hostaddr in self.db.get_hostaddr():
1394                 ip = string.split(hostaddr, '/')[0]
1395                 if len(string.split(hostaddr, '/')) == 2:
1396                     netmask = string.split(hostaddr, '/')[1]
1397                 else:
1398                     netmask = ""
1399                 lctl.add_interface(self.net_type, ip, netmask)
1400         if self.net_type == 'elan':
1401             sys_optimize_elan()
1402         if self.port and  node_is_router():
1403             run_one_acceptor(self.port)
1404             self.connect_peer_gateways()
1405
1406     def connect_peer_gateways(self):
1407         for router in self.db.lookup_class('node'):
1408             if router.get_val_int('router', 0):
1409                 for netuuid in router.get_networks():
1410                     net = self.db.lookup(netuuid)
1411                     gw = Network(net)
1412                     if (gw.cluster_id == self.cluster_id and
1413                         gw.net_type == self.net_type):
1414                         if gw.nid != self.nid:
1415                             lctl.connect(gw)
1416
1417     def disconnect_peer_gateways(self):
1418         for router in self.db.lookup_class('node'):
1419             if router.get_val_int('router', 0):
1420                 for netuuid in router.get_networks():
1421                     net = self.db.lookup(netuuid)
1422                     gw = Network(net)
1423                     if (gw.cluster_id == self.cluster_id and
1424                         gw.net_type == self.net_type):
1425                         if gw.nid != self.nid:
1426                             try:
1427                                 lctl.disconnect(gw)
1428                             except CommandError, e:
1429                                 print "disconnect failed: ", self.name
1430                                 e.dump()
1431                                 cleanup_error(e.rc)
1432
1433     def safe_to_clean(self):
1434         return not net_is_prepared()
1435
1436     def cleanup(self):
1437         self.info(self.net_type, self.nid, self.port)
1438         if self.port:
1439             stop_acceptor(self.port)
1440         if  node_is_router():
1441             self.disconnect_peer_gateways()
1442         if self.net_type == 'tcp':
1443             for hostaddr in self.db.get_hostaddr():
1444                 ip = string.split(hostaddr, '/')[0]
1445                 lctl.del_interface(self.net_type, ip)
1446
1447     def correct_level(self, level, op=None):
1448         return level
1449
1450 class RouteTable(Module):
1451     def __init__(self,db):
1452         Module.__init__(self, 'ROUTES', db)
1453
1454     def server_for_route(self, net_type, gw, gw_cluster_id, tgt_cluster_id,
1455                          lo, hi):
1456         # only setup connections for tcp, openib, and iib NALs
1457         srvdb = None
1458         if not net_type in ('tcp','openib','iib','vib','ra'):
1459             return None
1460
1461         # connect to target if route is to single node and this node is the gw
1462         if lo == hi and local_interface(net_type, gw_cluster_id, gw):
1463             if not local_cluster(net_type, tgt_cluster_id):
1464                 panic("target", lo, " not on the local cluster")
1465             srvdb = self.db.nid2server(lo, net_type, gw_cluster_id)
1466         # connect to gateway if this node is not the gw
1467         elif (local_cluster(net_type, gw_cluster_id)
1468               and not local_interface(net_type, gw_cluster_id, gw)):
1469             srvdb = self.db.nid2server(gw, net_type, gw_cluster_id)
1470         else:
1471             return None
1472
1473         if not srvdb:
1474             panic("no server for nid", lo)
1475             return None
1476
1477         return Network(srvdb)
1478         
1479     def prepare(self):
1480         if not config.record and net_is_prepared():
1481             return
1482         self.info()
1483         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1484             lctl.add_route(net_type, gw, lo, hi)
1485             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1486             if srv:
1487                 lctl.connect(srv)
1488
1489     def safe_to_clean(self):
1490         return not net_is_prepared()
1491
1492     def cleanup(self):
1493         if net_is_prepared():
1494             # the network is still being used, don't clean it up
1495             return
1496         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1497             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1498             if srv:
1499                 try:
1500                     lctl.disconnect(srv)
1501                 except CommandError, e:
1502                     print "disconnect failed: ", self.name
1503                     e.dump()
1504                     cleanup_error(e.rc)
1505
1506             try:
1507                 lctl.del_route(net_type, gw, lo, hi)
1508             except CommandError, e:
1509                 print "del_route failed: ", self.name
1510                 e.dump()
1511                 cleanup_error(e.rc)
1512
1513 class Management(Module):
1514     def __init__(self, db):
1515         Module.__init__(self, 'MGMT', db)
1516
1517     def add_module(self, manager):
1518         manager.add_lustre_module('lvfs', 'lvfs')
1519         manager.add_lustre_module('obdclass', 'obdclass')
1520         manager.add_lustre_module('ptlrpc', 'ptlrpc')
1521         manager.add_lustre_module('mgmt', 'mgmt_svc')
1522
1523     def prepare(self):
1524         if not config.record and is_prepared(self.name):
1525             return
1526         self.info()
1527         lctl.newdev("mgmt", self.name, self.uuid)
1528
1529     def safe_to_clean(self):
1530         return 1
1531
1532     def cleanup(self):
1533         if is_prepared(self.name):
1534             Module.cleanup(self)
1535
1536     def correct_level(self, level, op=None):
1537         return level
1538
1539 # This is only needed to load the modules; the LDLM device
1540 # is now created automatically.
1541 class LDLM(Module):
1542     def __init__(self,db):
1543         Module.__init__(self, 'LDLM', db)
1544
1545     def add_module(self, manager):
1546         manager.add_lustre_module('lvfs', 'lvfs')
1547         manager.add_lustre_module('obdclass', 'obdclass')
1548         manager.add_lustre_module('ptlrpc', 'ptlrpc')
1549
1550     def prepare(self):
1551         return
1552
1553     def cleanup(self):
1554         return
1555
1556     def correct_level(self, level, op=None):
1557         return level
1558
1559 class LOV(Module):
1560     def __init__(self, db, uuid, fs_name, name_override = None, config_only = None):
1561         Module.__init__(self, 'LOV', db)
1562         if name_override != None:
1563             self.name = "lov_%s" % name_override
1564         self.mds_uuid = self.db.get_first_ref('mds')
1565         self.stripe_sz = self.db.get_val_int('stripesize', 1048576)
1566         self.stripe_off = self.db.get_val_int('stripeoffset', 0)
1567         self.pattern = self.db.get_val_int('stripepattern', 0)
1568         self.devlist = self.db.get_lov_tgts('lov_tgt')
1569         self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist))
1570         self.osclist = []
1571         self.obdlist = []
1572         self.desc_uuid = self.uuid
1573         self.uuid = generate_client_uuid(self.name)
1574         self.fs_name = fs_name
1575         if config_only:
1576             self.config_only = 1
1577             return
1578         self.config_only = None
1579         mds = self.db.lookup(self.mds_uuid)
1580         self.mds_name = mds.getName()
1581         for (obd_uuid, index, gen, active) in self.devlist:
1582             if obd_uuid == '':
1583                 continue
1584             self.obdlist.append(obd_uuid)
1585             obd = self.db.lookup(obd_uuid)
1586             osc = get_osc(obd, self.uuid, fs_name)
1587             if osc:
1588                 self.osclist.append((osc, index, gen, active))
1589             else:
1590                 panic('osc not found:', obd_uuid)
1591     def get_uuid(self):
1592         return self.uuid
1593     def get_name(self):
1594         return self.name
1595     def prepare(self):
1596         if not config.record and is_prepared(self.name):
1597             return
1598         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
1599                   self.stripe_off, self.pattern, self.devlist,
1600                   self.mds_name)
1601         lctl.lov_setup(self.name, self.uuid, self.desc_uuid,  self.stripe_cnt,
1602                        self.stripe_sz, self.stripe_off, self.pattern,
1603                        string.join(self.obdlist))
1604         for (osc, index, gen, active) in self.osclist:
1605             target_uuid = osc.target_uuid
1606             try:
1607                 # Only ignore connect failures with --force, which
1608                 # isn't implemented here yet.
1609                 osc.active = active
1610                 osc.prepare(ignore_connect_failure=0)
1611             except CommandError, e:
1612                 print "Error preparing OSC %s\n" % osc.uuid
1613                 raise e
1614             lctl.lov_add_obd(self.name, self.uuid, target_uuid, index, gen)
1615
1616     def cleanup(self):
1617         for (osc, index, gen, active) in self.osclist:
1618             target_uuid = osc.target_uuid
1619             osc.cleanup()
1620         if is_prepared(self.name):
1621             Module.cleanup(self)
1622         if self.config_only:
1623             panic("Can't clean up config_only LOV ", self.name)
1624
1625     def add_module(self, manager):
1626         if self.config_only:
1627             panic("Can't load modules for config_only LOV ", self.name)
1628         for (osc, index, gen, active) in self.osclist:
1629             osc.add_module(manager)
1630             break
1631         manager.add_lustre_module('lov', 'lov')
1632
1633     def correct_level(self, level, op=None):
1634         return level
1635
1636 class LMV(Module):
1637     def __init__(self, db, uuid, fs_name, name_override = None):
1638         Module.__init__(self, 'LMV', db)
1639         if name_override != None:
1640             self.name = "lmv_%s" % name_override
1641             
1642         self.devlist = self.db.get_lmv_tgts('lmv_tgt')
1643         if self.devlist == None:
1644             self.devlist = self.db.get_refs('mds')
1645             
1646         self.mdclist = []
1647         self.desc_uuid = self.uuid
1648         self.uuid = uuid
1649         self.fs_name = fs_name
1650         for mds_uuid in self.devlist:
1651             mds = self.db.lookup(mds_uuid)
1652             if not mds:
1653                 panic("MDS not found!")
1654             mdc = MDC(mds, self.uuid, fs_name)
1655             if mdc:
1656                  self.mdclist.append(mdc)
1657             else:
1658                  panic('mdc not found:', mds_uuid)
1659
1660     def prepare(self):
1661         if is_prepared(self.name):
1662             return
1663             
1664         self.info();
1665         for mdc in self.mdclist:
1666             try:
1667                 # Only ignore connect failures with --force, which
1668                 # isn't implemented here yet.
1669                 mdc.prepare(ignore_connect_failure=0)
1670             except CommandError, e:
1671                 print "Error preparing LMV %s\n" % mdc.uuid
1672                 raise e
1673         
1674         lctl.lmv_setup(self.name, self.uuid, self.desc_uuid,
1675                        string.join(self.devlist))
1676
1677     def cleanup(self):
1678         for mdc in self.mdclist:
1679             mdc.cleanup()
1680         if is_prepared(self.name):
1681             Module.cleanup(self)
1682
1683     def add_module(self, manager):
1684         for mdc in self.mdclist:
1685             mdc.add_module(manager)
1686             break
1687         manager.add_lustre_module('lmv', 'lmv')
1688
1689     def correct_level(self, level, op=None):
1690         return level
1691
1692 class MDSDEV(Module):
1693     def __init__(self,db):
1694         Module.__init__(self, 'MDSDEV', db)
1695         self.devpath = self.db.get_val('devpath','')
1696         self.backdevpath = self.db.get_val('backdevpath','')
1697         self.size = self.db.get_val_int('devsize', 0)
1698         self.journal_size = self.db.get_val_int('journalsize', 0)
1699         self.fstype = self.db.get_val('fstype', '')
1700         self.backfstype = self.db.get_val('backfstype', '')
1701         self.nspath = self.db.get_val('nspath', '')
1702         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1703         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
1704         self.obdtype = self.db.get_val('obdtype', '')
1705         self.root_squash = self.db.get_val('root_squash', '')
1706         self.no_root_squash = self.db.get_val('no_root_squash', '')
1707         # overwrite the orignal MDSDEV name and uuid with the MDS name and uuid
1708         target_uuid = self.db.get_first_ref('target')
1709         self.mds = self.db.lookup(target_uuid)
1710         self.name = self.mds.getName()
1711         self.client_uuids = self.mds.get_refs('client')
1712         
1713         self.lmv = None
1714         self.master = None
1715         
1716         lmv_uuid = self.db.get_first_ref('lmv')
1717         if lmv_uuid != None:
1718             self.lmv = self.db.lookup(lmv_uuid)
1719             if self.lmv != None:
1720                 self.client_uuids = self.lmv.get_refs('client')
1721
1722         # FIXME: if fstype not set, then determine based on kernel version
1723         self.format = self.db.get_val('autoformat', "no")
1724         if self.mds.get_val('failover', 0):
1725             self.failover_mds = 'f'
1726         else:
1727             self.failover_mds = 'n'
1728         active_uuid = get_active_target(self.mds)
1729         if not active_uuid:
1730             panic("No target device found:", target_uuid)
1731         if active_uuid == self.uuid:
1732             self.active = 1
1733         else:
1734             self.active = 0
1735         if self.active and config.group and config.group != self.mds.get_val('group'):
1736             self.active = 0
1737
1738         # default inode inode for case when neither LOV either 
1739         # LMV is accessible.
1740         self.inode_size = 256
1741         
1742         inode_size = self.db.get_val_int('inodesize', 0)
1743         if not inode_size == 0:
1744             self.inode_size = inode_size
1745         else:
1746             # find the LOV for this MDS
1747             lovconfig_uuid = self.mds.get_first_ref('lovconfig')
1748             if lovconfig_uuid or self.lmv != None:
1749                 if self.lmv != None:
1750                     lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1751                     lovconfig = self.lmv.lookup(lovconfig_uuid)
1752                     lov_uuid = lovconfig.get_first_ref('lov')
1753                     if lov_uuid == None:
1754                         panic(self.mds.getName() + ": No LOV found for lovconfig ", 
1755                               lovconfig.name)
1756                 else:
1757                     lovconfig = self.mds.lookup(lovconfig_uuid)
1758                     lov_uuid = lovconfig.get_first_ref('lov')
1759                     if lov_uuid == None:
1760                        panic(self.mds.getName() + ": No LOV found for lovconfig ", 
1761                              lovconfig.name)
1762
1763                     if self.lmv != None:
1764                         lovconfig_uuid = self.lmv.get_first_ref('lovconfig')
1765                         lovconfig = self.lmv.lookup(lovconfig_uuid)
1766                         lov_uuid = lovconfig.get_first_ref('lov')
1767
1768                 lov = LOV(self.db.lookup(lov_uuid), lov_uuid, self.name, 
1769                           config_only = 1)
1770
1771                 # default stripe count controls default inode_size
1772                 stripe_count = lov.stripe_cnt
1773                 if stripe_count > 77:
1774                     self.inode_size = 4096
1775                 elif stripe_count > 35:
1776                     self.inode_size = 2048
1777                 elif stripe_count > 13:
1778                     self.inode_size = 1024
1779                 elif stripe_count > 3:
1780                     self.inode_size = 512
1781                 else:
1782                     self.inode_size = 256
1783
1784         self.target_dev_uuid = self.uuid
1785         self.uuid = target_uuid
1786
1787         # setup LMV
1788         if self.lmv != None:
1789             client_uuid = self.name + "_lmv_UUID"
1790             self.master = LMV(self.lmv, client_uuid, 
1791                               self.name, self.name)
1792
1793     def add_module(self, manager):
1794         if self.active:
1795             manager.add_lustre_module('mdc', 'mdc')
1796             manager.add_lustre_module('osc', 'osc')
1797             manager.add_lustre_module('ost', 'ost')
1798             manager.add_lustre_module('lov', 'lov')
1799             manager.add_lustre_module('mds', 'mds')
1800
1801             if self.fstype == 'smfs' or self.fstype == 'ldiskfs':
1802                 manager.add_lustre_module(self.fstype, self.fstype)
1803                 
1804             if self.fstype:
1805                 manager.add_lustre_module('lvfs', 'fsfilt_%s' % (self.fstype))
1806             
1807             # if fstype is smfs, then we should also take care about backing 
1808             # store fs.
1809             if self.fstype == 'smfs':
1810                 manager.add_lustre_module(self.backfstype, self.backfstype)
1811                 manager.add_lustre_module('lvfs', 'fsfilt_%s' % (self.backfstype))
1812
1813             for option in string.split(self.mountfsoptions, ','):
1814                 if option == 'snap':
1815                     if not self.fstype == 'smfs':
1816                         panic("mountoptions has 'snap', but fstype is not smfs.")
1817                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.fstype))
1818                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.backfstype))
1819
1820         # add LMV modules
1821         if self.master != None:
1822             self.master.add_module(manager)
1823             
1824     def get_mount_options(self, blkdev):
1825         options = def_mount_options(self.fstype, 'mds')
1826             
1827         if config.mountfsoptions:
1828             if options:
1829                 options = "%s,%s" %(options, config.mountfsoptions)
1830             else:
1831                 options = config.mountfsoptions
1832             if self.mountfsoptions:
1833                 options = "%s,%s" %(options, self.mountfsoptions)
1834         else:
1835             if self.mountfsoptions:
1836                 if options:
1837                     options = "%s,%s" %(options, self.mountfsoptions)
1838                 else:
1839                     options = self.mountfsoptions
1840             
1841         if self.fstype == 'smfs':
1842             if options:
1843                 options = "%s,type=%s,dev=%s" %(options, 
1844                            self.backfstype, blkdev)
1845             else:
1846                 options = "type=%s,dev=%s" %(self.backfstype, blkdev)
1847         return options
1848         
1849     def prepare(self):
1850         if not config.record and is_prepared(self.name):
1851             return
1852         if not self.active:
1853             debug(self.uuid, "not active")
1854             return
1855         if config.reformat:
1856             # run write_conf automatically, if --reformat used
1857             self.write_conf()
1858         run_acceptors()
1859         
1860         # prepare LMV
1861         if self.master != None:
1862              self.master.prepare()
1863              
1864         # never reformat here
1865         blkdev = block_dev(self.devpath, self.size, self.fstype, 0,
1866                            self.format, self.journal_size, self.inode_size,
1867                            self.mkfsoptions, self.backfstype, self.backdevpath)
1868         
1869         if not is_prepared('MDT'):
1870             lctl.newdev("mdt", 'MDT', 'MDT_UUID', setup ="")
1871         try: 
1872             if self.fstype == 'smfs':
1873                 realdev = self.fstype
1874             else:
1875                 realdev = blkdev
1876                 
1877             if self.obdtype == None:
1878                 self.obdtype = 'dumb'
1879                 
1880             if self.master == None:
1881                 master_name = 'dumb'
1882             else:
1883                 master_name = self.master.name
1884                 
1885             if self.client_uuids == None:
1886                 profile_name = 'dumb'
1887             else:
1888                 profile_name = self.name
1889             
1890             mountfsoptions = self.get_mount_options(blkdev)
1891
1892             self.info("mds", realdev, mountfsoptions, self.fstype, self.size, 
1893                       self.format, master_name, profile_name, self.obdtype)
1894             
1895             lctl.newdev("mds", self.name, self.uuid,
1896                         setup = "%s %s %s %s %s %s" %(realdev, 
1897                             self.fstype, profile_name, mountfsoptions,
1898                             master_name, self.obdtype))
1899
1900             if development_mode():
1901                 procentry = "/proc/fs/lustre/mds/grp_hash_upcall"
1902                 upcall = os.path.abspath(os.path.dirname(sys.argv[0]) + "/l_getgroups")
1903                 if not (os.access(procentry, os.R_OK) and os.access(upcall, os.R_OK)):
1904                     print "MDS Warning: failed to set group-hash upcall"
1905                 else:
1906                     run("echo ", upcall, " > ", procentry)
1907
1908         except CommandError, e:
1909             if e.rc == 2:
1910                 panic("MDS is missing the config log. Need to run " +
1911                        "lconf --write_conf.")
1912             else:
1913                 raise e
1914         
1915         if config.root_squash == None:
1916             config.root_squash = self.root_squash
1917         if config.no_root_squash == None:
1918             config.no_root_squash = self.no_root_squash
1919         if config.root_squash:
1920             if config.no_root_squash:
1921                 nsnid = config.no_root_squash
1922             else:
1923                 nsnid = "0"
1924             lctl.root_squash(self.name, config.root_squash, nsnid)
1925
1926     def write_conf(self):
1927         if not self.client_uuids:
1928             return 0
1929             
1930         do_cleanup = 0
1931         if not is_prepared(self.name):
1932             blkdev = block_dev(self.devpath, self.size, self.fstype,
1933                                config.reformat, self.format, self.journal_size,
1934                                self.inode_size, self.mkfsoptions,
1935                                self.backfstype, self.backdevpath)
1936
1937             if self.fstype == 'smfs':
1938                 realdev = self.fstype
1939             else:
1940                 realdev = blkdev
1941             
1942             # Even for writing logs we mount mds with supplied mount options
1943             # because it will not mount smfs (if used) otherwise.
1944             mountfsoptions = self.get_mount_options(blkdev)
1945
1946             if self.obdtype == None:
1947                 self.obdtype = 'dumb'
1948                 
1949             self.info("mds", realdev, mountfsoptions, self.fstype, self.size, 
1950                       self.format, "dumb", "dumb", self.obdtype)
1951             
1952             lctl.newdev("mds", self.name, self.uuid,
1953                         setup ="%s %s %s %s %s %s" %(realdev, self.fstype, 
1954                                                      'dumb', mountfsoptions,
1955                                                      'dumb', self.obdtype))
1956             do_cleanup = 1
1957
1958         # record logs for all MDS clients
1959         for obd_uuid in self.client_uuids:
1960             log("recording client:", obd_uuid)
1961
1962             client_uuid = generate_client_uuid(self.name)
1963             client = VOSC(self.db.lookup(obd_uuid), client_uuid, 
1964                           self.name, self.name)
1965             config.record = 1
1966             lctl.clear_log(self.name, self.name)
1967             lctl.record(self.name, self.name)
1968             client.prepare()
1969             lctl.mount_option(self.name, client.get_name(), "")
1970             lctl.end_record()
1971             process_updates(self.db, self.name, self.name, client)
1972
1973             config.cleanup = 1
1974             lctl.clear_log(self.name, self.name + '-clean')
1975             lctl.record(self.name, self.name + '-clean')
1976             client.cleanup()
1977             lctl.del_mount_option(self.name)
1978             lctl.end_record()
1979             process_updates(self.db, self.name, self.name + '-clean', client)
1980             config.cleanup = 0
1981             config.record = 0
1982
1983         # record logs for each client
1984         if config.noexec:
1985             noexec_opt = '-n'
1986         else:
1987             noexec_opt = ''
1988         if config.ldapurl:
1989             config_options = "--ldapurl " + config.ldapurl + " --config " + config.config
1990         else:
1991             config_options = CONFIG_FILE
1992
1993         for node_db in self.db.lookup_class('node'):
1994             client_name = node_db.getName()
1995             for prof_uuid in node_db.get_refs('profile'):
1996                 prof_db = node_db.lookup(prof_uuid)
1997                 # refactor this into a funtion to test "clientness"
1998                 # of a node.
1999                 for ref_class, ref_uuid in prof_db.get_all_refs():
2000                     if ref_class in ('mountpoint','echoclient'):
2001                         debug("recording", client_name)
2002                         old_noexec = config.noexec
2003                         config.noexec = 0
2004                         ret, out = run (sys.argv[0], noexec_opt,
2005                                         " -v --record --nomod",
2006                                         "--record_log", client_name,
2007                                         "--record_device", self.name,
2008                                         "--node", client_name,
2009                                         config_options)
2010                         if config.verbose:
2011                             for s in out: log("record> ", string.strip(s))
2012                         ret, out = run (sys.argv[0], noexec_opt,
2013                                         "--cleanup -v --record --nomod",
2014                                         "--record_log", client_name + "-clean",
2015                                         "--record_device", self.name,
2016                                         "--node", client_name,
2017                                         config_options)
2018                         if config.verbose:
2019                             for s in out: log("record> ", string.strip(s))
2020                         config.noexec = old_noexec
2021         if do_cleanup:
2022             try:
2023                 lctl.cleanup(self.name, self.uuid, 0, 0)
2024             except CommandError, e:
2025                 log(self.module_name, "cleanup failed: ", self.name)
2026                 e.dump()
2027                 cleanup_error(e.rc)
2028                 Module.cleanup(self)
2029
2030             clean_dev(self.devpath, self.fstype, self.backfstype, 
2031                       self.backdevpath)
2032
2033     def msd_remaining(self):
2034         out = lctl.device_list()
2035         for s in out:
2036             if string.split(s)[2] in ('mds',):
2037                 return 1
2038
2039     def safe_to_clean(self):
2040         return self.active
2041
2042     def safe_to_clean_modules(self):
2043         return not self.msd_remaining()
2044         
2045     def cleanup(self):
2046         if not self.active:
2047             debug(self.uuid, "not active")
2048             return
2049         self.info()
2050         if is_prepared(self.name):
2051             try:
2052                 lctl.cleanup(self.name, self.uuid, config.force,
2053                              config.failover)
2054             except CommandError, e:
2055                 log(self.module_name, "cleanup failed: ", self.name)
2056                 e.dump()
2057                 cleanup_error(e.rc)
2058                 Module.cleanup(self)
2059             # cleanup LMV
2060             if self.master != None:
2061                 self.master.cleanup()
2062         if not self.msd_remaining() and is_prepared('MDT'):
2063             try:
2064                 lctl.cleanup("MDT", "MDT_UUID", config.force,
2065                              config.failover)
2066             except CommandError, e:
2067                 print "cleanup failed: ", self.name
2068                 e.dump()
2069                 cleanup_error(e.rc)
2070         
2071         clean_dev(self.devpath, self.fstype, self.backfstype, 
2072                   self.backdevpath)
2073
2074     def correct_level(self, level, op=None):
2075         #if self.master != None:
2076         #   level = level + 2
2077         return level
2078
2079 class OSD(Module):
2080     def __init__(self, db):
2081         Module.__init__(self, 'OSD', db)
2082         self.osdtype = self.db.get_val('osdtype')
2083         self.devpath = self.db.get_val('devpath', '')
2084         self.backdevpath = self.db.get_val('backdevpath', '')
2085         self.size = self.db.get_val_int('devsize', 0)
2086         self.journal_size = self.db.get_val_int('journalsize', 0)
2087         self.inode_size = self.db.get_val_int('inodesize', 0)
2088         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
2089         self.mountfsoptions = self.db.get_val('mountfsoptions', '')
2090         self.fstype = self.db.get_val('fstype', '')
2091         self.backfstype = self.db.get_val('backfstype', '')
2092         self.nspath = self.db.get_val('nspath', '')
2093         target_uuid = self.db.get_first_ref('target')
2094         ost = self.db.lookup(target_uuid)
2095         self.name = ost.getName()
2096         self.format = self.db.get_val('autoformat', 'yes')
2097         if ost.get_val('failover', 0):
2098             self.failover_ost = 'f'
2099         else:
2100             self.failover_ost = 'n'
2101
2102         active_uuid = get_active_target(ost)
2103         if not active_uuid:
2104             panic("No target device found:", target_uuid)
2105         if active_uuid == self.uuid:
2106             self.active = 1
2107         else:
2108             self.active = 0
2109         if self.active and config.group and config.group != ost.get_val('group'):
2110             self.active = 0
2111
2112         self.target_dev_uuid = self.uuid
2113         self.uuid = target_uuid
2114     
2115     def add_module(self, manager):
2116         if self.active:
2117             manager.add_lustre_module('ost', 'ost')
2118             
2119             if self.fstype == 'smfs' or self.fstype == 'ldiskfs':
2120                 manager.add_lustre_module(self.fstype, self.fstype)
2121                 
2122             if self.fstype:
2123                 manager.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.fstype))
2124
2125             if self.fstype == 'smfs':
2126                 manager.add_lustre_module(self.backfstype, self.backfstype)
2127                 manager.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.backfstype))
2128
2129             for option in self.mountfsoptions:
2130                 if option == 'snap':
2131                     if not self.fstype == 'smfs':
2132                         panic("mountoptions with snap, but fstype is not smfs\n")
2133                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.fstype))
2134                     manager.add_lustre_module('lvfs', 'fsfilt_snap_%s' % (self.backfstype))
2135
2136             manager.add_lustre_module(self.osdtype, self.osdtype)
2137
2138     def get_mount_options(self, blkdev):
2139         options = def_mount_options(self.fstype, 'ost')
2140             
2141         if config.mountfsoptions:
2142             if options:
2143                 options = "%s,%s" %(options, config.mountfsoptions)
2144             else:
2145                 options = config.mountfsoptions
2146             if self.mountfsoptions:
2147                 options = "%s,%s" %(options, self.mountfsoptions)
2148         else:
2149             if self.mountfsoptions:
2150                 if options:
2151                     options = "%s,%s" %(options, self.mountfsoptions)
2152                 else:
2153                     options = self.mountfsoptions
2154             
2155         if self.fstype == 'smfs':
2156             if options:
2157                 options = "%s,type=%s,dev=%s" %(options, 
2158                     self.backfstype, blkdev)
2159             else:
2160                 options = "type=%s,dev=%s" %(self.backfstype, 
2161                     blkdev)
2162         return options
2163         
2164     # need to check /proc/mounts and /etc/mtab before
2165     # formatting anything.
2166     # FIXME: check if device is already formatted.
2167     def prepare(self):
2168         if is_prepared(self.name):
2169             return
2170         if not self.active:
2171             debug(self.uuid, "not active")
2172             return
2173         run_acceptors()
2174         if self.osdtype == 'obdecho':
2175             blkdev = ''
2176         else:
2177             blkdev = block_dev(self.devpath, self.size, self.fstype,
2178                                config.reformat, self.format, self.journal_size,
2179                                self.inode_size, self.mkfsoptions, self.backfstype,
2180                                self.backdevpath)
2181
2182         if self.fstype == 'smfs':
2183             realdev = self.fstype
2184         else:
2185             realdev = blkdev
2186
2187         mountfsoptions = self.get_mount_options(blkdev)
2188         
2189         self.info(self.osdtype, realdev, mountfsoptions, self.fstype, 
2190                   self.size, self.format, self.journal_size, self.inode_size)
2191                   
2192         lctl.newdev(self.osdtype, self.name, self.uuid,
2193                     setup ="%s %s %s %s" %(realdev, self.fstype,
2194                                            self.failover_ost, 
2195                                            mountfsoptions))
2196         if not is_prepared('OSS'):
2197             lctl.newdev("ost", 'OSS', 'OSS_UUID', setup ="")
2198
2199     def osd_remaining(self):
2200         out = lctl.device_list()
2201         for s in out:
2202             if string.split(s)[2] in ('obdfilter', 'obdecho'):
2203                 return 1
2204
2205     def safe_to_clean(self):
2206         return self.active
2207
2208     def safe_to_clean_modules(self):
2209         return not self.osd_remaining()
2210
2211     def cleanup(self):
2212         if not self.active:
2213             debug(self.uuid, "not active")
2214             return
2215         if is_prepared(self.name):
2216             self.info()
2217             try:
2218                 lctl.cleanup(self.name, self.uuid, config.force,
2219                              config.failover)
2220             except CommandError, e:
2221                 log(self.module_name, "cleanup failed: ", self.name)
2222                 e.dump()
2223                 cleanup_error(e.rc)
2224         if not self.osd_remaining() and is_prepared('OSS'):
2225             try:
2226                 lctl.cleanup("OSS", "OSS_UUID", config.force,
2227                              config.failover)
2228             except CommandError, e:
2229                 print "cleanup failed: ", self.name
2230                 e.dump()
2231                 cleanup_error(e.rc)
2232         if not self.osdtype == 'obdecho':
2233             clean_dev(self.devpath, self.fstype, self.backfstype, 
2234                       self.backdevpath)
2235
2236     def correct_level(self, level, op=None):
2237         return level
2238
2239 def mgmt_uuid_for_fs(mtpt_name):
2240     if not mtpt_name:
2241         return ''
2242     mtpt_db = toplustreDB.lookup_name(mtpt_name)
2243     fs_uuid = mtpt_db.get_first_ref('filesystem')
2244     fs = toplustreDB.lookup(fs_uuid)
2245     if not fs:
2246         return ''
2247     return fs.get_first_ref('mgmt')
2248
2249 # Generic client module, used by OSC and MDC
2250 class Client(Module):
2251     def __init__(self, tgtdb, uuid, module, fs_name, self_name=None,
2252                  module_dir=None):
2253         self.target_name = tgtdb.getName()
2254         self.target_uuid = tgtdb.getUUID()
2255         self.module_dir = module_dir
2256         self.module = module
2257         self.db = tgtdb
2258         self.active = 1
2259
2260         self.tgt_dev_uuid = get_active_target(tgtdb)
2261         if not self.tgt_dev_uuid:
2262             panic("No target device found for target(1):", self.target_name)
2263
2264         self._server = None
2265         self._connected = 0
2266
2267         self.module = module
2268         self.module_name = string.upper(module)
2269         if not self_name:
2270             self.name = '%s_%s_%s_%s' % (self.module_name, socket.gethostname(),
2271                                          self.target_name, fs_name)
2272         else:
2273             self.name = self_name
2274         self.uuid = uuid
2275         self.lookup_server(self.tgt_dev_uuid)
2276         mgmt_uuid = mgmt_uuid_for_fs(fs_name)
2277         if mgmt_uuid:
2278             self.mgmt_name = mgmtcli_name_for_uuid(mgmt_uuid)
2279         else:
2280             self.mgmt_name = ''
2281         self.fs_name = fs_name
2282         if not self.module_dir:
2283             self.module_dir = module
2284
2285     def add_module(self, manager):
2286         manager.add_lustre_module(self.module_dir, self.module)
2287
2288     def lookup_server(self, srv_uuid):
2289         """ Lookup a server's network information """
2290         self._server_nets = get_ost_net(self.db, srv_uuid)
2291         if len(self._server_nets) == 0:
2292             panic ("Unable to find a server for:", srv_uuid)
2293             
2294     def get_name(self):
2295         return self.name
2296
2297     def get_servers(self):
2298         return self._server_nets
2299
2300     def prepare(self, ignore_connect_failure = 0):
2301         self.info(self.target_uuid)
2302         if not config.record and is_prepared(self.name):
2303             self.cleanup()
2304         try:
2305             srv = choose_local_server(self.get_servers())
2306             if srv:
2307                 lctl.connect(srv)
2308             else:
2309                 routes = find_route(self.get_servers())
2310                 if len(routes) == 0:
2311                     panic ("no route to",  self.target_uuid)
2312                 for (srv, r) in routes:
2313                     lctl.add_route_host(r[0], srv.nid_uuid, r[1], r[3])
2314         except CommandError, e:
2315             if not ignore_connect_failure:
2316                 raise e
2317         if srv:
2318             if self.permits_inactive() and (self.target_uuid in config.inactive or self.active == 0):
2319                 debug("%s inactive" % self.target_uuid)
2320                 inactive_p = "inactive"
2321             else:
2322                 debug("%s active" % self.target_uuid)
2323                 inactive_p = ""
2324             lctl.newdev(self.module, self.name, self.uuid,
2325                         setup ="%s %s %s %s" % (self.target_uuid, srv.nid_uuid,
2326                                                 inactive_p, self.mgmt_name))
2327
2328     def cleanup(self):
2329         if is_prepared(self.name):
2330             Module.cleanup(self)
2331             try:
2332                 srv = choose_local_server(self.get_servers())
2333                 if srv:
2334                     lctl.disconnect(srv)
2335                 else:
2336                     for (srv, r) in find_route(self.get_servers()):
2337                         lctl.del_route_host(r[0], srv.nid_uuid, r[1], r[3])
2338             except CommandError, e:
2339                 log(self.module_name, "cleanup failed: ", self.name)
2340                 e.dump()
2341                 cleanup_error(e.rc)
2342
2343     def correct_level(self, level, op=None):
2344         return level
2345
2346     def deactivate(self):
2347         try:
2348             lctl.deactivate(self.name)
2349         except CommandError, e:
2350             log(self.module_name, "deactivate failed: ", self.name)
2351             e.dump()
2352             cleanup_error(e.rc)
2353
2354 class MDC(Client):
2355     def __init__(self, db, uuid, fs_name):
2356          Client.__init__(self, db, uuid, 'mdc', fs_name)
2357
2358     def permits_inactive(self):
2359         return 0
2360
2361 class OSC(Client):
2362     def __init__(self, db, uuid, fs_name):
2363          Client.__init__(self, db, uuid, 'osc', fs_name)
2364
2365     def permits_inactive(self):
2366         return 1
2367
2368 def mgmtcli_name_for_uuid(uuid):
2369     return 'MGMTCLI_%s' % uuid
2370
2371 class ManagementClient(Client):
2372     def __init__(self, db, uuid):
2373         Client.__init__(self, db, uuid, 'mgmt_cli', '',
2374                         self_name = mgmtcli_name_for_uuid(db.getUUID()),
2375                         module_dir = 'mgmt')
2376
2377 class CMOBD(Module):
2378     def __init__(self, db):
2379         Module.__init__(self, 'CMOBD', db)
2380         self.name = self.db.getName(); 
2381         self.uuid = generate_client_uuid(self.name)
2382         self.master_uuid = self.db.get_first_ref('masterobd')
2383         self.cache_uuid = self.db.get_first_ref('cacheobd')
2384
2385         master_obd = self.db.lookup(self.master_uuid)
2386         if not master_obd:
2387             panic('master obd not found:', self.master_uuid)
2388
2389         cache_obd = self.db.lookup(self.cache_uuid)
2390         if not cache_obd:
2391             panic('cache obd not found:', self.cache_uuid)
2392             
2393         self.master = None
2394         self.cache = None
2395             
2396         master_class = master_obd.get_class()
2397         cache_class = cache_obd.get_class()
2398
2399         if master_class == 'ost' or master_class == 'lov':
2400             client_uuid = "%s_lov_master_UUID" % (self.name)
2401             self.master = LOV(master_obd, client_uuid, self.name);
2402         elif master_class == 'mds':
2403             self.master = get_mdc(db, self.name, self.master_uuid) 
2404         elif master_class == 'lmv':
2405             client_uuid = "%s_lmv_master_UUID" % (self.name)
2406             self.master = LMV(master_obd, client_uuid, self.name);
2407         else:
2408             panic("unknown master obd class '%s'" %(master_class))
2409             
2410         if cache_class == 'ost' or cache_class == 'lov':
2411             client_uuid = "%s_lov_cache_UUID" % (self.name)
2412             self.cache = LOV(cache_obd, client_uuid, self.name);
2413         elif cache_class == 'mds':
2414             self.cache = get_mdc(db, self.name, self.cache_uuid)
2415         elif cache_class == 'lmv':
2416             client_uuid = "%s_lmv_cache_UUID" % (self.name)
2417             self.cache = LMV(cache_obd, client_uuid, self.name);
2418         else:
2419             panic("unknown cache obd class '%s'" %(cache_class))
2420
2421     def prepare(self):
2422         self.master.prepare()
2423         if not config.record and is_prepared(self.name):
2424             return
2425         self.info(self.master_uuid, self.cache_uuid)
2426         lctl.newdev("cmobd", self.name, self.uuid,
2427                     setup ="%s %s" %(self.master.uuid,
2428                                      self.cache.uuid))
2429
2430     def get_uuid(self):
2431         return self.uuid
2432         
2433     def get_name(self):
2434         return self.name
2435         
2436     def get_master_name(self):
2437         return self.master.name
2438             
2439     def get_cache_name(self):
2440         return self.cache.name
2441
2442     def cleanup(self):
2443         if is_prepared(self.name):
2444             Module.cleanup(self)
2445         if self.master:
2446             self.master.cleanup()
2447
2448     def add_module(self, manager):
2449         manager.add_lustre_module('cmobd', 'cmobd')
2450         self.master.add_module(manager)
2451
2452     def correct_level(self, level, op=None):
2453         return level
2454
2455 class COBD(Module):
2456     def __init__(self, db, uuid, name):
2457         Module.__init__(self, 'COBD', db)
2458         self.name = self.db.getName(); 
2459         self.uuid = generate_client_uuid(self.name)
2460         self.master_uuid = self.db.get_first_ref('masterobd')
2461         self.cache_uuid = self.db.get_first_ref('cacheobd')
2462
2463         master_obd = self.db.lookup(self.master_uuid)
2464         if not master_obd:
2465             panic('master obd not found:', self.master_uuid)
2466
2467         cache_obd = self.db.lookup(self.cache_uuid)
2468         if not cache_obd:
2469             panic('cache obd not found:', self.cache_uuid)
2470             
2471         self.master = None
2472         self.cache = None
2473
2474         master_class = master_obd.get_class()
2475         cache_class = cache_obd.get_class()
2476
2477         if master_class == 'ost' or master_class == 'lov':
2478             client_uuid = "%s_lov_master_UUID" % (self.name)
2479             self.master = LOV(master_obd, client_uuid, name);
2480         elif master_class == 'mds':
2481             self.master = get_mdc(db, name, self.master_uuid) 
2482         elif master_class == 'lmv':
2483             client_uuid = "%s_lmv_master_UUID" % (self.name)
2484             self.master = LMV(master_obd, client_uuid, self.name);
2485         else:
2486             panic("unknown master obd class '%s'" %(master_class))
2487             
2488         if cache_class == 'ost' or cache_class == 'lov':
2489             client_uuid = "%s_lov_cache_UUID" % (self.name)
2490             self.cache = LOV(cache_obd, client_uuid, name);
2491         elif cache_class == 'mds':
2492             self.cache = get_mdc(db, name, self.cache_uuid)
2493         elif cache_class == 'lmv':
2494             client_uuid = "%s_lmv_cache_UUID" % (self.name)
2495             self.cache = LMV(cache_obd, client_uuid, self.name);
2496         else:
2497             panic("unknown cache obd class '%s'" %(cache_class))
2498             
2499     def get_uuid(self):
2500         return self.uuid
2501
2502     def get_name(self):
2503         return self.name
2504
2505     def get_master_name(self):
2506         return self.master.name
2507
2508     def get_cache_name(self):
2509         return self.cache.name
2510
2511     def prepare(self):
2512         self.master.prepare()
2513         self.cache.prepare()
2514         if not config.record and is_prepared(self.name):
2515             return
2516         self.info(self.master_uuid, self.cache_uuid)
2517         lctl.newdev("cobd", self.name, self.uuid,
2518                     setup ="%s %s" %(self.master.name,
2519                                      self.cache.name))
2520
2521     def cleanup(self):
2522         if is_prepared(self.name):
2523             Module.cleanup(self)
2524         self.master.cleanup()
2525         self.cache.cleanup()
2526
2527     def add_module(self, manager):
2528         manager.add_lustre_module('cobd', 'cobd')
2529         self.master.add_module(manager)
2530
2531 # virtual interface for  OSC and LOV
2532 class VOSC(Module):
2533     def __init__(self, db, client_uuid, name, name_override = None):
2534         Module.__init__(self, 'VOSC', db)
2535         if db.get_class() == 'lov':
2536             self.osc = LOV(db, client_uuid, name, name_override)
2537             self.type = 'lov'
2538         elif db.get_class() == 'cobd':
2539             self.osc = COBD(db, client_uuid, name)
2540             self.type = 'cobd'
2541         else:
2542             self.osc = OSC(db, client_uuid, name)
2543             self.type = 'osc'
2544             
2545     def get_uuid(self):
2546         return self.osc.get_uuid()
2547
2548     def get_name(self):
2549         return self.osc.get_name()
2550
2551     def prepare(self):
2552         self.osc.prepare()
2553         
2554     def cleanup(self):
2555         self.osc.cleanup()
2556         
2557     def add_module(self, manager):
2558         self.osc.add_module(manager)
2559         
2560     def correct_level(self, level, op=None):
2561         return self.osc.correct_level(level, op)
2562
2563 # virtual interface for MDC and LMV
2564 class VMDC(Module):
2565     def __init__(self, db, client_uuid, name, name_override = None):
2566         Module.__init__(self, 'VMDC', db)
2567         if db.get_class() == 'lmv':
2568             self.mdc = LMV(db, client_uuid, name, name_override)
2569         elif db.get_class() == 'cobd':
2570             self.mdc = COBD(db, client_uuid, name)
2571         else:
2572             self.mdc = MDC(db, client_uuid, name)
2573             
2574     def get_uuid(self):
2575         return self.mdc.uuid
2576
2577     def get_name(self):
2578         return self.mdc.name
2579
2580     def prepare(self):
2581         self.mdc.prepare()
2582         
2583     def cleanup(self):
2584         self.mdc.cleanup()
2585         
2586     def add_module(self, manager):
2587         self.mdc.add_module(manager)
2588         
2589     def correct_level(self, level, op=None):
2590         return self.mdc.correct_level(level, op)
2591
2592 class ECHO_CLIENT(Module):
2593     def __init__(self,db):
2594         Module.__init__(self, 'ECHO_CLIENT', db)
2595         self.obd_uuid = self.db.get_first_ref('obd')
2596         obd = self.db.lookup(self.obd_uuid)
2597         self.uuid = generate_client_uuid(self.name)
2598         self.osc = VOSC(obd, self.uuid, self.name)
2599
2600     def prepare(self):
2601         if not config.record and is_prepared(self.name):
2602             return
2603         run_acceptors()
2604         self.osc.prepare() # XXX This is so cheating. -p
2605         self.info(self.obd_uuid)
2606
2607         lctl.newdev("echo_client", self.name, self.uuid,
2608                     setup = self.osc.get_name())
2609
2610     def cleanup(self):
2611         if is_prepared(self.name):
2612             Module.cleanup(self)
2613         self.osc.cleanup()
2614
2615     def add_module(self, manager):
2616         self.osc.add_module(manager)
2617         manager.add_lustre_module('obdecho', 'obdecho')
2618
2619     def correct_level(self, level, op=None):
2620         return level
2621
2622 def generate_client_uuid(name):
2623         client_uuid = '%05x_%.19s_%05x%05x' % (int(random.random() * 1048576),
2624                                                name,
2625                                                int(random.random() * 1048576),
2626                                                int(random.random() * 1048576))
2627         return client_uuid[:36]
2628
2629 class Mountpoint(Module):
2630     def __init__(self,db):
2631         Module.__init__(self, 'MTPT', db)
2632         self.path = self.db.get_val('path')
2633         self.clientoptions = self.db.get_val('clientoptions', '')
2634         self.fs_uuid = self.db.get_first_ref('filesystem')
2635         fs = self.db.lookup(self.fs_uuid)
2636         self.mds_uuid = fs.get_first_ref('lmv')
2637         if not self.mds_uuid:
2638             self.mds_uuid = fs.get_first_ref('mds')
2639         self.obd_uuid = fs.get_first_ref('obd')
2640         self.mgmt_uuid = fs.get_first_ref('mgmt')
2641         client_uuid = generate_client_uuid(self.name)
2642
2643         ost = self.db.lookup(self.obd_uuid)
2644         if not ost:
2645             panic("no ost: ", self.obd_uuid)
2646             
2647         mds = self.db.lookup(self.mds_uuid)
2648         if not mds:
2649             panic("no mds: ", self.mds_uuid)
2650        
2651         self.vosc = VOSC(ost, client_uuid, self.name, self.name)
2652         self.vmdc = VMDC(mds, client_uuid, self.name, self.name)
2653         
2654         if self.mgmt_uuid:
2655             self.mgmtcli = ManagementClient(db.lookup(self.mgmt_uuid),
2656                                             client_uuid)
2657         else:
2658             self.mgmtcli = None
2659
2660     def prepare(self):
2661         if not config.record and fs_is_mounted(self.path):
2662             log(self.path, "already mounted.")
2663             return
2664         run_acceptors()
2665         if self.mgmtcli:
2666             self.mgmtcli.prepare()
2667         self.vosc.prepare()
2668         self.vmdc.prepare()
2669         vmdc_name = self.vmdc.get_name()
2670
2671         self.info(self.path, self.mds_uuid, self.obd_uuid)
2672         if config.record or config.lctl_dump:
2673             lctl.mount_option(local_node_name, self.vosc.get_name(), vmdc_name)
2674             return
2675
2676         if config.clientoptions:
2677             if self.clientoptions:
2678                 self.clientoptions = self.clientoptions + ',' + \
2679                                      config.clientoptions
2680             else:
2681                 self.clientoptions = config.clientoptions
2682         if self.clientoptions:
2683             self.clientoptions = ',' + self.clientoptions
2684             # Linux kernel will deal with async and not pass it to ll_fill_super,
2685             # so replace it with Lustre async
2686             self.clientoptions = string.replace(self.clientoptions, "async", 
2687                                                 "lasync")
2688
2689         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s%s %s %s" % \
2690               (self.vosc.get_name(), vmdc_name, self.clientoptions, 
2691                config.config, self.path)
2692         run("mkdir", self.path)
2693         ret, val = run(cmd)
2694         if ret:
2695             self.vmdc.cleanup()            
2696             self.vosc.cleanup()
2697             panic("mount failed:", self.path, ":", string.join(val))
2698
2699     def cleanup(self):
2700         self.info(self.path, self.mds_uuid,self.obd_uuid)
2701
2702         if config.record or config.lctl_dump:
2703             lctl.del_mount_option(local_node_name)
2704         else:
2705             if fs_is_mounted(self.path):
2706                 if config.force:
2707                     (rc, out) = run("umount", "-f", self.path)
2708                 else:
2709                     (rc, out) = run("umount", self.path)
2710                 if rc:
2711                     raise CommandError('umount', out, rc)
2712
2713             if fs_is_mounted(self.path):
2714                 panic("fs is still mounted:", self.path)
2715
2716         self.vmdc.cleanup()
2717         self.vosc.cleanup()
2718         if self.mgmtcli:
2719             self.mgmtcli.cleanup()
2720
2721     def add_module(self, manager):
2722         manager.add_lustre_module('mdc', 'mdc')
2723         
2724         if self.mgmtcli:
2725             self.mgmtcli.add_module(manager)
2726         
2727         self.vosc.add_module(manager)
2728         self.vmdc.add_module(manager)
2729
2730         manager.add_lustre_module('llite', 'llite')
2731
2732     def correct_level(self, level, op=None):
2733         return level
2734
2735 # ============================================================
2736 # misc query functions
2737
2738 def get_ost_net(self, osd_uuid):
2739     srv_list = []
2740     if not osd_uuid:
2741         return srv_list
2742     osd = self.lookup(osd_uuid)
2743     node_uuid = osd.get_first_ref('node')
2744     node = self.lookup(node_uuid)
2745     if not node:
2746         panic("unable to find node for osd_uuid:", osd_uuid,
2747               " node_ref:", node_uuid_)
2748     for net_uuid in node.get_networks():
2749         db = node.lookup(net_uuid)
2750         srv_list.append(Network(db))
2751     return srv_list
2752
2753
2754 # the order of iniitailization is based on level. 
2755 def getServiceLevel(self):
2756     type = self.get_class()
2757     ret=0;
2758     if type in ('network',):
2759         ret = 5
2760     elif type in ('routetbl',):
2761         ret = 6
2762     elif type in ('ldlm',):
2763         ret = 20
2764     elif type in ('osd', 'cobd'):
2765         ret = 30
2766     elif type in ('mdsdev',):
2767         ret = 40
2768     elif type in ('lmv',):
2769         ret = 45
2770     elif type in ('cmobd',):
2771         ret = 50
2772     elif type in ('mountpoint', 'echoclient'):
2773         ret = 70
2774     else:
2775         panic("Unknown type: ", type)
2776
2777     if ret < config.minlevel or ret > config.maxlevel:
2778         ret = 0
2779     return ret
2780
2781 #
2782 # return list of services in a profile. list is a list of tuples
2783 # [(level, db_object),]
2784 def getServices(self):
2785     list = []
2786     for ref_class, ref_uuid in self.get_all_refs():
2787             servdb = self.lookup(ref_uuid)
2788             if  servdb:
2789                 level = getServiceLevel(servdb)
2790                 if level > 0:
2791                     list.append((level, servdb))
2792             else:
2793                 panic('service not found: ' + ref_uuid)
2794
2795     list.sort()
2796     return list
2797
2798
2799 ############################################################
2800 # MDC UUID hack -
2801 # FIXME: clean this mess up!
2802 #
2803 # OSC is no longer in the xml, so we have to fake it.
2804 # this is getting ugly and begging for another refactoring
2805 def get_osc(ost_db, uuid, fs_name):
2806     osc = OSC(ost_db, uuid, fs_name)
2807     return osc
2808
2809 def get_mdc(db, fs_name, mds_uuid):
2810     mds_db = db.lookup(mds_uuid);
2811     if not mds_db:
2812         error("no mds:", mds_uuid)
2813     mdc = MDC(mds_db, mds_uuid, fs_name)
2814     return mdc
2815
2816 ############################################################
2817 # routing ("rooting")
2818
2819 # list of (nettype, cluster_id, nid)
2820 local_clusters = []
2821
2822 def find_local_clusters(node_db):
2823     global local_clusters
2824     for netuuid in node_db.get_networks():
2825         net = node_db.lookup(netuuid)
2826         srv = Network(net)
2827         debug("add_local", netuuid)
2828         local_clusters.append((srv.net_type, srv.cluster_id, srv.nid))
2829         if srv.port > 0:
2830             if acceptors.has_key(srv.port):
2831                 panic("duplicate port:", srv.port)
2832             acceptors[srv.port] = AcceptorHandler(srv.port, srv.net_type)
2833
2834 # This node is a gateway.
2835 is_router = 0
2836 def node_is_router():
2837     return is_router
2838
2839 # If there are any routers found in the config, then this will be true
2840 # and all nodes will load kptlrouter.
2841 needs_router = 0
2842 def node_needs_router():
2843     return needs_router or is_router
2844
2845 # list of (nettype, gw, tgt_cluster_id, lo, hi)
2846 # Currently, these local routes are only added to kptlrouter route
2847 # table if they are needed to connect to a specific server.  This
2848 # should be changed so all available routes are loaded, and the
2849 # ptlrouter can make all the decisions.
2850 local_routes = []
2851
2852 def find_local_routes(lustre):
2853     """ Scan the lustre config looking for routers .  Build list of
2854     routes. """
2855     global local_routes, needs_router
2856     local_routes = []
2857     list = lustre.lookup_class('node')
2858     for router in list:
2859         if router.get_val_int('router', 0):
2860             needs_router = 1
2861             for (local_type, local_cluster_id, local_nid) in local_clusters:
2862                 gw = None
2863                 for netuuid in router.get_networks():
2864                     db = router.lookup(netuuid)
2865                     if (local_type == db.get_val('nettype') and
2866                        local_cluster_id == db.get_val('clusterid')):
2867                         gw = db.get_val('nid')
2868                         break
2869                 if gw:
2870                     debug("find_local_routes: gw is", gw)
2871                     for route in router.get_local_routes(local_type, gw):
2872                         local_routes.append(route)
2873     debug("find_local_routes:", local_routes)
2874
2875
2876 def choose_local_server(srv_list):
2877     for srv in srv_list:
2878         if local_cluster(srv.net_type, srv.cluster_id):
2879             return srv
2880
2881 def local_cluster(net_type, cluster_id):
2882     for cluster in local_clusters:
2883         if net_type == cluster[0] and cluster_id == cluster[1]:
2884             return 1
2885     return 0
2886
2887 def local_interface(net_type, cluster_id, nid):
2888     for cluster in local_clusters:
2889         if (net_type == cluster[0] and cluster_id == cluster[1]
2890             and nid == cluster[2]):
2891             return 1
2892     return 0
2893
2894 def find_route(srv_list):
2895     result = []
2896     frm_type = local_clusters[0][0]
2897     for srv in srv_list:
2898         debug("find_route: srv:", srv.nid, "type: ", srv.net_type)
2899         to_type = srv.net_type
2900         to = srv.nid
2901         cluster_id = srv.cluster_id
2902         debug ('looking for route to', to_type, to)
2903         for r in local_routes:
2904             debug("find_route: ", r)
2905             if  (r[3] <= to and to <= r[4]) and cluster_id == r[2]:
2906                 result.append((srv, r))
2907     return result
2908            
2909 def get_active_target(db):
2910     target_uuid = db.getUUID()
2911     target_name = db.getName()
2912     node_name = get_select(target_name)
2913     if node_name:
2914         tgt_dev_uuid = db.get_node_tgt_dev(node_name, target_uuid)
2915     else:
2916         tgt_dev_uuid = db.get_first_ref('active')
2917     return tgt_dev_uuid
2918
2919 def get_server_by_nid_uuid(db,  nid_uuid):
2920     for n in db.lookup_class("network"):
2921         net = Network(n)
2922         if net.nid_uuid == nid_uuid:
2923             return net
2924         
2925
2926 ############################################################
2927 # lconf level logic
2928 # Start a service.
2929 def newService(db):
2930     type = db.get_class()
2931     debug('Service:', type, db.getName(), db.getUUID())
2932     n = None
2933     if type == 'ldlm':
2934         n = LDLM(db)
2935     elif type == 'lov':
2936         n = LOV(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2937     elif type == 'network':
2938         n = Network(db)
2939     elif type == 'routetbl':
2940         n = RouteTable(db)
2941     elif type == 'osd':
2942         n = OSD(db)
2943     elif type == 'cobd':
2944         n = COBD(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2945     elif type == 'cmobd':
2946         n = CMOBD(db)
2947     elif type == 'mdsdev':
2948         n = MDSDEV(db)
2949     elif type == 'mountpoint':
2950         n = Mountpoint(db)
2951     elif type == 'echoclient':
2952         n = ECHO_CLIENT(db)
2953     elif type == 'lmv':
2954         n = LMV(db)
2955     else:
2956         panic ("unknown service type:", type)
2957     return n
2958
2959 #
2960 # Prepare the system to run lustre using a particular profile
2961 # in a the configuration. 
2962 #  * load & the modules
2963 #  * setup networking for the current node
2964 #  * make sure partitions are in place and prepared
2965 #  * initialize devices with lctl
2966 # Levels is important, and needs to be enforced.
2967 def for_each_profile(db, prof_list, operation):
2968     for prof_uuid in prof_list:
2969         prof_db = db.lookup(prof_uuid)
2970         if not prof_db:
2971             panic("profile:", prof_uuid, "not found.")
2972         services = getServices(prof_db)
2973         operation(services)
2974
2975 def magic_get_osc(db, rec, lov):
2976     if lov:
2977         lov_uuid = lov.get_uuid()
2978         lov_name = lov.osc.fs_name
2979     else:
2980         lov_uuid = rec.getAttribute('lov_uuidref')
2981         # FIXME: better way to find the mountpoint?
2982         filesystems = db.root_node.getElementsByTagName('filesystem')
2983         fsuuid = None
2984         for fs in filesystems:
2985             ref = fs.getElementsByTagName('obd_ref')
2986             if ref[0].getAttribute('uuidref') == lov_uuid:
2987                 fsuuid = fs.getAttribute('uuid')
2988                 break
2989
2990         if not fsuuid:
2991             panic("malformed xml: lov uuid '" + lov_uuid + "' referenced in 'add' record is not used by any filesystems.")
2992
2993         mtpts = db.root_node.getElementsByTagName('mountpoint')
2994         lov_name = None
2995         for fs in mtpts:
2996             ref = fs.getElementsByTagName('filesystem_ref')
2997             if ref[0].getAttribute('uuidref') == fsuuid:
2998                 lov_name = fs.getAttribute('name')
2999                 break
3000
3001         if not lov_name:
3002             panic("malformed xml: 'add' record references lov uuid '" + lov_uuid + "', which references filesystem uuid '" + fsuuid + "', which does not reference a mountpoint.")
3003
3004     print "lov_uuid: " + lov_uuid + "; lov_name: " + lov_name
3005
3006     ost_uuid = rec.getAttribute('ost_uuidref')
3007     obd = db.lookup(ost_uuid)
3008
3009     if not obd:
3010         panic("malformed xml: 'add' record references ost uuid '" + ost_uuid + "' which cannot be found.")
3011
3012     osc = get_osc(obd, lov_uuid, lov_name)
3013     if not osc:
3014         panic('osc not found:', obd_uuid)
3015     return osc
3016
3017 # write logs for update records.  sadly, logs of all types -- and updates in
3018 # particular -- are something of an afterthought.  lconf needs rewritten with
3019 # these as core concepts.  so this is a pretty big hack.
3020 def process_update_record(db, update, lov):
3021     for rec in update.childNodes:
3022         if rec.nodeType != rec.ELEMENT_NODE:
3023             continue
3024
3025         log("found "+rec.nodeName+" record in update version " +
3026             str(update.getAttribute('version')))
3027
3028         lov_uuid = rec.getAttribute('lov_uuidref')
3029         ost_uuid = rec.getAttribute('ost_uuidref')
3030         index = rec.getAttribute('index')
3031         gen = rec.getAttribute('generation')
3032
3033         if not lov_uuid or not ost_uuid or not index or not gen:
3034             panic("malformed xml: 'update' record requires lov_uuid, ost_uuid, index, and generation.")
3035
3036         if not lov:
3037             tmplov = db.lookup(lov_uuid)
3038             if not tmplov:
3039                 panic("malformed xml: 'delete' record contains lov UUID '" + lov_uuid + "', which cannot be located.")
3040             lov_name = tmplov.getName()
3041         else:
3042             lov_name = lov.osc.name
3043
3044         # ------------------------------------------------------------- add
3045         if rec.nodeName == 'add':
3046             if config.cleanup:
3047                 lctl.lov_del_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3048                 continue
3049
3050             osc = magic_get_osc(db, rec, lov)
3051
3052             try:
3053                 # Only ignore connect failures with --force, which
3054                 # isn't implemented here yet.
3055                 osc.prepare(ignore_connect_failure=0)
3056             except CommandError, e:
3057                 print "Error preparing OSC %s\n" % osc.uuid
3058                 raise e
3059
3060             lctl.lov_add_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3061
3062         # ------------------------------------------------------ deactivate
3063         elif rec.nodeName == 'deactivate':
3064             if config.cleanup:
3065                 continue
3066
3067             osc = magic_get_osc(db, rec, lov)
3068
3069             try:
3070                 osc.deactivate()
3071             except CommandError, e:
3072                 print "Error deactivating OSC %s\n" % osc.uuid
3073                 raise e
3074
3075         # ---------------------------------------------------------- delete
3076         elif rec.nodeName == 'delete':
3077             if config.cleanup:
3078                 continue
3079
3080             osc = magic_get_osc(db, rec, lov)
3081
3082             try:
3083                 config.cleanup = 1
3084                 osc.cleanup()
3085                 config.cleanup = 0
3086             except CommandError, e:
3087                 print "Error cleaning up OSC %s\n" % osc.uuid
3088                 raise e
3089
3090             lctl.lov_del_obd(lov_name, lov_uuid, ost_uuid, index, gen)
3091
3092 def process_updates(db, log_device, log_name, lov = None):
3093     updates = db.root_node.getElementsByTagName('update')
3094     for u in updates:
3095         if not u.childNodes:
3096             log("ignoring empty update record (version " +
3097                 str(u.getAttribute('version')) + ")")
3098             continue
3099
3100         version = u.getAttribute('version')
3101         real_name = "%s-%s" % (log_name, version)
3102         lctl.clear_log(log_device, real_name)
3103         lctl.record(log_device, real_name)
3104
3105         process_update_record(db, u, lov)
3106
3107         lctl.end_record()
3108
3109 def doWriteconf(services):
3110     #if config.nosetup:
3111     #    return
3112     for s in services:
3113         if s[1].get_class() == 'mdsdev':
3114             n = newService(s[1])
3115             n.write_conf()
3116
3117 def doSetup(services):
3118     if config.nosetup:
3119         return
3120     slist = []
3121     for s in services:
3122         n = newService(s[1])
3123         n.level = s[0]
3124         slist.append((n.level, n))
3125     nlist = []
3126     for n in slist:
3127         nl = n[1].correct_level(n[0])
3128         nlist.append((nl, n[1]))
3129     nlist.sort()
3130     for n in nlist:
3131         n[1].prepare()
3132
3133 def doLoadModules(services):
3134     if config.nomod:
3135         return
3136     
3137     # adding all needed modules from all services
3138     for s in services:
3139         n = newService(s[1])
3140         n.add_module(mod_manager)
3141     
3142     # loading all registered modules
3143     mod_manager.load_modules()
3144
3145 def doUnloadModules(services):
3146     if config.nomod:
3147         return
3148         
3149     # adding all needed modules from all services
3150     for s in services:
3151         n = newService(s[1])
3152         if n.safe_to_clean_modules():
3153             n.add_module(mod_manager)
3154     
3155     # unloading all registered modules
3156     mod_manager.cleanup_modules()
3157
3158 def doCleanup(services):
3159     if config.nosetup:
3160         return
3161     slist = []
3162
3163     for s in services:
3164         n = newService(s[1])
3165         n.level = s[0]
3166         slist.append((n.level, n))
3167     nlist = []
3168     for n in slist:
3169         nl = n[1].correct_level(n[0])
3170         nlist.append((nl, n[1]))
3171     nlist.sort()
3172     nlist.reverse()
3173
3174     for n in nlist:
3175         if n[1].safe_to_clean():
3176             n[1].cleanup()
3177
3178 #
3179 # Load profile for 
3180 def doHost(lustreDB, hosts):
3181     global is_router, local_node_name
3182     node_db = None
3183     for h in hosts:
3184         node_db = lustreDB.lookup_name(h, 'node')
3185         if node_db:
3186             break
3187     if not node_db:
3188         panic('No host entry found.')
3189
3190     local_node_name = node_db.get_val('name', 0)
3191     is_router = node_db.get_val_int('router', 0)
3192     lustre_upcall = node_db.get_val('lustreUpcall', '')
3193     portals_upcall = node_db.get_val('portalsUpcall', '')
3194     timeout = node_db.get_val_int('timeout', 0)
3195     ptldebug = node_db.get_val('ptldebug', '')
3196     subsystem = node_db.get_val('subsystem', '')
3197     
3198     find_local_clusters(node_db)
3199     if not is_router:
3200         find_local_routes(lustreDB)
3201
3202     # Two step process: (1) load modules, (2) setup lustre
3203     # if not cleaning, load modules first.
3204     prof_list = node_db.get_refs('profile')
3205
3206     if config.write_conf:
3207         for_each_profile(node_db, prof_list, doLoadModules)
3208         sys_make_devices()
3209         for_each_profile(node_db, prof_list, doWriteconf)
3210         for_each_profile(node_db, prof_list, doUnloadModules)
3211         lustreDB.close()
3212
3213     elif config.recover:
3214         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
3215             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
3216                                      "--client_uuid <UUID> --conn_uuid <UUID>")
3217         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
3218                    config.conn_uuid)
3219     elif config.cleanup:
3220         if config.force:
3221             # the command line can override this value
3222             timeout = 5
3223         # ugly hack, only need to run lctl commands for --dump
3224         if config.lctl_dump or config.record:
3225             for_each_profile(node_db, prof_list, doCleanup)
3226             return
3227
3228         sys_set_timeout(timeout)
3229         sys_set_ptldebug(ptldebug)
3230         sys_set_subsystem(subsystem)
3231         sys_set_lustre_upcall(lustre_upcall)
3232         sys_set_portals_upcall(portals_upcall)
3233
3234         for_each_profile(node_db, prof_list, doCleanup)
3235         for_each_profile(node_db, prof_list, doUnloadModules)
3236         lustreDB.close()
3237
3238     else:
3239         # ugly hack, only need to run lctl commands for --dump
3240         if config.lctl_dump or config.record:
3241             sys_set_timeout(timeout)
3242             sys_set_lustre_upcall(lustre_upcall)
3243             for_each_profile(node_db, prof_list, doSetup)
3244             return
3245
3246         sys_make_devices()
3247         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
3248         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
3249
3250         for_each_profile(node_db, prof_list, doLoadModules)
3251
3252         sys_set_debug_path()
3253         sys_set_ptldebug(ptldebug)
3254         sys_set_subsystem(subsystem)
3255         script = config.gdb_script
3256         run(lctl.lctl, ' modules >', script)
3257         if config.gdb:
3258             log ("The GDB module script is in", script)
3259             # pause, so user has time to break and
3260             # load the script
3261             time.sleep(5)
3262         sys_set_timeout(timeout)
3263         sys_set_lustre_upcall(lustre_upcall)
3264         sys_set_portals_upcall(portals_upcall)
3265
3266         for_each_profile(node_db, prof_list, doSetup)
3267         lustreDB.close()
3268
3269 def doRecovery(lustreDB, lctl, tgt_uuid, client_uuid, nid_uuid):
3270     tgt = lustreDB.lookup(tgt_uuid)
3271     if not tgt:
3272         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
3273     new_uuid = get_active_target(tgt)
3274     if not new_uuid:
3275         raise Lustre.LconfError("doRecovery: no active target found for: " +
3276                                 tgt_uuid)
3277     net = choose_local_server(get_ost_net(lustreDB, new_uuid))
3278     if not net:
3279         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
3280
3281     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
3282     try:
3283         oldnet = get_server_by_nid_uuid(lustreDB, nid_uuid)
3284         lustreDB.close()
3285         if oldnet:
3286             lctl.disconnect(oldnet)
3287     except CommandError, e:
3288         log("recover: disconnect", nid_uuid, "failed: ")
3289         e.dump()
3290
3291     try:
3292         lctl.connect(net)
3293     except CommandError, e:
3294         log("recover: connect failed")
3295         e.dump()
3296
3297     lctl.recover(client_uuid, net.nid_uuid)
3298
3299
3300 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
3301     base = os.path.dirname(cmd)
3302     if development_mode():
3303         if not config.lustre:
3304             debug('using objdir module paths')            
3305             config.lustre = (os.path.join(base, ".."))
3306         # normalize the portals dir, using command line arg if set
3307         if config.portals:
3308             portals_dir = config.portals
3309         dir = os.path.join(config.lustre, portals_dir)
3310         config.portals = dir
3311         debug('config.portals', config.portals)
3312     elif config.lustre and config.portals:
3313         # production mode
3314         # if --lustre and --portals, normalize portals 
3315         # can ignore POTRALS_DIR here, since it is probly useless here
3316         config.portals = os.path.join(config.lustre, config.portals)
3317         debug('config.portals B', config.portals)
3318
3319 def sysctl(path, val):
3320     debug("+ sysctl", path, val)
3321     if config.noexec:
3322         return
3323     try:
3324         fp = open(os.path.join('/proc/sys', path), 'w')
3325         fp.write(str(val))
3326         fp.close()
3327     except IOError, e:
3328         panic(str(e))
3329
3330
3331 def sys_set_debug_path():
3332     sysctl('portals/debug_path', config.debug_path)
3333
3334 def sys_set_lustre_upcall(upcall):
3335     # the command overrides the value in the node config
3336     if config.lustre_upcall:
3337         upcall = config.lustre_upcall
3338     elif config.upcall:
3339         upcall = config.upcall
3340     if upcall:
3341         lctl.set_lustre_upcall(upcall)
3342
3343 def sys_set_portals_upcall(upcall):
3344     # the command overrides the value in the node config
3345     if config.portals_upcall:
3346         upcall = config.portals_upcall
3347     elif config.upcall:
3348         upcall = config.upcall
3349     if upcall:
3350         sysctl('portals/upcall', upcall)
3351
3352 def sys_set_timeout(timeout):
3353     # the command overrides the value in the node config
3354     if config.timeout and config.timeout > 0:
3355         timeout = config.timeout
3356     if timeout != None and timeout > 0:
3357         lctl.set_timeout(timeout)
3358
3359 def sys_tweak_socknal ():
3360     # reserve at least 8MB, or we run out of RAM in skb_alloc under read
3361     if sys_get_branch() == '2.6':
3362         fp = open('/proc/meminfo')
3363         lines = fp.readlines()
3364         fp.close()
3365         memtotal = 131072
3366         for l in lines:
3367             a = string.split(l)
3368             if a[0] == 'MemTotal:':
3369                 memtotal = a[1]
3370                 debug("memtotal" + memtotal)
3371         if int(memtotal) < 262144:
3372             minfree = int(memtotal) / 16
3373         else:
3374             minfree = 32768
3375         debug("+ minfree ", minfree)
3376         sysctl("vm/min_free_kbytes", minfree)
3377     if config.single_socket:
3378         sysctl("socknal/typed", 0)
3379
3380 def sys_optimize_elan ():
3381     procfiles = ["/proc/elan/config/eventint_punt_loops",
3382                  "/proc/qsnet/elan3/config/eventint_punt_loops",
3383                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
3384     for p in procfiles:
3385         if os.access(p, os.W_OK):
3386             run ("echo 1 > " + p)
3387
3388 def sys_set_ptldebug(ptldebug):
3389     if config.ptldebug:
3390         ptldebug = config.ptldebug
3391     if ptldebug:
3392         try:
3393             val = eval(ptldebug, ptldebug_names)
3394             val = "0x%x" % (val)
3395             sysctl('portals/debug', val)
3396         except NameError, e:
3397             panic(str(e))
3398
3399 def sys_set_subsystem(subsystem):
3400     if config.subsystem:
3401         subsystem = config.subsystem
3402     if subsystem:
3403         try:
3404             val = eval(subsystem, subsystem_names)
3405             val = "0x%x" % (val)
3406             sysctl('portals/subsystem_debug', val)
3407         except NameError, e:
3408             panic(str(e))
3409
3410 def sys_set_netmem_max(path, max):
3411     debug("setting", path, "to at least", max)
3412     if config.noexec:
3413         return
3414     fp = open(path)
3415     str = fp.readline()
3416     fp.close()
3417     cur = int(str)
3418     if max > cur:
3419         fp = open(path, 'w')
3420         fp.write('%d\n' %(max))
3421         fp.close()
3422     
3423     
3424 def sys_make_devices():
3425     if not os.access('/dev/portals', os.R_OK):
3426         run('mknod /dev/portals c 10 240')
3427     if not os.access('/dev/obd', os.R_OK):
3428         run('mknod /dev/obd c 10 241')
3429
3430
3431 # Add dir to the global PATH, if not already there.
3432 def add_to_path(new_dir):
3433     syspath = string.split(os.environ['PATH'], ':')
3434     if new_dir in syspath:
3435         return
3436     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
3437     
3438 def default_debug_path():
3439     path = '/tmp/lustre-log'
3440     if os.path.isdir('/r'):
3441         return '/r' + path
3442     else:
3443         return path
3444
3445 def default_gdb_script():
3446     script = '/tmp/ogdb'
3447     if os.path.isdir('/r'):
3448         return '/r' + script
3449     else:
3450         return script
3451
3452
3453 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
3454 # ensure basic elements are in the system path
3455 def sanitise_path():
3456     for dir in DEFAULT_PATH:
3457         add_to_path(dir)
3458
3459 # global hack for the --select handling
3460 tgt_select = {}
3461 def init_select(args):
3462     # args = [service=nodeA,service2=nodeB service3=nodeC]
3463     global tgt_select
3464     for arg in args:
3465         list = string.split(arg, ',')
3466         for entry in list:
3467             srv, node = string.split(entry, '=')
3468             tgt_select[srv] = node
3469
3470 def get_select(srv):
3471     if tgt_select.has_key(srv):
3472         return tgt_select[srv]
3473     return None
3474
3475
3476 FLAG = Lustre.Options.FLAG
3477 PARAM = Lustre.Options.PARAM
3478 INTPARAM = Lustre.Options.INTPARAM
3479 PARAMLIST = Lustre.Options.PARAMLIST
3480 lconf_options = [
3481     ('verbose,v', "Print system commands as they are run"),
3482     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
3483     ('config', "Cluster config name used for LDAP query", PARAM),
3484     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
3485     ('node',   "Load config for <nodename>", PARAM),
3486     ('cleanup,d', "Cleans up config. (Shutdown)"),
3487     ('force,f', "Forced unmounting and/or obd detach during cleanup",
3488                FLAG, 0),
3489     ('single_socket', "socknal option: only use one socket instead of bundle",
3490                FLAG, 0),
3491     ('failover',"""Used to shut down without saving state.
3492                    This will allow this node to "give up" a service to a
3493                    another node for failover purposes. This will not
3494                    be a clean shutdown.""",
3495                FLAG, 0),
3496     ('gdb', """Prints message after creating gdb module script
3497                     and sleeps for 5 seconds."""),
3498     ('noexec,n', """Prints the commands and steps that will be run for a
3499                     config without executing them. This can used to check if a
3500                     config file is doing what it should be doing"""),
3501     ('nomod', "Skip load/unload module step."),
3502     ('nosetup', "Skip device setup/cleanup step."),
3503     ('reformat', "Reformat all devices (without question)"),
3504     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
3505     ('mountfsoptions', "Additional options for mount fs command line", PARAM),
3506     ('clientoptions', "Additional options for Lustre", PARAM),
3507     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
3508                PARAM),
3509     ('write_conf', "Save all the client config information on mds."),
3510     ('record', "Write config information on mds."),
3511     ('record_log', "Name of config record log.", PARAM),
3512     ('record_device', "MDS device name that will record the config commands",
3513               PARAM),
3514     ('root_squash', "MDS squash root to appointed uid",
3515               PARAM),
3516     ('no_root_squash', "Don't squash root for appointed nid",
3517               PARAM),
3518     ('minlevel', "Minimum level of services to configure/cleanup",
3519                  INTPARAM, 0),
3520     ('maxlevel', """Maximum level of services to configure/cleanup 
3521                     Levels are aproximatly like:
3522                             10 - netwrk
3523                             20 - device, ldlm
3524                             30 - osd, mdd
3525                             40 - mds, ost
3526                             70 - mountpoint, echo_client, osc, mdc, lov""",
3527                INTPARAM, 100),
3528     ('lustre', """Base directory of lustre sources. This parameter will
3529                   cause lconf to load modules from a source tree.""", PARAM),
3530     ('portals', """Portals source directory.  If this is a relative path,
3531                    then it is assumed to be relative to lustre. """, PARAM),
3532     ('timeout', "Set recovery timeout", INTPARAM),
3533     ('upcall',  "Set both portals and lustre upcall script", PARAM),
3534     ('lustre_upcall', "Set lustre upcall script", PARAM),
3535     ('portals_upcall', "Set portals upcall script", PARAM),
3536     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
3537     ('ptldebug', "Set the portals debug level",  PARAM),
3538     ('subsystem', "Set the portals debug subsystem",  PARAM),
3539     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
3540     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
3541 # Client recovery options
3542     ('recover', "Recover a device"),
3543     ('group', "The group of devices to configure or cleanup", PARAM),
3544     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
3545     ('client_uuid', "The failed client (required for recovery)", PARAM),
3546     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
3547
3548     ('inactive', """The name of an inactive service, to be ignored during
3549                     mounting (currently OST-only). Can be repeated.""",
3550                 PARAMLIST),
3551     ]      
3552
3553 def main():
3554     global lctl, config, toplustreDB, CONFIG_FILE, mod_manager
3555
3556     # in the upcall this is set to SIG_IGN
3557     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
3558     
3559     cl = Lustre.Options("lconf", "config.xml", lconf_options)
3560     try:
3561         config, args = cl.parse(sys.argv[1:])
3562     except Lustre.OptionError, e:
3563         print e
3564         sys.exit(1)
3565
3566     setupModulePath(sys.argv[0])
3567
3568     host = socket.gethostname()
3569
3570     # the PRNG is normally seeded with time(), which is not so good for starting
3571     # time-synchronized clusters
3572     input = open('/dev/urandom', 'r')
3573     if not input:
3574         print 'Unable to open /dev/urandom!'
3575         sys.exit(1)
3576     seed = input.read(32)
3577     input.close()
3578     random.seed(seed)
3579
3580     sanitise_path()
3581     
3582     init_select(config.select)
3583
3584     if len(args) > 0:
3585         # allow config to be fetched via HTTP, but only with python2
3586         if sys.version[0] != '1' and args[0].startswith('http://'):
3587             import urllib2
3588             try:
3589                 config_file = urllib2.urlopen(args[0])
3590             except (urllib2.URLError, socket.error), err:
3591                 if hasattr(err, 'args'):
3592                     err = err.args[1]
3593                 print "Could not access '%s': %s" %(args[0], err)
3594                 sys.exit(1)
3595         elif not os.access(args[0], os.R_OK):
3596             print 'File not found or readable:', args[0]
3597             sys.exit(1)
3598         else:
3599             # regular file
3600             config_file = open(args[0], 'r')
3601         try:
3602             dom = xml.dom.minidom.parse(config_file)
3603         except Exception:
3604             panic("%s does not appear to be a config file." % (args[0]))
3605             sys.exit(1) # make sure to die here, even in debug mode.
3606         config_file.close()
3607         CONFIG_FILE = args[0]
3608         lustreDB = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
3609         if not config.config:
3610             config.config = os.path.basename(args[0])# use full path?
3611             if config.config[-4:] == '.xml':
3612                 config.config = config.config[:-4]
3613     elif config.ldapurl:
3614         if not config.config:
3615             panic("--ldapurl requires --config name")
3616         dn = "config=%s,fs=lustre" % (config.config)
3617         lustreDB = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
3618     elif config.ptldebug or config.subsystem:
3619         sys_set_ptldebug(None)
3620         sys_set_subsystem(None)
3621         sys.exit(0)
3622     else:
3623         print 'Missing config file or ldap URL.'
3624         print 'see lconf --help for command summary'
3625         sys.exit(1)
3626
3627     toplustreDB = lustreDB
3628
3629     ver = lustreDB.get_version()
3630     if not ver:
3631         panic("No version found in config data, please recreate.")
3632     if ver != Lustre.CONFIG_VERSION:
3633         panic("Config version", ver, "does not match lconf version",
3634               Lustre.CONFIG_VERSION)
3635
3636     node_list = []
3637     if config.node:
3638         node_list.append(config.node)
3639     else:
3640         if len(host) > 0:
3641             node_list.append(host)
3642         node_list.append('localhost')
3643
3644     debug("configuring for host: ", node_list)
3645
3646     if len(host) > 0:
3647         config.debug_path = config.debug_path + '-' + host
3648         config.gdb_script = config.gdb_script + '-' + host
3649
3650     lctl = LCTLInterface('lctl')
3651
3652     if config.lctl_dump:
3653         lctl.use_save_file(config.lctl_dump)
3654
3655     if config.record:
3656         if not (config.record_device and config.record_log):
3657             panic("When recording, both --record_log and --record_device must be specified.")
3658         lctl.clear_log(config.record_device, config.record_log)
3659         lctl.record(config.record_device, config.record_log)
3660
3661     # init module manager
3662     mod_manager = kmod_manager(config.lustre, config.portals)
3663
3664     doHost(lustreDB, node_list)
3665
3666     if not config.record:
3667         return
3668
3669     lctl.end_record()
3670
3671     process_updates(lustreDB, config.record_device, config.record_log)
3672
3673 if __name__ == "__main__":
3674     try:
3675         main()
3676     except Lustre.LconfError, e:
3677         print e
3678 #        traceback.print_exc(file=sys.stdout)
3679         sys.exit(1)
3680     except CommandError, e:
3681         e.dump()
3682         sys.exit(e.rc)
3683
3684     if first_cleanup_error:
3685         sys.exit(first_cleanup_error)