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