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