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