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