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