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