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