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