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