Whamcloud - gitweb
b=2618
[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 == 0:
727             if devsize == 0:
728                 if not is_block(dev):
729                     ret, out = runcmd("ls -l %s" %dev)
730                     devsize = int(string.split(out[0])[4]) / 1024
731                 else:
732                     ret, out = runcmd("sfdisk -s %s" %dev)
733                     devsize = int(out[0])
734             if devsize > 1024 * 1024:
735                 jsize = ((devsize / 102400) * 4)
736             if jsize > 400:
737                 jsize = 400        
738         if jsize:  jopt = "-J size=%d" %(jsize,)
739         if isize:  iopt = "-I %d" %(isize,)
740         mkfs = 'mkfs.ext2 -j -b 4096 '
741         if not isblock or config.force:
742             mkfs = mkfs + ' -F '
743     elif fstype == 'reiserfs':
744         # reiserfs journal size is in blocks
745         if jsize:  jopt = "--journal_size %d" %(jsize,)
746         mkfs = 'mkreiserfs -ff'
747     else:
748         panic('unsupported fs type: ', fstype)
749
750     if config.mkfsoptions != None:
751         mkfs = mkfs + ' ' + config.mkfsoptions
752     if mkfsoptions != None:
753         mkfs = mkfs + ' ' + mkfsoptions
754     (ret, out) = run (mkfs, jopt, iopt, dev, block_cnt)
755     if ret:
756         panic("Unable to build fs:", dev, string.join(out))
757     # enable hash tree indexing on fsswe
758     if fstype in ('ext3', 'extN'):
759         htree = 'echo "feature FEATURE_C5" | debugfs -w'
760         (ret, out) = run (htree, dev)
761         if ret:
762             panic("Unable to enable htree:", dev)
763
764 # some systems use /dev/loopN, some /dev/loop/N
765 def loop_base():
766     import re
767     loop = '/dev/loop'
768     if not os.access(loop + str(0), os.R_OK):
769         loop = loop + '/'
770         if not os.access(loop + str(0), os.R_OK):
771             panic ("can't access loop devices")
772     return loop
773     
774 # find loop device assigned to thefile
775 def find_loop(file):
776     loop = loop_base()
777     for n in xrange(0, MAX_LOOP_DEVICES):
778         dev = loop + str(n)
779         if os.access(dev, os.R_OK):
780             (stat, out) = run('losetup', dev)
781             if out and stat == 0:
782                 m = re.search(r'\((.*)\)', out[0])
783                 if m and file == m.group(1):
784                     return dev
785         else:
786             break
787     return ''
788
789 # create file if necessary and assign the first free loop device
790 def init_loop(file, size, fstype, journal_size, inode_size, mkfsoptions, reformat):
791     dev = find_loop(file)
792     if dev:
793         print 'WARNING file:', file, 'already mapped to', dev
794         return dev
795     if reformat or not os.access(file, os.R_OK | os.W_OK):
796         if size < 8000:
797             panic("size of loopback file '%s' must be larger than 8MB, but is set to %s" % (file,size))
798         (ret, out) = run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,
799                                                                          file))
800         if ret:
801             panic("Unable to create backing store:", file)
802         mkfs(file, size, fstype, journal_size, inode_size, mkfsoptions, isblock=0)
803
804     loop = loop_base()
805     # find next free loop
806     for n in xrange(0, MAX_LOOP_DEVICES):
807         dev = loop + str(n)
808         if os.access(dev, os.R_OK):
809             (stat, out) = run('losetup', dev)
810             if stat:
811                 run('losetup', dev, file)
812                 return dev
813         else:
814             print "out of loop devices"
815             return ''
816     print "out of loop devices"
817     return ''
818
819 # undo loop assignment
820 def clean_loop(file):
821     dev = find_loop(file)
822     if dev:
823         ret, out = run('losetup -d', dev)
824         if ret:
825             log('unable to clean loop device:', dev, 'for file:', file)
826             logall(out)
827
828 # determine if dev is formatted as a <fstype> filesystem
829 def need_format(fstype, dev):
830     # FIXME don't know how to implement this    
831     return 0
832
833 # initialize a block device if needed
834 def block_dev(dev, size, fstype, reformat, autoformat, journal_size,
835               inode_size, mkfsoptions):
836     if config.noexec: return dev
837     if not is_block(dev):
838         dev = init_loop(dev, size, fstype, journal_size, inode_size,
839                         mkfsoptions, reformat)
840     elif reformat or (need_format(fstype, dev) and autoformat == 'yes'):
841         mkfs(dev, size, fstype, journal_size, inode_size, mkfsoptions,
842              isblock=0)
843 #    else:
844 #        panic("device:", dev,
845 #              "not prepared, and autoformat is not set.\n",
846 #              "Rerun with --reformat option to format ALL filesystems")
847         
848     return dev
849
850 def if2addr(iface):
851     """lookup IP address for an interface"""
852     rc, out = run("/sbin/ifconfig", iface)
853     if rc or not out:
854        return None
855     addr = string.split(out[1])[1]
856     ip = string.split(addr, ':')[1]
857     return ip
858
859 def sys_get_elan_position_file():
860     procfiles = ["/proc/elan/device0/position",
861                  "/proc/qsnet/elan4/device0/position",
862                  "/proc/qsnet/elan3/device0/position"]
863     for p in procfiles:
864         if os.access(p, os.R_OK):
865             return p
866     return ""
867
868 def sys_get_local_nid(net_type, wildcard, cluster_id):
869     """Return the local nid."""
870     local = ""
871     if sys_get_elan_position_file():
872         local = sys_get_local_address('elan', '*', cluster_id)
873     else:
874         local = sys_get_local_address(net_type, wildcard, cluster_id)
875     return local
876         
877 def sys_get_local_address(net_type, wildcard, cluster_id):
878     """Return the local address for the network type."""
879     local = ""
880     if net_type in ('tcp',):
881         if  ':' in wildcard:
882             iface, star = string.split(wildcard, ':')
883             local = if2addr(iface)
884             if not local:
885                 panic ("unable to determine ip for:", wildcard)
886         else:
887             host = socket.gethostname()
888             local = socket.gethostbyname(host)
889     elif net_type == 'elan':
890         # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()'
891         f = sys_get_elan_position_file()
892         if not f:
893             panic ("unable to determine local Elan ID")
894         try:
895             fp = open(f, 'r')
896             lines = fp.readlines()
897             fp.close()
898             for l in lines:
899                 a = string.split(l)
900                 if a[0] == 'NodeId':
901                     elan_id = a[1]
902                     break
903             try:
904                 nid = my_int(cluster_id) + my_int(elan_id) 
905                 local = "%d" % (nid)
906             except ValueError, e:
907                 local = elan_id
908         except IOError, e:
909             log(e)
910     elif net_type == 'gm':
911         fixme("automatic local address for GM")
912     elif net_type == 'scimac':
913         scinode="/opt/scali/sbin/scinode"
914         if os.path.exists(scinode):
915             (rc,local) = run(scinode)
916         else:
917             panic (scinode, " not found on node with scimac networking")
918         if rc:
919             panic (scinode, " failed")
920         local=string.rstrip(local[0])
921
922     return local
923
924 def mod_loaded(modname):
925     """Check if a module is already loaded. Look in /proc/modules for it."""
926     try:
927         fp = open('/proc/modules')
928         lines = fp.readlines()
929         fp.close()
930         # please forgive my tired fingers for this one
931         ret = filter(lambda word, mod=modname: word == mod,
932                      map(lambda line: string.split(line)[0], lines))
933         return ret
934     except Exception, e:
935         return 0
936
937 # XXX: instead of device_list, ask for $name and see what we get
938 def is_prepared(name):
939     """Return true if a device exists for the name"""
940     if config.lctl_dump:
941         return 0
942     if (config.noexec or config.record) and config.cleanup:
943         return 1
944     try:
945         # expect this format:
946         # 1 UP ldlm ldlm ldlm_UUID 2
947         out = lctl.device_list()
948         for s in out:
949             if name == string.split(s)[3]:
950                 return 1
951     except CommandError, e:
952         e.dump()
953     return 0
954
955 def is_network_prepared():
956     """If the any device exists, then assume that all networking
957        has been configured"""
958     out = lctl.device_list()
959     return len(out) > 0
960
961 def fs_is_mounted(path):
962     """Return true if path is a mounted lustre filesystem"""
963     try:
964         fp = open('/proc/mounts')
965         lines = fp.readlines()
966         fp.close()
967         for l in lines:
968             a = string.split(l)
969             if a[1] == path and a[2] == 'lustre_lite':
970                 return 1
971     except IOError, e:
972         log(e)
973     return 0
974         
975
976 class kmod:
977     """Manage kernel modules"""
978     def __init__(self, lustre_dir, portals_dir):
979         self.lustre_dir = lustre_dir
980         self.portals_dir = portals_dir
981         self.kmodule_list = []
982
983     def add_portals_module(self, dev_dir, modname):
984         """Append a module to list of modules to load."""
985         self.kmodule_list.append((self.portals_dir, dev_dir, modname))
986
987     def add_lustre_module(self, dev_dir, modname):
988         """Append a module to list of modules to load."""
989         self.kmodule_list.append((self.lustre_dir, dev_dir, modname))
990
991     def load_module(self):
992         """Load all the modules in the list in the order they appear."""
993         for src_dir, dev_dir, mod in self.kmodule_list:
994             if mod_loaded(mod) and not config.noexec:
995                 continue
996             log ('loading module:', mod, 'srcdir', src_dir, 'devdir', dev_dir)
997             if src_dir:
998                 module = find_module(src_dir, dev_dir,  mod)
999                 if not module:
1000                     panic('module not found:', mod)
1001                 (rc, out)  = run('/sbin/insmod', module)
1002                 if rc:
1003                     raise CommandError('insmod', out, rc)
1004             else:
1005                 (rc, out) = run('/sbin/modprobe', mod)
1006                 if rc:
1007                     raise CommandError('modprobe', out, rc)
1008
1009     def cleanup_module(self):
1010         """Unload the modules in the list in reverse order."""
1011         rev = self.kmodule_list
1012         rev.reverse()
1013         for src_dir, dev_dir, mod in rev:
1014             if not mod_loaded(mod) and not config.noexec:
1015                 continue
1016             # debug hack
1017             if mod == 'portals' and config.dump:
1018                 lctl.dump(config.dump)
1019             log('unloading module:', mod)
1020             (rc, out) = run('/sbin/rmmod', mod)
1021             if rc:
1022                 log('! unable to unload module:', mod)
1023                 logall(out)
1024
1025 # ============================================================
1026 # Classes to prepare and cleanup the various objects
1027 #
1028 class Module:
1029     """ Base class for the rest of the modules. The default cleanup method is
1030     defined here, as well as some utilitiy funcs.
1031     """
1032     def __init__(self, module_name, db):
1033         self.db = db
1034         self.module_name = module_name
1035         self.name = self.db.getName()
1036         self.uuid = self.db.getUUID()
1037         self._server = None
1038         self._connected = 0
1039         self.kmod = kmod(config.lustre, config.portals)
1040         
1041     def info(self, *args):
1042         msg = string.join(map(str,args))
1043         print self.module_name + ":", self.name, self.uuid, msg
1044
1045     def cleanup(self):
1046         """ default cleanup, used for most modules """
1047         self.info()
1048         try:
1049             lctl.cleanup(self.name, self.uuid, config.force)
1050         except CommandError, e:
1051             log(self.module_name, "cleanup failed: ", self.name)
1052             e.dump()
1053             cleanup_error(e.rc)
1054             
1055     def add_portals_module(self, dev_dir, modname):
1056         """Append a module to list of modules to load."""
1057         self.kmod.add_portals_module(dev_dir, modname)
1058
1059     def add_lustre_module(self, dev_dir, modname):
1060         """Append a module to list of modules to load."""
1061         self.kmod.add_lustre_module(dev_dir, modname)
1062
1063     def load_module(self):
1064         """Load all the modules in the list in the order they appear."""
1065         self.kmod.load_module()
1066             
1067     def cleanup_module(self):
1068         """Unload the modules in the list in reverse order."""
1069         if self.safe_to_clean():
1070             self.kmod.cleanup_module()
1071
1072     def safe_to_clean(self):
1073         return 1
1074         
1075     def safe_to_clean_modules(self):
1076         return self.safe_to_clean()
1077         
1078 class Network(Module):
1079     def __init__(self,db):
1080         Module.__init__(self, 'NETWORK', db)
1081         self.net_type = self.db.get_val('nettype')
1082         self.nid = self.db.get_val('nid', '*')
1083         self.cluster_id = self.db.get_val('clusterid', "0")
1084         self.port = self.db.get_val_int('port', 0)
1085         self.send_mem = self.db.get_val_int('sendmem', DEFAULT_TCPBUF)
1086         self.recv_mem = self.db.get_val_int('recvmem', DEFAULT_TCPBUF)
1087         self.irq_affinity = self.db.get_val_int('irqaffinity', 0)
1088
1089         if '*' in self.nid:
1090             self.nid = sys_get_local_nid(self.net_type, self.nid, self.cluster_id)
1091             if not self.nid:
1092                 panic("unable to set nid for", self.net_type, self.nid, cluster_id)
1093             self.generic_nid = 1
1094             debug("nid:", self.nid)
1095         else:
1096             self.generic_nid = 0
1097
1098         self.nid_uuid = self.nid_to_uuid(self.nid)
1099
1100         self.hostaddr = self.db.get_val('hostaddr', self.nid)
1101         if '*' in self.hostaddr:
1102             self.hostaddr = sys_get_local_address(self.net_type, self.hostaddr, self.cluster_id)
1103             if not self.hostaddr:
1104                 panic("unable to set hostaddr for", self.net_type, self.hostaddr, self.cluster_id)
1105             debug("hostaddr:", self.hostaddr)
1106
1107         self.add_portals_module("libcfs", 'portals')
1108         if node_needs_router():
1109             self.add_portals_module("router", 'kptlrouter')
1110         if self.net_type == 'tcp':
1111             self.add_portals_module("knals/socknal", 'ksocknal')
1112         if self.net_type == 'elan':
1113             self.add_portals_module("knals/qswnal", 'kqswnal')
1114         if self.net_type == 'gm':
1115             self.add_portals_module("knals/gmnal", 'kgmnal')
1116         if self.net_type == 'scimac':
1117             self.add_portals_module("knals/scimacnal", 'kscimacnal')
1118
1119     def nid_to_uuid(self, nid):
1120         return "NID_%s_UUID" %(nid,)
1121
1122     def prepare(self):
1123         if is_network_prepared():
1124             return
1125         self.info(self.net_type, self.nid, self.port)
1126         if not (config.record and self.generic_nid):
1127             lctl.network(self.net_type, self.nid)
1128         if self.net_type == 'tcp':
1129             sys_tweak_socknal()
1130         if self.net_type == 'elan':
1131             sys_optimize_elan()
1132         if self.port and  node_is_router():
1133             run_one_acceptor(self.port)
1134             self.connect_peer_gateways()
1135
1136     def connect_peer_gateways(self):
1137         for router in self.db.lookup_class('node'):
1138             if router.get_val_int('router', 0):
1139                 for netuuid in router.get_networks():
1140                     net = self.db.lookup(netuuid)
1141                     gw = Network(net)
1142                     if (gw.cluster_id == self.cluster_id and
1143                         gw.net_type == self.net_type):
1144                         if gw.nid != self.nid:
1145                             lctl.connect(gw)
1146
1147     def disconnect_peer_gateways(self):
1148         for router in self.db.lookup_class('node'):
1149             if router.get_val_int('router', 0):
1150                 for netuuid in router.get_networks():
1151                     net = self.db.lookup(netuuid)
1152                     gw = Network(net)
1153                     if (gw.cluster_id == self.cluster_id and
1154                         gw.net_type == self.net_type):
1155                         if gw.nid != self.nid:
1156                             try:
1157                                 lctl.disconnect(gw)
1158                             except CommandError, e:
1159                                 print "disconnect failed: ", self.name
1160                                 e.dump()
1161                                 cleanup_error(e.rc)
1162
1163     def safe_to_clean(self):
1164         return not is_network_prepared()
1165
1166     def cleanup(self):
1167         self.info(self.net_type, self.nid, self.port)
1168         if self.port:
1169             stop_acceptor(self.port)
1170         if  node_is_router():
1171             self.disconnect_peer_gateways()
1172
1173 class RouteTable(Module):
1174     def __init__(self,db):
1175         Module.__init__(self, 'ROUTES', db)
1176
1177     def server_for_route(self, net_type, gw, gw_cluster_id, tgt_cluster_id,
1178                          lo, hi):
1179         # only setup connections for tcp NALs
1180         srvdb = None
1181         if not net_type in ('tcp',):
1182             return None
1183
1184         # connect to target if route is to single node and this node is the gw
1185         if lo == hi and local_interface(net_type, gw_cluster_id, gw):
1186             if not local_cluster(net_type, tgt_cluster_id):
1187                 panic("target", lo, " not on the local cluster")
1188             srvdb = self.db.nid2server(lo, net_type, gw_cluster_id)
1189         # connect to gateway if this node is not the gw
1190         elif (local_cluster(net_type, gw_cluster_id)
1191               and not local_interface(net_type, gw_cluster_id, gw)):
1192             srvdb = self.db.nid2server(gw, net_type, gw_cluster_id)
1193         else:
1194             return None
1195
1196         if not srvdb:
1197             panic("no server for nid", lo)
1198             return None
1199
1200         return Network(srvdb)
1201         
1202     def prepare(self):
1203         if is_network_prepared():
1204             return
1205         self.info()
1206         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1207             lctl.add_route(net_type, gw, lo, hi)
1208             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1209             if srv:
1210                 lctl.connect(srv)
1211
1212     def safe_to_clean(self):
1213         return not is_network_prepared()
1214
1215     def cleanup(self):
1216         if is_network_prepared():
1217             # the network is still being used, don't clean it up
1218             return
1219         for net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi in self.db.get_route_tbl():
1220             srv = self.server_for_route(net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)
1221             if srv:
1222                 try:
1223                     lctl.disconnect(srv)
1224                 except CommandError, e:
1225                     print "disconnect failed: ", self.name
1226                     e.dump()
1227                     cleanup_error(e.rc)
1228
1229             try:
1230                 lctl.del_route(net_type, gw, lo, hi)
1231             except CommandError, e:
1232                 print "del_route failed: ", self.name
1233                 e.dump()
1234                 cleanup_error(e.rc)
1235
1236 class Management(Module):
1237     def __init__(self, db):
1238         Module.__init__(self, 'MGMT', db)
1239         self.add_lustre_module('lvfs', 'lvfs')
1240         self.add_lustre_module('obdclass', 'obdclass')
1241         self.add_lustre_module('ptlrpc', 'ptlrpc')
1242         self.add_lustre_module('mgmt', 'mgmt_svc')
1243
1244     def prepare(self):
1245         if is_prepared(self.name):
1246             return
1247         self.info()
1248         lctl.newdev("mgmt", self.name, self.uuid)
1249
1250     def safe_to_clean(self):
1251         return 1
1252
1253     def cleanup(self):
1254         if is_prepared(self.name):
1255             Module.cleanup(self)
1256
1257 # This is only needed to load the modules; the LDLM device
1258 # is now created automatically.
1259 class LDLM(Module):
1260     def __init__(self,db):
1261         Module.__init__(self, 'LDLM', db)
1262         self.add_lustre_module('lvfs', 'lvfs')
1263         self.add_lustre_module('obdclass', 'obdclass')
1264         self.add_lustre_module('ptlrpc', 'ptlrpc')
1265
1266     def prepare(self):
1267         return
1268
1269     def cleanup(self):
1270         return
1271
1272 class LOV(Module):
1273     def __init__(self, db, uuid, fs_name, name_override = None, config_only = None):
1274         Module.__init__(self, 'LOV', db)
1275         if name_override != None:
1276             self.name = "lov_%s" % name_override
1277         self.add_lustre_module('lov', 'lov')
1278         self.mds_uuid = self.db.get_first_ref('mds')
1279         self.stripe_sz = self.db.get_val_int('stripesize', 65536)
1280         self.stripe_off = self.db.get_val_int('stripeoffset', 0)
1281         self.pattern = self.db.get_val_int('stripepattern', 0)
1282         self.devlist = self.db.get_refs('obd')
1283         self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist))
1284         self.osclist = []
1285         self.desc_uuid = self.uuid
1286         self.uuid = generate_client_uuid(self.name)
1287         self.fs_name = fs_name
1288         if config_only:
1289             self.config_only = 1
1290             return
1291         self.config_only = None
1292         mds= self.db.lookup(self.mds_uuid)
1293         self.mds_name = mds.getName()
1294         for obd_uuid in self.devlist:
1295             obd = self.db.lookup(obd_uuid)
1296             osc = get_osc(obd, self.uuid, fs_name)
1297             if osc:
1298                 self.osclist.append(osc)
1299             else:
1300                 panic('osc not found:', obd_uuid)
1301             
1302     def prepare(self):
1303         if is_prepared(self.name):
1304             return
1305         if self.config_only:
1306             panic("Can't prepare config_only LOV ", self.name)
1307             
1308         for osc in self.osclist:
1309             try:
1310                 # Only ignore connect failures with --force, which
1311                 # isn't implemented here yet.
1312                 osc.prepare(ignore_connect_failure=0)
1313             except CommandError, e:
1314                 print "Error preparing OSC %s\n" % osc.uuid
1315                 raise e
1316         self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
1317                   self.stripe_off, self.pattern, self.devlist, self.mds_name)
1318         lctl.lov_setup(self.name, self.uuid,
1319                        self.desc_uuid, self.mds_name, self.stripe_cnt,
1320                        self.stripe_sz, self.stripe_off, self.pattern,
1321                        string.join(self.devlist))
1322
1323     def cleanup(self):
1324         if is_prepared(self.name):
1325             Module.cleanup(self)
1326         if self.config_only:
1327             panic("Can't clean up config_only LOV ", self.name)
1328         for osc in self.osclist:
1329             osc.cleanup()
1330
1331     def load_module(self):
1332         if self.config_only:
1333             panic("Can't load modules for config_only LOV ", self.name)
1334         for osc in self.osclist:
1335             osc.load_module()
1336             break
1337         Module.load_module(self)
1338
1339     def cleanup_module(self):
1340         if self.config_only:
1341             panic("Can't cleanup modules for config_only LOV ", self.name)
1342         Module.cleanup_module(self)
1343         for osc in self.osclist:
1344             osc.cleanup_module()
1345             break
1346
1347 class MDSDEV(Module):
1348     def __init__(self,db):
1349         Module.__init__(self, 'MDSDEV', db)
1350         self.devpath = self.db.get_val('devpath','')
1351         self.size = self.db.get_val_int('devsize', 0)
1352         self.journal_size = self.db.get_val_int('journalsize', 0)
1353         self.fstype = self.db.get_val('fstype', '')
1354         self.nspath = self.db.get_val('nspath', '')
1355         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1356         # overwrite the orignal MDSDEV name and uuid with the MDS name and uuid
1357         target_uuid = self.db.get_first_ref('target')
1358         mds = self.db.lookup(target_uuid)
1359         self.name = mds.getName()
1360         self.filesystem_uuids = mds.get_refs('filesystem')
1361         # FIXME: if fstype not set, then determine based on kernel version
1362         self.format = self.db.get_val('autoformat', "no")
1363         if mds.get_val('failover', 0):
1364             self.failover_mds = 'f'
1365         else:
1366             self.failover_mds = 'n'
1367         active_uuid = get_active_target(mds)
1368         if not active_uuid:
1369             panic("No target device found:", target_uuid)
1370         if active_uuid == self.uuid:
1371             self.active = 1
1372         else:
1373             self.active = 0
1374         if self.active and config.group and config.group != ost.get_val('group'):
1375             self.active = 0
1376
1377         self.inode_size = self.db.get_val_int('inodesize', 0)
1378         if self.inode_size == 0:
1379             # find the LOV for this MDS
1380             lovconfig_uuid = mds.get_first_ref('lovconfig')
1381             if not lovconfig_uuid:
1382                 panic("No LOV config found for MDS ", mds.name)
1383             lovconfig = mds.lookup(lovconfig_uuid)
1384             lov_uuid = lovconfig.get_first_ref('lov')
1385             if not lov_uuid:
1386                 panic("No LOV found for lovconfig ", lovconfig.name)
1387             lov = LOV(self.db.lookup(lov_uuid), lov_uuid, 'FS_name', config_only = 1)
1388
1389             # default stripe count controls default inode_size
1390             stripe_count = lov.stripe_cnt
1391             if stripe_count > 77:
1392                 self.inode_size = 4096
1393             elif stripe_count > 35:
1394                 self.inode_size = 2048
1395             elif stripe_count > 13:
1396                 self.inode_size = 1024
1397             elif stripe_count > 3:
1398                 self.inode_size = 512
1399             else:
1400                 self.inode_size = 256
1401
1402         self.target_dev_uuid = self.uuid
1403         self.uuid = target_uuid
1404         # modules
1405         self.add_lustre_module('mdc', 'mdc')
1406         self.add_lustre_module('osc', 'osc')
1407         self.add_lustre_module('lov', 'lov')
1408         self.add_lustre_module('mds', 'mds')
1409         if self.fstype:
1410             self.add_lustre_module('lvfs', 'fsfilt_%s' % (self.fstype))
1411
1412     def load_module(self):
1413         if self.active:
1414             Module.load_module(self)
1415             
1416     def prepare(self):
1417         if is_prepared(self.name):
1418             return
1419         if not self.active:
1420             debug(self.uuid, "not active")
1421             return
1422         if config.reformat:
1423             # run write_conf automatically, if --reformat used
1424             self.write_conf()
1425         self.info(self.devpath, self.fstype, self.size, self.format)
1426         run_acceptors()
1427         # never reformat here
1428         blkdev = block_dev(self.devpath, self.size, self.fstype, 0,
1429                            self.format, self.journal_size, self.inode_size,
1430                            self.mkfsoptions)
1431         if not is_prepared('MDT'):
1432             lctl.newdev("mdt", 'MDT', 'MDT_UUID', setup ="")
1433         try: 
1434             lctl.newdev("mds", self.name, self.uuid,
1435                         setup ="%s %s %s" %(blkdev, self.fstype, self.name))
1436         except CommandError, e:
1437             if e.rc == 2:
1438                 panic("MDS is missing the config log. Need to run " +
1439                        "lconf --write_conf.")
1440             else:
1441                 raise e
1442
1443     def write_conf(self):
1444         if is_prepared(self.name):
1445             return
1446         self.info(self.devpath, self.fstype, self.format)
1447         blkdev = block_dev(self.devpath, self.size, self.fstype,
1448                            config.reformat, self.format, self.journal_size,
1449                            self.inode_size, self.mkfsoptions)
1450         lctl.newdev("mds", self.name, self.uuid,
1451                     setup ="%s %s" %(blkdev, self.fstype))
1452                 
1453         # record logs for the MDS lov
1454         for uuid in self.filesystem_uuids:
1455             log("recording clients for filesystem:", uuid)
1456             fs = self.db.lookup(uuid)
1457             obd_uuid = fs.get_first_ref('obd')
1458             client_uuid = generate_client_uuid(self.name)
1459             client = VOSC(self.db.lookup(obd_uuid), client_uuid, self.name,
1460                           self.name)
1461             config.record = 1
1462             lctl.record(self.name, self.name)
1463             client.prepare()
1464             lctl.mount_option(self.name, client.get_name(), "")
1465             lctl.end_record()
1466
1467             config.cleanup = 1
1468             lctl.record(self.name, self.name + '-clean')
1469             client.cleanup()
1470             lctl.del_mount_option(self.name)
1471             lctl.end_record()
1472             config.cleanup = 0
1473             config.record = 0
1474
1475         # record logs for each client
1476         if config.ldapurl:
1477             config_options = "--ldapurl " + config.ldapurl + " --config " + config.config
1478         else:
1479             config_options = CONFIG_FILE
1480
1481         for node_db in self.db.lookup_class('node'):
1482             client_name = node_db.getName()
1483             for prof_uuid in node_db.get_refs('profile'):
1484                 prof_db = node_db.lookup(prof_uuid)
1485                 # refactor this into a funtion to test "clientness"
1486                 # of a node.
1487                 for ref_class, ref_uuid in prof_db.get_all_refs():
1488                     if ref_class in ('mountpoint','echoclient'):
1489                         debug("recording", client_name)
1490                         old_noexec = config.noexec
1491                         config.noexec = 0
1492                         noexec_opt = ('', '-n')
1493                         ret, out = run (sys.argv[0],
1494                                         noexec_opt[old_noexec == 1],
1495                                         " -v --record --nomod",
1496                                         "--record_log", client_name,
1497                                         "--record_device", self.name,
1498                                         "--node", client_name,
1499                                         config_options)
1500                         if config.verbose:
1501                             for s in out: log("record> ", string.strip(s))
1502                         ret, out = run (sys.argv[0],
1503                                         noexec_opt[old_noexec == 1],
1504                                         "--cleanup -v --record --nomod",
1505                                         "--record_log", client_name + "-clean",
1506                                         "--record_device", self.name,
1507                                         "--node", client_name,
1508                                         config_options)
1509                         if config.verbose:
1510                             for s in out: log("record> ", string.strip(s))
1511                         config.noexec = old_noexec
1512         try:
1513             lctl.cleanup(self.name, self.uuid, 0, 0)
1514         except CommandError, e:
1515             log(self.module_name, "cleanup failed: ", self.name)
1516             e.dump()
1517             cleanup_error(e.rc)
1518             Module.cleanup(self)
1519         clean_loop(self.devpath)
1520  
1521     def msd_remaining(self):
1522         out = lctl.device_list()
1523         for s in out:
1524             if string.split(s)[2] in ('mds',):
1525                 return 1
1526
1527     def safe_to_clean(self):
1528         return self.active
1529
1530     def safe_to_clean_modules(self):
1531         return not self.msd_remaining()
1532
1533     def cleanup(self):
1534         if not self.active:
1535             debug(self.uuid, "not active")
1536             return
1537         self.info()
1538         if is_prepared(self.name):
1539             try:
1540                 lctl.cleanup(self.name, self.uuid, config.force,
1541                              config.failover)
1542             except CommandError, e:
1543                 log(self.module_name, "cleanup failed: ", self.name)
1544                 e.dump()
1545                 cleanup_error(e.rc)
1546                 Module.cleanup(self)
1547         if not self.msd_remaining() and is_prepared('MDT'):
1548             try:
1549                 lctl.cleanup("MDT", "MDT_UUID", config.force,
1550                              config.failover)
1551             except CommandError, e:
1552                 print "cleanup failed: ", self.name
1553                 e.dump()
1554                 cleanup_error(e.rc)
1555         clean_loop(self.devpath)
1556
1557 class OSD(Module):
1558     def __init__(self, db):
1559         Module.__init__(self, 'OSD', db)
1560         self.osdtype = self.db.get_val('osdtype')
1561         self.devpath = self.db.get_val('devpath', '')
1562         self.size = self.db.get_val_int('devsize', 0)
1563         self.journal_size = self.db.get_val_int('journalsize', 0)
1564         self.inode_size = self.db.get_val_int('inodesize', 0)
1565         self.mkfsoptions = self.db.get_val('mkfsoptions', '')
1566         self.fstype = self.db.get_val('fstype', '')
1567         self.nspath = self.db.get_val('nspath', '')
1568         target_uuid = self.db.get_first_ref('target')
1569         ost = self.db.lookup(target_uuid)
1570         self.name = ost.getName()
1571         self.format = self.db.get_val('autoformat', 'yes')
1572         if ost.get_val('failover', 0):
1573             self.failover_ost = 'f'
1574         else:
1575             self.failover_ost = 'n'
1576
1577         active_uuid = get_active_target(ost)
1578         if not active_uuid:
1579             panic("No target device found:", target_uuid)
1580         if active_uuid == self.uuid:
1581             self.active = 1
1582         else:
1583             self.active = 0
1584         if self.active and config.group and config.group != ost.get_val('group'):
1585             self.active = 0
1586             
1587         self.target_dev_uuid = self.uuid
1588         self.uuid = target_uuid
1589         # modules
1590         self.add_lustre_module('ost', 'ost')
1591         # FIXME: should we default to ext3 here?
1592         if self.fstype:
1593             self.add_lustre_module('lvfs' , 'fsfilt_%s' % (self.fstype))
1594         self.add_lustre_module(self.osdtype, self.osdtype)
1595
1596     def load_module(self):
1597         if self.active:
1598             Module.load_module(self)
1599
1600     # need to check /proc/mounts and /etc/mtab before
1601     # formatting anything.
1602     # FIXME: check if device is already formatted.
1603     def prepare(self):
1604         if is_prepared(self.name):
1605             return
1606         if not self.active:
1607             debug(self.uuid, "not active")
1608             return
1609         self.info(self.osdtype, self.devpath, self.size, self.fstype,
1610                   self.format, self.journal_size, self.inode_size)
1611         run_acceptors()
1612         if self.osdtype == 'obdecho':
1613             blkdev = ''
1614         else:
1615             blkdev = block_dev(self.devpath, self.size, self.fstype,
1616                                config.reformat, self.format, self.journal_size,
1617                                self.inode_size, self.mkfsoptions)
1618         lctl.newdev(self.osdtype, self.name, self.uuid,
1619                     setup ="%s %s %s" %(blkdev, self.fstype,
1620                                            self.failover_ost))
1621         if not is_prepared('OSS'):
1622             lctl.newdev("ost", 'OSS', 'OSS_UUID', setup ="")
1623
1624     def osd_remaining(self):
1625         out = lctl.device_list()
1626         for s in out:
1627             if string.split(s)[2] in ('obdfilter', 'obdecho'):
1628                 return 1
1629
1630     def safe_to_clean(self):
1631         return self.active
1632
1633     def safe_to_clean_modules(self):
1634         return not self.osd_remaining()
1635
1636     def cleanup(self):
1637         if not self.active:
1638             debug(self.uuid, "not active")
1639             return
1640         if is_prepared(self.name):
1641             self.info()
1642             try:
1643                 lctl.cleanup(self.name, self.uuid, config.force,
1644                              config.failover)
1645             except CommandError, e:
1646                 log(self.module_name, "cleanup failed: ", self.name)
1647                 e.dump()
1648                 cleanup_error(e.rc)
1649         if not self.osd_remaining() and is_prepared('OSS'):
1650             try:
1651                 lctl.cleanup("OSS", "OSS_UUID", config.force,
1652                              config.failover)
1653             except CommandError, e:
1654                 print "cleanup failed: ", self.name
1655                 e.dump()
1656                 cleanup_error(e.rc)
1657         if not self.osdtype == 'obdecho':
1658             clean_loop(self.devpath)
1659
1660 def mgmt_uuid_for_fs(mtpt_name):
1661     if not mtpt_name:
1662         return ''
1663     mtpt_db = toplevel.lookup_name(mtpt_name)
1664     fs_uuid = mtpt_db.get_first_ref('filesystem')
1665     fs = toplevel.lookup(fs_uuid)
1666     if not fs:
1667         return ''
1668     return fs.get_first_ref('mgmt')
1669
1670 # Generic client module, used by OSC and MDC
1671 class Client(Module):
1672     def __init__(self, tgtdb, uuid, module, fs_name, self_name=None,
1673                  module_dir=None):
1674         self.target_name = tgtdb.getName()
1675         self.target_uuid = tgtdb.getUUID()
1676         self.db = tgtdb
1677
1678         self.tgt_dev_uuid = get_active_target(tgtdb)
1679         if not self.tgt_dev_uuid:
1680             panic("No target device found for target:", self.target_name)
1681             
1682         self.kmod = kmod(config.lustre, config.portals)
1683         self._server = None
1684         self._connected = 0
1685
1686         self.module = module
1687         self.module_name = string.upper(module)
1688         if not self_name:
1689             self.name = '%s_%s_%s_%s' % (self.module_name, socket.gethostname(),
1690                                          self.target_name, fs_name)
1691         else:
1692             self.name = self_name
1693         self.uuid = uuid
1694         self.lookup_server(self.tgt_dev_uuid)
1695         mgmt_uuid = mgmt_uuid_for_fs(fs_name)
1696         if mgmt_uuid:
1697             self.mgmt_name = mgmtcli_name_for_uuid(mgmt_uuid)
1698         else:
1699             self.mgmt_name = ''
1700         self.fs_name = fs_name
1701         if not module_dir:
1702             module_dir = module
1703         self.add_lustre_module(module_dir, module)
1704
1705     def lookup_server(self, srv_uuid):
1706         """ Lookup a server's network information """
1707         self._server_nets = get_ost_net(self.db, srv_uuid)
1708         if len(self._server_nets) == 0:
1709             panic ("Unable to find a server for:", srv_uuid)
1710
1711     def get_servers(self):
1712         return self._server_nets
1713
1714     def prepare(self, ignore_connect_failure = 0):
1715         self.info(self.target_uuid)
1716         if is_prepared(self.name):
1717             self.cleanup()
1718         try:
1719             srv = choose_local_server(self.get_servers())
1720             if srv:
1721                 lctl.connect(srv)
1722             else:
1723                 routes = find_route(self.get_servers())
1724                 if len(routes) == 0:
1725                     panic ("no route to",  self.target_uuid)
1726                 for (srv, r) in routes:
1727                     lctl.add_route_host(r[0], srv.nid_uuid, r[1], r[3])
1728         except CommandError, e:
1729             if not ignore_connect_failure:
1730                 raise e
1731         if srv:
1732             if self.target_uuid in config.inactive and self.permits_inactive():
1733                 debug("%s inactive" % self.target_uuid)
1734                 inactive_p = "inactive"
1735             else:
1736                 debug("%s active" % self.target_uuid)
1737                 inactive_p = ""
1738             lctl.newdev(self.module, self.name, self.uuid,
1739                         setup ="%s %s %s %s" % (self.target_uuid, srv.nid_uuid,
1740                                                 inactive_p, self.mgmt_name))
1741
1742     def cleanup(self):
1743         if is_prepared(self.name):
1744             Module.cleanup(self)
1745             try:
1746                 srv = choose_local_server(self.get_servers())
1747                 if srv:
1748                     lctl.disconnect(srv)
1749                 else:
1750                     for (srv, r) in find_route(self.get_servers()):
1751                         lctl.del_route_host(r[0], srv.nid_uuid, r[1], r[3])
1752             except CommandError, e:
1753                 log(self.module_name, "cleanup failed: ", self.name)
1754                 e.dump()
1755                 cleanup_error(e.rc)
1756
1757
1758 class MDC(Client):
1759     def __init__(self, db, uuid, fs_name):
1760          Client.__init__(self, db, uuid, 'mdc', fs_name)
1761
1762     def permits_inactive(self):
1763         return 0
1764
1765 class OSC(Client):
1766     def __init__(self, db, uuid, fs_name):
1767          Client.__init__(self, db, uuid, 'osc', fs_name)
1768
1769     def permits_inactive(self):
1770         return 1
1771
1772 def mgmtcli_name_for_uuid(uuid):
1773     return 'MGMTCLI_%s' % uuid
1774
1775 class ManagementClient(Client):
1776     def __init__(self, db, uuid):
1777         Client.__init__(self, db, uuid, 'mgmt_cli', '',
1778                         self_name = mgmtcli_name_for_uuid(db.getUUID()),
1779                         module_dir = 'mgmt')
1780             
1781 class COBD(Module):
1782     def __init__(self, db):
1783         Module.__init__(self, 'COBD', db)
1784         self.real_uuid = self.db.get_first_ref('realobd')
1785         self.cache_uuid = self.db.get_first_ref('cacheobd')
1786         self.add_lustre_module('cobd' , 'cobd')
1787
1788     # need to check /proc/mounts and /etc/mtab before
1789     # formatting anything.
1790     # FIXME: check if device is already formatted.
1791     def prepare(self):
1792         if is_prepared(self.name):
1793             return
1794         self.info(self.real_uuid, self.cache_uuid)
1795         lctl.newdev("cobd", self.name, self.uuid,
1796                     setup ="%s %s" %(self.real_uuid, self.cache_uuid))
1797
1798
1799 # virtual interface for  OSC and LOV
1800 class VOSC(Module):
1801     def __init__(self, db, uuid, fs_name, name_override = None):
1802         Module.__init__(self, 'VOSC', db)
1803         if db.get_class() == 'lov':
1804             self.osc = LOV(db, uuid, fs_name, name_override)
1805         else:
1806             self.osc = get_osc(db, uuid, fs_name)
1807     def get_uuid(self):
1808         return self.osc.uuid
1809     def get_name(self):
1810         return self.osc.name
1811     def prepare(self):
1812         self.osc.prepare()
1813     def cleanup(self):
1814         self.osc.cleanup()
1815     def load_module(self):
1816         self.osc.load_module()
1817     def cleanup_module(self):
1818         self.osc.cleanup_module()
1819
1820
1821 class ECHO_CLIENT(Module):
1822     def __init__(self,db):
1823         Module.__init__(self, 'ECHO_CLIENT', db)
1824         self.add_lustre_module('obdecho', 'obdecho')
1825         self.obd_uuid = self.db.get_first_ref('obd')
1826         obd = self.db.lookup(self.obd_uuid)
1827         self.uuid = generate_client_uuid(self.name)
1828         self.osc = VOSC(obd, self.uuid, self.name)
1829
1830     def prepare(self):
1831         if is_prepared(self.name):
1832             return
1833         run_acceptors()
1834         self.osc.prepare() # XXX This is so cheating. -p
1835         self.info(self.obd_uuid)
1836
1837         lctl.newdev("echo_client", self.name, self.uuid,
1838                     setup = self.osc.get_name())
1839
1840     def cleanup(self):
1841         if is_prepared(self.name):
1842             Module.cleanup(self)
1843         self.osc.cleanup()
1844
1845     def load_module(self):
1846         self.osc.load_module()
1847         Module.load_module(self)
1848
1849     def cleanup_module(self):
1850         Module.cleanup_module(self)
1851         self.osc.cleanup_module()
1852
1853
1854 def generate_client_uuid(name):
1855         client_uuid = '%05x_%.19s_%05x%05x' % (int(random.random() * 1048576),
1856                                                name,
1857                                                int(random.random() * 1048576),
1858                                                int(random.random() * 1048576))
1859         return client_uuid[:36]
1860
1861
1862 class Mountpoint(Module):
1863     def __init__(self,db):
1864         Module.__init__(self, 'MTPT', db)
1865         self.path = self.db.get_val('path')
1866         self.fs_uuid = self.db.get_first_ref('filesystem')
1867         fs = self.db.lookup(self.fs_uuid)
1868         self.mds_uuid = fs.get_first_ref('mds')
1869         self.obd_uuid = fs.get_first_ref('obd')
1870         self.mgmt_uuid = fs.get_first_ref('mgmt')
1871         obd = self.db.lookup(self.obd_uuid)
1872         client_uuid = generate_client_uuid(self.name)
1873         self.vosc = VOSC(obd, client_uuid, self.name)
1874         self.mdc = get_mdc(db, client_uuid, self.name, self.mds_uuid)
1875
1876         self.add_lustre_module('mdc', 'mdc')
1877         self.add_lustre_module('llite', 'llite')
1878         if self.mgmt_uuid:
1879             self.mgmtcli = ManagementClient(db.lookup(self.mgmt_uuid),
1880                                             client_uuid)
1881         else:
1882             self.mgmtcli = None
1883
1884     def prepare(self):
1885         if fs_is_mounted(self.path):
1886             log(self.path, "already mounted.")
1887             return
1888         run_acceptors()
1889         if self.mgmtcli:
1890             self.mgmtcli.prepare()
1891         self.vosc.prepare()
1892         self.mdc.prepare()
1893         mdc_name = self.mdc.name
1894
1895         self.info(self.path, self.mds_uuid, self.obd_uuid)
1896         if config.record or config.lctl_dump:
1897             lctl.mount_option(local_node_name, self.vosc.get_name(), mdc_name)
1898             return
1899         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s %s %s" % \
1900               (self.vosc.get_name(), mdc_name, config.config, self.path)
1901         run("mkdir", self.path)
1902         ret, val = run(cmd)
1903         if ret:
1904             self.mdc.cleanup()            
1905             self.vosc.cleanup()
1906             panic("mount failed:", self.path, ":", string.join(val))
1907
1908     def cleanup(self):
1909         self.info(self.path, self.mds_uuid,self.obd_uuid)
1910
1911         if config.record or config.lctl_dump:
1912             lctl.del_mount_option(local_node_name)
1913         else:
1914             if fs_is_mounted(self.path):
1915                 if config.force:
1916                     (rc, out) = run("umount", "-f", self.path)
1917                 else:
1918                     (rc, out) = run("umount", self.path)
1919                 if rc:
1920                     raise CommandError('umount', out, rc)
1921
1922             if fs_is_mounted(self.path):
1923                 panic("fs is still mounted:", self.path)
1924
1925         self.mdc.cleanup()
1926         self.vosc.cleanup()
1927         if self.mgmtcli:
1928             self.mgmtcli.cleanup()
1929
1930     def load_module(self):
1931         if self.mgmtcli:
1932             self.mgmtcli.load_module()
1933         self.vosc.load_module()
1934         Module.load_module(self)
1935
1936     def cleanup_module(self):
1937         Module.cleanup_module(self)
1938         self.vosc.cleanup_module()
1939         if self.mgmtcli:
1940             self.mgmtcli.cleanup_module()
1941
1942
1943 # ============================================================
1944 # misc query functions
1945
1946 def get_ost_net(self, osd_uuid):
1947     srv_list = []
1948     if not osd_uuid:
1949         return srv_list
1950     osd = self.lookup(osd_uuid)
1951     node_uuid = osd.get_first_ref('node')
1952     node = self.lookup(node_uuid)
1953     if not node:
1954         panic("unable to find node for osd_uuid:", osd_uuid,
1955               " node_ref:", node_uuid)
1956     for net_uuid in node.get_networks():
1957         db = node.lookup(net_uuid)
1958         srv_list.append(Network(db))
1959     return srv_list
1960
1961
1962 # the order of iniitailization is based on level. 
1963 def getServiceLevel(self):
1964     type = self.get_class()
1965     ret=0;
1966     if type in ('network',):
1967         ret = 5
1968     elif type in ('routetbl',):
1969         ret = 6
1970     elif type in ('ldlm',):
1971         ret = 20
1972     elif type in ('mgmt',):
1973         ret = 25
1974     elif type in ('osd', 'cobd'):
1975         ret = 30
1976     elif type in ('mdsdev',):
1977         ret = 40
1978     elif type in ('mountpoint', 'echoclient'):
1979         ret = 70
1980     else:
1981         panic("Unknown type: ", type)
1982
1983     if ret < config.minlevel or ret > config.maxlevel:
1984         ret = 0 
1985     return ret
1986
1987 #
1988 # return list of services in a profile. list is a list of tuples
1989 # [(level, db_object),]
1990 def getServices(self):
1991     list = []
1992     for ref_class, ref_uuid in self.get_all_refs(): 
1993             servdb = self.lookup(ref_uuid)
1994             if  servdb:
1995                 level = getServiceLevel(servdb)
1996                 if level > 0:
1997                     list.append((level, servdb))
1998             else:
1999                 panic('service not found: ' + ref_uuid)
2000
2001     list.sort()
2002     return list
2003
2004
2005 ############################################################
2006 # MDC UUID hack - 
2007 # FIXME: clean this mess up!
2008 #
2009 # OSC is no longer in the xml, so we have to fake it.
2010 # this is getting ugly and begging for another refactoring
2011 def get_osc(ost_db, uuid, fs_name):
2012     osc = OSC(ost_db, uuid, fs_name)
2013     return osc
2014
2015 def get_mdc(db, uuid, fs_name, mds_uuid):
2016     mds_db = db.lookup(mds_uuid);
2017     if not mds_db:
2018         panic("no mds:", mds_uuid)
2019     mdc = MDC(mds_db, uuid, fs_name)
2020     return mdc
2021
2022 ############################################################
2023 # routing ("rooting")
2024
2025 # list of (nettype, cluster_id, nid)
2026 local_clusters = []
2027
2028 def find_local_clusters(node_db):
2029     global local_clusters
2030     for netuuid in node_db.get_networks():
2031         net = node_db.lookup(netuuid)
2032         srv = Network(net)
2033         debug("add_local", netuuid)
2034         local_clusters.append((srv.net_type, srv.cluster_id, srv.nid))
2035         if srv.port > 0:
2036             if acceptors.has_key(srv.port):
2037                 panic("duplicate port:", srv.port)
2038             acceptors[srv.port] = AcceptorHandler(srv.port, srv.net_type,
2039                                                   srv.send_mem, srv.recv_mem,
2040                                                   srv.irq_affinity)
2041
2042 # This node is a gateway.
2043 is_router = 0
2044 def node_is_router():
2045     return is_router
2046
2047 # If there are any routers found in the config, then this will be true
2048 # and all nodes will load kptlrouter.
2049 needs_router = 0
2050 def node_needs_router():
2051     return needs_router or is_router
2052
2053 # list of (nettype, gw, tgt_cluster_id, lo, hi)
2054 # Currently, these local routes are only added to kptlrouter route
2055 # table if they are needed to connect to a specific server.  This
2056 # should be changed so all available routes are loaded, and the
2057 # ptlrouter can make all the decisions.
2058 local_routes = []
2059
2060 def find_local_routes(lustre):
2061     """ Scan the lustre config looking for routers .  Build list of
2062     routes. """
2063     global local_routes, needs_router
2064     local_routes = []
2065     list = lustre.lookup_class('node')
2066     for router in list:
2067         if router.get_val_int('router', 0):
2068             needs_router = 1
2069             for (local_type, local_cluster_id, local_nid) in local_clusters:
2070                 gw = None
2071                 for netuuid in router.get_networks():
2072                     db = router.lookup(netuuid)
2073                     if (local_type == db.get_val('nettype') and
2074                        local_cluster_id == db.get_val('clusterid')):
2075                         gw = db.get_val('nid')
2076                         break
2077                 if gw:
2078                     debug("find_local_routes: gw is", gw)
2079                     for route in router.get_local_routes(local_type, gw):
2080                         local_routes.append(route)
2081     debug("find_local_routes:", local_routes)
2082
2083
2084 def choose_local_server(srv_list):
2085     for srv in srv_list:
2086         if local_cluster(srv.net_type, srv.cluster_id):
2087             return srv
2088
2089 def local_cluster(net_type, cluster_id):
2090     for cluster in local_clusters:
2091         if net_type == cluster[0] and cluster_id == cluster[1]:
2092             return 1
2093     return 0
2094
2095 def local_interface(net_type, cluster_id, nid):
2096     for cluster in local_clusters:
2097         if (net_type == cluster[0] and cluster_id == cluster[1]
2098             and nid == cluster[2]):
2099             return 1
2100     return 0
2101
2102 def find_route(srv_list):
2103     result = []
2104     frm_type = local_clusters[0][0]
2105     for srv in srv_list:
2106         debug("find_route: srv:", srv.nid, "type: ", srv.net_type)
2107         to_type = srv.net_type
2108         to = srv.nid
2109         cluster_id = srv.cluster_id
2110         debug ('looking for route to', to_type, to)
2111         for r in local_routes:
2112             debug("find_route: ", r)
2113             if  (r[3] <= to and to <= r[4]) and cluster_id == r[2]:
2114                 result.append((srv, r))
2115     return result
2116            
2117 def get_active_target(db):
2118     target_uuid = db.getUUID()
2119     target_name = db.getName()
2120     node_name = get_select(target_name)
2121     if node_name:
2122         tgt_dev_uuid = db.get_node_tgt_dev(node_name, target_uuid)
2123     else:
2124         tgt_dev_uuid = db.get_first_ref('active')
2125     return tgt_dev_uuid
2126
2127 def get_server_by_nid_uuid(db,  nid_uuid):
2128     for n in db.lookup_class("network"):
2129         net = Network(n)
2130         if net.nid_uuid == nid_uuid:
2131             return net
2132         
2133
2134 ############################################################
2135 # lconf level logic
2136 # Start a service.
2137 def newService(db):
2138     type = db.get_class()
2139     debug('Service:', type, db.getName(), db.getUUID())
2140     n = None
2141     if type == 'ldlm':
2142         n = LDLM(db)
2143     elif type == 'lov':
2144         n = LOV(db, "YOU_SHOULD_NEVER_SEE_THIS_UUID")
2145     elif type == 'network':
2146         n = Network(db)
2147     elif type == 'routetbl':
2148         n = RouteTable(db)
2149     elif type == 'osd':
2150         n = OSD(db)
2151     elif type == 'cobd':
2152         n = COBD(db)
2153     elif type == 'mdsdev':
2154         n = MDSDEV(db)
2155     elif type == 'mountpoint':
2156         n = Mountpoint(db)
2157     elif type == 'echoclient':
2158         n = ECHO_CLIENT(db)
2159     elif type == 'mgmt':
2160         n = Management(db)
2161     else:
2162         panic ("unknown service type:", type)
2163     return n
2164
2165 #
2166 # Prepare the system to run lustre using a particular profile
2167 # in a the configuration. 
2168 #  * load & the modules
2169 #  * setup networking for the current node
2170 #  * make sure partitions are in place and prepared
2171 #  * initialize devices with lctl
2172 # Levels is important, and needs to be enforced.
2173 def for_each_profile(db, prof_list, operation):
2174     for prof_uuid in prof_list:
2175         prof_db = db.lookup(prof_uuid)
2176         if not prof_db:
2177             panic("profile:", profile, "not found.")
2178         services = getServices(prof_db)
2179         operation(services)
2180         
2181 def doWriteconf(services):
2182     if config.nosetup:
2183         return
2184     for s in services:
2185         if s[1].get_class() == 'mdsdev':
2186             n = newService(s[1])
2187             n.write_conf()
2188
2189 def doSetup(services):
2190     if config.nosetup:
2191         return
2192     for s in services:
2193         n = newService(s[1])
2194         n.prepare()
2195     
2196 def doModules(services):
2197     if config.nomod:
2198         return
2199     for s in services:
2200         n = newService(s[1])
2201         n.load_module()
2202
2203 def doCleanup(services):
2204     if config.nosetup:
2205         return
2206     services.reverse()
2207     for s in services:
2208         n = newService(s[1])
2209         if n.safe_to_clean():
2210             n.cleanup()
2211
2212 def doUnloadModules(services):
2213     if config.nomod:
2214         return
2215     services.reverse()
2216     for s in services:
2217         n = newService(s[1])
2218         if n.safe_to_clean_modules():
2219             n.cleanup_module()
2220
2221 #
2222 # Load profile for 
2223 def doHost(lustreDB, hosts):
2224     global is_router, local_node_name
2225     node_db = None
2226     for h in hosts:
2227         node_db = lustreDB.lookup_name(h, 'node')
2228         if node_db:
2229             break
2230     if not node_db:
2231         panic('No host entry found.')
2232
2233     local_node_name = node_db.get_val('name', 0)
2234     is_router = node_db.get_val_int('router', 0)
2235     lustre_upcall = node_db.get_val('lustreUpcall', '')
2236     portals_upcall = node_db.get_val('portalsUpcall', '')
2237     timeout = node_db.get_val_int('timeout', 0)
2238     ptldebug = node_db.get_val('ptldebug', '')
2239     subsystem = node_db.get_val('subsystem', '')
2240     
2241     find_local_clusters(node_db)
2242     if not is_router:
2243         find_local_routes(lustreDB)
2244
2245     # Two step process: (1) load modules, (2) setup lustre
2246     # if not cleaning, load modules first.
2247     prof_list = node_db.get_refs('profile')
2248
2249     if config.write_conf:
2250         for_each_profile(node_db, prof_list, doModules)
2251         sys_make_devices()
2252         for_each_profile(node_db, prof_list, doWriteconf)
2253         for_each_profile(node_db, prof_list, doUnloadModules)
2254
2255     elif config.recover:
2256         if not (config.tgt_uuid and config.client_uuid and config.conn_uuid):
2257             raise Lustre.LconfError( "--recovery requires --tgt_uuid <UUID> " +
2258                                      "--client_uuid <UUID> --conn_uuid <UUID>")
2259         doRecovery(lustreDB, lctl, config.tgt_uuid, config.client_uuid,
2260                    config.conn_uuid)
2261     elif config.cleanup:
2262         if config.force:
2263             # the command line can override this value
2264             timeout = 5
2265         # ugly hack, only need to run lctl commands for --dump
2266         if config.lctl_dump or config.record:
2267             for_each_profile(node_db, prof_list, doCleanup)
2268             return
2269
2270         sys_set_timeout(timeout)
2271         sys_set_ptldebug(ptldebug)
2272         sys_set_subsystem(subsystem)
2273         sys_set_lustre_upcall(lustre_upcall)
2274         sys_set_portals_upcall(portals_upcall)
2275
2276         for_each_profile(node_db, prof_list, doCleanup)
2277         for_each_profile(node_db, prof_list, doUnloadModules)
2278
2279     else:
2280         # ugly hack, only need to run lctl commands for --dump
2281         if config.lctl_dump or config.record:
2282             sys_set_timeout(timeout)
2283             sys_set_lustre_upcall(lustre_upcall)
2284             for_each_profile(node_db, prof_list, doSetup)
2285             return
2286
2287         sys_make_devices()
2288         sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
2289         sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
2290
2291         for_each_profile(node_db, prof_list, doModules)
2292
2293         sys_set_debug_path()
2294         sys_set_ptldebug(ptldebug)
2295         sys_set_subsystem(subsystem)
2296         script = config.gdb_script
2297         run(lctl.lctl, ' modules >', script)
2298         if config.gdb:
2299             log ("The GDB module script is in", script)
2300             # pause, so user has time to break and
2301             # load the script
2302             time.sleep(5)
2303         sys_set_timeout(timeout)
2304         sys_set_lustre_upcall(lustre_upcall)
2305         sys_set_portals_upcall(portals_upcall)
2306
2307         for_each_profile(node_db, prof_list, doSetup)
2308
2309 def doRecovery(db, lctl, tgt_uuid, client_uuid, nid_uuid):
2310     tgt = db.lookup(tgt_uuid)
2311     if not tgt:
2312         raise Lustre.LconfError("doRecovery: "+ tgt_uuid +" not found.")
2313     new_uuid = get_active_target(tgt)
2314     if not new_uuid:
2315         raise Lustre.LconfError("doRecovery: no active target found for: " +
2316                                 tgt_uuid)
2317     net = choose_local_server(get_ost_net(db, new_uuid))
2318     if not net:
2319         raise Lustre.LconfError("Unable to find a connection to:" + new_uuid)
2320
2321     log("Reconnecting", tgt_uuid, " to ",  net.nid_uuid);
2322     try:
2323         oldnet = get_server_by_nid_uuid(db, nid_uuid)
2324         if oldnet:
2325             lctl.disconnect(oldnet)
2326     except CommandError, e:
2327         log("recover: disconnect", nid_uuid, "failed: ")
2328         e.dump()
2329
2330     try:
2331         lctl.connect(net)
2332     except CommandError, e:
2333         log("recover: connect failed")
2334         e.dump()
2335
2336     lctl.recover(client_uuid, net.nid_uuid)
2337
2338
2339 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
2340     base = os.path.dirname(cmd)
2341     if development_mode():
2342         if not config.lustre:
2343             config.lustre = (os.path.join(base, ".."))
2344         # normalize the portals dir, using command line arg if set
2345         if config.portals:
2346             portals_dir = config.portals
2347         dir = os.path.join(config.lustre, portals_dir)
2348         config.portals = dir
2349         debug('config.portals', config.portals)
2350     elif config.lustre and config.portals:
2351         # production mode
2352         # if --lustre and --portals, normalize portals 
2353         # can ignore POTRALS_DIR here, since it is probly useless here
2354         config.portals = os.path.join(config.lustre, config.portals)
2355         debug('config.portals B', config.portals)
2356
2357 def sysctl(path, val):
2358     debug("+ sysctl", path, val)
2359     if config.noexec:
2360         return
2361     try:
2362         fp = open(os.path.join('/proc/sys', path), 'w')
2363         fp.write(str(val))
2364         fp.close()
2365     except IOError, e:
2366         panic(str(e))
2367
2368
2369 def sys_set_debug_path():
2370     sysctl('portals/debug_path', config.debug_path)
2371
2372 def sys_set_lustre_upcall(upcall):
2373     # the command overrides the value in the node config
2374     if config.lustre_upcall:
2375         upcall = config.lustre_upcall
2376     elif config.upcall:
2377         upcall = config.upcall
2378     if upcall:
2379         lctl.set_lustre_upcall(upcall)
2380
2381 def sys_set_portals_upcall(upcall):
2382     # the command overrides the value in the node config
2383     if config.portals_upcall:
2384         upcall = config.portals_upcall
2385     elif config.upcall:
2386         upcall = config.upcall
2387     if upcall:
2388         sysctl('portals/upcall', upcall)
2389
2390 def sys_set_timeout(timeout):
2391     # the command overrides the value in the node config
2392     if config.timeout and config.timeout > 0:
2393         timeout = config.timeout
2394     if timeout != None and timeout > 0:
2395         lctl.set_timeout(timeout)
2396
2397 def sys_tweak_socknal ():
2398     if config.single_socket:
2399         sysctl("socknal/typed", 0)
2400
2401 def sys_optimize_elan ():
2402     procfiles = ["/proc/elan/config/eventint_punt_loops",
2403                  "/proc/qsnet/elan3/config/eventint_punt_loops",
2404                  "/proc/qsnet/elan4/config/elan4_mainint_punt_loops"]
2405     for p in procfiles:
2406         if os.access(p, os.R_OK):
2407             run ("echo 0 > " + p)
2408
2409 def sys_set_ptldebug(ptldebug):
2410     if config.ptldebug:
2411         ptldebug = config.ptldebug
2412     if ptldebug:
2413         try:
2414             val = eval(ptldebug, ptldebug_names)
2415             val = "0x%x" % (val)
2416             sysctl('portals/debug', val)
2417         except NameError, e:
2418             panic(str(e))
2419
2420 def sys_set_subsystem(subsystem):
2421     if config.subsystem:
2422         subsystem = config.subsystem
2423     if subsystem:
2424         try:
2425             val = eval(subsystem, subsystem_names)
2426             val = "0x%x" % (val)
2427             sysctl('portals/subsystem_debug', val)
2428         except NameError, e:
2429             panic(str(e))
2430
2431 def sys_set_netmem_max(path, max):
2432     debug("setting", path, "to at least", max)
2433     if config.noexec:
2434         return
2435     fp = open(path)
2436     str = fp.readline()
2437     fp.close()
2438     cur = int(str)
2439     if max > cur:
2440         fp = open(path, 'w')
2441         fp.write('%d\n' %(max))
2442         fp.close()
2443     
2444     
2445 def sys_make_devices():
2446     if not os.access('/dev/portals', os.R_OK):
2447         run('mknod /dev/portals c 10 240')
2448     if not os.access('/dev/obd', os.R_OK):
2449         run('mknod /dev/obd c 10 241')
2450
2451
2452 # Add dir to the global PATH, if not already there.
2453 def add_to_path(new_dir):
2454     syspath = string.split(os.environ['PATH'], ':')
2455     if new_dir in syspath:
2456         return
2457     os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
2458     
2459 def default_debug_path():
2460     path = '/tmp/lustre-log'
2461     if os.path.isdir('/r'):
2462         return '/r' + path
2463     else:
2464         return path
2465
2466 def default_gdb_script():
2467     script = '/tmp/ogdb'
2468     if os.path.isdir('/r'):
2469         return '/r' + script
2470     else:
2471         return script
2472
2473
2474 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
2475 # ensure basic elements are in the system path
2476 def sanitise_path():
2477     for dir in DEFAULT_PATH:
2478         add_to_path(dir)
2479
2480 # global hack for the --select handling
2481 tgt_select = {}
2482 def init_select(args):
2483     # args = [service=nodeA,service2=nodeB service3=nodeC]
2484     global tgt_select
2485     for arg in args:
2486         list = string.split(arg, ',')
2487         for entry in list:
2488             srv, node = string.split(entry, '=')
2489             tgt_select[srv] = node
2490
2491 def get_select(srv):
2492     if tgt_select.has_key(srv):
2493         return tgt_select[srv]
2494     return None
2495
2496
2497 FLAG = Lustre.Options.FLAG
2498 PARAM = Lustre.Options.PARAM
2499 INTPARAM = Lustre.Options.INTPARAM
2500 PARAMLIST = Lustre.Options.PARAMLIST
2501 lconf_options = [
2502     ('verbose,v', "Print system commands as they are run"),
2503     ('ldapurl',"LDAP server URL, eg. ldap://localhost", PARAM),
2504     ('config', "Cluster config name used for LDAP query", PARAM),
2505     ('select', "service=nodeA,service2=nodeB ", PARAMLIST),
2506     ('node',   "Load config for <nodename>", PARAM),
2507     ('cleanup,d', "Cleans up config. (Shutdown)"),
2508     ('force,f', "Forced unmounting and/or obd detach during cleanup",
2509                FLAG, 0),
2510     ('single_socket', "socknal option: only use one socket instead of bundle",
2511                FLAG, 0),
2512     ('failover',"""Used to shut down without saving state.
2513                    This will allow this node to "give up" a service to a
2514                    another node for failover purposes. This will not
2515                    be a clean shutdown.""",
2516                FLAG, 0),
2517     ('gdb', """Prints message after creating gdb module script
2518                     and sleeps for 5 seconds."""),
2519     ('noexec,n', """Prints the commands and steps that will be run for a
2520                     config without executing them. This can used to check if a
2521                     config file is doing what it should be doing"""),
2522     ('nomod', "Skip load/unload module step."),
2523     ('nosetup', "Skip device setup/cleanup step."),
2524     ('reformat', "Reformat all devices (without question)"),
2525     ('mkfsoptions', "Additional options for the mk*fs command line", PARAM),
2526     ('dump',  "Dump the kernel debug log to file before portals is unloaded",
2527                PARAM),
2528     ('write_conf', "Save all the client config information on mds."),
2529     ('record', "Write config information on mds."),
2530     ('record_log', "Name of config record log.", PARAM),
2531     ('record_device', "MDS device name that will record the config commands",
2532               PARAM),
2533     ('minlevel', "Minimum level of services to configure/cleanup",
2534                  INTPARAM, 0),
2535     ('maxlevel', """Maximum level of services to configure/cleanup 
2536                     Levels are aproximatly like:
2537                             10 - netwrk
2538                             20 - device, ldlm
2539                             30 - osd, mdd
2540                             40 - mds, ost
2541                             70 - mountpoint, echo_client, osc, mdc, lov""",
2542                INTPARAM, 100),
2543     ('lustre', """Base directory of lustre sources. This parameter will
2544                   cause lconf to load modules from a source tree.""", PARAM),
2545     ('portals', """Portals source directory.  If this is a relative path,
2546                    then it is assumed to be relative to lustre. """, PARAM),
2547     ('timeout', "Set recovery timeout", INTPARAM),
2548     ('upcall',  "Set both portals and lustre upcall script", PARAM),
2549     ('lustre_upcall', "Set lustre upcall script", PARAM),
2550     ('portals_upcall', "Set portals upcall script", PARAM),
2551     ('lctl_dump', "Save lctl ioctls to the dumpfile argument", PARAM),
2552     ('ptldebug', "Set the portals debug level",  PARAM),
2553     ('subsystem', "Set the portals debug subsystem",  PARAM),
2554     ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()),
2555     ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()),
2556 # Client recovery options
2557     ('recover', "Recover a device"),
2558     ('group', "The group of devices to configure or cleanup", PARAM),
2559     ('tgt_uuid', "The failed target (required for recovery)", PARAM),
2560     ('client_uuid', "The failed client (required for recovery)", PARAM),
2561     ('conn_uuid', "The failed connection (required for recovery)", PARAM),
2562
2563     ('inactive', """The name of an inactive service, to be ignored during
2564                     mounting (currently OST-only). Can be repeated.""",
2565                 PARAMLIST),
2566     ]      
2567
2568 def main():
2569     global lctl, config, toplevel, CONFIG_FILE
2570
2571     # in the upcall this is set to SIG_IGN
2572     signal.signal(signal.SIGCHLD, signal.SIG_DFL)
2573     
2574     cl = Lustre.Options("lconf", "config.xml", lconf_options)
2575     try:
2576         config, args = cl.parse(sys.argv[1:])
2577     except Lustre.OptionError, e:
2578         print e
2579         sys.exit(1)
2580
2581     setupModulePath(sys.argv[0])
2582
2583     host = socket.gethostname()
2584
2585     # the PRNG is normally seeded with time(), which is not so good for starting
2586     # time-synchronized clusters
2587     input = open('/dev/urandom', 'r')
2588     if not input:
2589         print 'Unable to open /dev/urandom!'
2590         sys.exit(1)
2591     seed = input.read(32)
2592     input.close()
2593     random.seed(seed)
2594
2595     sanitise_path()
2596     
2597     init_select(config.select)
2598
2599     if len(args) > 0:
2600         if not os.access(args[0], os.R_OK):
2601             print 'File not found or readable:', args[0]
2602             sys.exit(1)
2603         try:
2604             dom = xml.dom.minidom.parse(args[0])
2605         except Exception:
2606             panic("%s does not appear to be a config file." % (args[0]))
2607             sys.exit(1) # make sure to die here, even in debug mode.
2608         CONFIG_FILE = args[0]
2609         db = Lustre.LustreDB_XML(dom.documentElement, dom.documentElement)
2610         if not config.config:
2611             config.config = os.path.basename(args[0])# use full path?
2612             if config.config[-4:] == '.xml':
2613                 config.config = config.config[:-4]
2614     elif config.ldapurl:
2615         if not config.config:
2616             panic("--ldapurl requires --config name")
2617         dn = "config=%s,fs=lustre" % (config.config)
2618         db = Lustre.LustreDB_LDAP('', {}, base=dn, url = config.ldapurl)
2619     elif config.ptldebug or config.subsystem:
2620         sys_set_ptldebug(None)
2621         sys_set_subsystem(None)
2622         sys.exit(0)
2623     else:
2624         print 'Missing config file or ldap URL.'
2625         print 'see lconf --help for command summary'
2626         sys.exit(1)
2627
2628     toplevel = db
2629
2630     ver = db.get_version()
2631     if not ver:
2632         panic("No version found in config data, please recreate.")
2633     if ver != Lustre.CONFIG_VERSION:
2634         panic("Config version", ver, "does not match lconf version",
2635               Lustre.CONFIG_VERSION)
2636
2637     node_list = []
2638     if config.node:
2639         node_list.append(config.node)
2640     else:
2641         if len(host) > 0:
2642             node_list.append(host)
2643         node_list.append('localhost')
2644
2645     debug("configuring for host: ", node_list)
2646
2647     if len(host) > 0:
2648         config.debug_path = config.debug_path + '-' + host
2649         config.gdb_script = config.gdb_script + '-' + host
2650
2651     lctl = LCTLInterface('lctl')
2652
2653     if config.lctl_dump:
2654         lctl.use_save_file(config.lctl_dump)
2655
2656     if config.record:
2657         if not (config.record_device and config.record_log):
2658             panic("When recording, both --record_log and --record_device must be specified.")
2659         lctl.record(config.record_device, config.record_log)
2660
2661     doHost(db, node_list)
2662
2663     if config.record:
2664         lctl.end_record()
2665
2666 if __name__ == "__main__":
2667     try:
2668         main()
2669     except Lustre.LconfError, e:
2670         print e
2671 #        traceback.print_exc(file=sys.stdout)
2672         sys.exit(1)
2673     except CommandError, e:
2674         e.dump()
2675         sys.exit(e.rc)
2676
2677     if first_cleanup_error:
2678         sys.exit(first_cleanup_error)