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