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