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