Whamcloud - gitweb
* llrmount.sh now uses lconf
[fs/lustre-release.git] / lustre / utils / lconf
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002 Cluster File Systems, Inc.
4 #   Author: Robert Read <rread@clusterfs.com>
5
6 #   This file is part of Lustre, http://www.lustre.org.
7 #
8 #   Lustre is free software; you can redistribute it and/or
9 #   modify it under the terms of version 2 of the GNU General Public
10 #   License as published by the Free Software Foundation.
11 #
12 #   Lustre is distributed in the hope that it will be useful,
13 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #   GNU General Public License for more details.
16 #
17 #   You should have received a copy of the GNU General Public License
18 #   along with Lustre; if not, write to the Free Software
19 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #
21 # lconf - lustre configuration tool
22 #
23 # lconf is the main driver script for starting and stopping
24 # lustre filesystem services.
25 #
26 # Based in part on the XML obdctl modifications done by Brian Behlendorf 
27
28 import sys, getopt
29 import string, os, stat, popen2, socket, time
30 import re, exceptions
31 import xml.dom.minidom
32
33 # Global parameters
34 TCP_ACCEPTOR = ''
35
36 #
37 # Maximum number of devices to search for.
38 # (the /dev/loop* nodes need to be created beforehand)
39 MAX_LOOP_DEVICES = 256
40
41
42 def usage():
43     print """usage: lconf config.xml
44
45 config.xml          Lustre configuration in xml format.
46 --get <url>         URL to fetch a config file
47 --node <nodename>   Load config for <nodename>
48 -d | --cleanup      Cleans up config. (Shutdown)
49 -v | --verbose      Print system commands as they are run
50 -h | --help         Print this help 
51 --gdb               Prints message after creating gdb module script
52                     and sleeps for 5 seconds.
53 -n | --noexec       Prints the commands and steps that will be run for a
54                     config without executing them. This can used to check if a
55                     config file is doing what it should be doing. (Implies -v)
56 --nomod             Skip load/unload module step.
57 --nosetup           Skip device setup/cleanup step.
58 """
59     TODO = """
60 --ldap server       LDAP server with lustre config database
61 --makeldiff         Translate xml source to LDIFF 
62 --reformat          Reformat all devices (will confirm)
63 This are perhaps not needed:
64 --lustre="src dir"  Base directory of lustre sources. Used to search
65                     for modules.
66 --portals=src       Portals source 
67 """
68     sys.exit()
69
70 # ============================================================
71 # Config parameters, encapsulated in a class
72 class Config:
73     def __init__(self):
74         # flags
75         self._noexec = 0
76         self._verbose = 0
77         self._reformat = 0
78         self._cleanup = 0
79         self._gdb = 0
80         self._nomod = 0
81         self._nosetup = 0
82         # parameters
83         self._modules = None
84         self._node = None
85         self._url = None
86         self._gdb_script = '/tmp/ogdb'
87         self._debug_path = '/tmp/lustre-log'
88         self._src_dir = None
89
90     def verbose(self, flag = None):
91         if flag: self._verbose = flag
92         return self._verbose
93
94     def noexec(self, flag = None):
95         if flag: self._noexec = flag
96         return self._noexec
97
98     def reformat(self, flag = None):
99         if flag: self._reformat = flag
100         return self._reformat
101
102     def cleanup(self, flag = None):
103         if flag: self._cleanup = flag
104         return self._cleanup
105
106     def gdb(self, flag = None):
107         if flag: self._gdb = flag
108         return self._gdb
109
110     def nomod(self, flag = None):
111         if flag: self._nomod = flag
112         return self._nomod
113
114     def nosetup(self, flag = None):
115         if flag: self._nosetup = flag
116         return self._nosetup
117
118     def node(self, val = None):
119         if val: self._node = val
120         return self._node
121
122     def url(self, val = None):
123         if val: self._url = val
124         return self._url
125
126     def gdb_script(self):
127         if os.path.isdir('/r'):
128             return '/r' + self._gdb_script
129         else:
130             return self._gdb_script
131
132     def debug_path(self):
133         if os.path.isdir('/r'):
134             return '/r' + self._debug_path
135         else:
136             return self._debug_path
137
138     def src_dir(self, val = None):
139         if val: self._url = val
140         return self._url
141
142 config = Config()
143
144 # ============================================================ 
145 # debugging and error funcs
146
147 def fixme(msg = "this feature"):
148     raise LconfError, msg + ' not implmemented yet.'
149
150 def panic(*args):
151     msg = string.join(map(str,args))
152     if not config.noexec():
153         raise LconfError(msg)
154
155 def log(*args):
156     msg = string.join(map(str,args))
157     print msg
158
159 def logall(msgs):
160     for s in msgs:
161         print string.strip(s)
162
163 def debug(*args):
164     if config.verbose():
165         msg = string.join(map(str,args))
166         print msg
167
168 # ============================================================
169 # locally defined exceptions
170 class CommandError (exceptions.Exception):
171     def __init__(self, cmd_name, cmd_err, rc=None):
172         self.cmd_name = cmd_name
173         self.cmd_err = cmd_err
174         self.rc = rc
175
176     def dump(self):
177         import types
178         if type(self.cmd_err) == types.StringType:
179             if self.rc:
180                 print "! %s (%d): %s" % (self.cmd_name, self.rc, self.cmd_err)
181             else:
182                 print "! %s: %s" % (self.cmd_name, self.cmd_err)
183         elif type(self.cmd_err) == types.ListType:
184             if self.rc:
185                 print "! %s (error %d):" % (self.cmd_name, self.rc)
186             else:
187                 print "! %s:" % (self.cmd_name)
188             for s in self.cmd_err:
189                 print "> %s" %(string.strip(s))
190         else:
191             print self.cmd_err
192
193 class LconfError (exceptions.Exception):
194     def __init__(self, args):
195         self.args = args
196
197
198 # ============================================================
199 # handle lctl interface
200 class LCTLInterface:
201     """
202     Manage communication with lctl
203     """
204
205     def __init__(self, cmd):
206         """
207         Initialize close by finding the lctl binary.
208         """
209         self.lctl = find_prog(cmd)
210         if not self.lctl:
211             if config.noexec():
212                 debug('! lctl not found')
213                 self.lctl = 'lctl'
214             else:
215                 raise CommandError('lctl', "unable to find lctl binary.")
216             
217     def run(self, cmds):
218         """
219         run lctl
220         the cmds are written to stdin of lctl
221         lctl doesn't return errors when run in script mode, so
222         stderr is checked
223         should modify command line to accept multiple commands, or
224         create complex command line options
225         """
226         debug("+", self.lctl, cmds)
227         if config.noexec(): return (0, [])
228         p = popen2.Popen3(self.lctl, 1)
229         p.tochild.write(cmds + "\n")
230         p.tochild.close()
231         out = p.fromchild.readlines()
232         err = p.childerr.readlines()
233         ret = p.wait()
234         if ret or len(err):
235             raise CommandError(self.lctl, err, ret)
236         return ret, out
237
238             
239     def network(self, net, nid):
240         """ initialized network and add "self" """
241         # Idea: "mynid" could be used for all network types to add "self," and then
242         # this special case would be gone and the "self" hack would be hidden.
243         if net  == 'tcp':
244             cmds =  """
245   network %s
246   mynid %s
247   add_uuid self %s
248   quit""" % (net, nid, nid)
249         else:
250             cmds =  """
251   network %s
252   add_uuid self %s
253   quit""" % (net, nid)
254             
255         self.run(cmds)
256
257     # create a new connection 
258     def connect(self, net, nid, port, servuuid, send_buf, read_buf):
259         # XXX: buf size params not used yet
260         cmds =  """
261   network %s
262   connect %s %d
263   add_uuid %s %s
264   quit""" % (net, nid, port,  servuuid, nid)
265         self.run(cmds)
266                 
267     # create a new connection 
268     def add_route(self, net, to, via):
269         cmds =  """
270         """ 
271         #self.run(cmds)
272
273     # disconnect one connection
274     def disconnect(self, net, nid, port, servuuid):
275         cmds =  """
276   network %s
277   disconnect %s 
278   del_uuid %s
279   quit""" % (net, nid, servuuid)
280         self.run(cmds)
281
282     # disconnect all connections
283     def disconnectAll(self, net):
284         cmds =  """
285   network %s
286   disconnect
287   del_uuid self
288   quit""" % (net)
289         self.run(cmds)
290
291     # create a new device with lctl
292     def newdev(self, attach, setup = ""):
293         cmds = """
294   newdev
295   attach %s
296   setup %s
297   quit""" % (attach, setup)
298         self.run(cmds)
299
300     # cleanup a device
301     def cleanup(self, name, uuid):
302         cmds = """
303   device $%s
304   cleanup
305   detach
306   quit""" % (name)
307         self.run(cmds)
308
309     # create an lov
310     def lovconfig(self, uuid, mdsuuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist):
311         cmds = """
312   device $%s
313   probe
314   lovconfig %s %d %d %d %s %s
315   quit""" % (mdsuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
316         self.run(cmds)
317
318 # ============================================================
319 # Various system-level functions
320 # (ideally moved to their own module)
321
322 # Run a command and return the output and status.
323 # stderr is sent to /dev/null, could use popen3 to
324 # save it if necessary
325 def run(*args):
326     cmd = string.join(map(str,args))
327     debug ("+", cmd)
328     if config.noexec(): return (0, [])
329     f = os.popen(cmd + ' 2>&1')
330     out = f.readlines()
331     ret = f.close()
332     if ret:
333         ret = ret >> 8
334     else:
335         ret = 0
336     return (ret, out)
337
338 # Run a command in the background.
339 def run_daemon(*args):
340     cmd = string.join(map(str,args))
341     debug ("+", cmd)
342     if config.noexec(): return 0
343     f = os.popen(cmd + ' 2>&1')
344     ret = f.close()
345     if ret:
346         ret = ret >> 8
347     else:
348         ret = 0
349     return ret
350
351 # Determine full path to use for an external command
352 # searches dirname(argv[0]) first, then PATH
353 def find_prog(cmd):
354     syspath = string.split(os.environ['PATH'], ':')
355     cmdpath = os.path.dirname(sys.argv[0])
356     syspath.insert(0, cmdpath);
357     syspath.insert(0, os.path.join(cmdpath, '../../portals/linux/utils/'))
358     for d in syspath:
359         prog = os.path.join(d,cmd)
360         if os.access(prog, os.X_OK):
361             return prog
362     return ''
363
364 # Recursively look for file starting at base dir
365 def do_find_file(base, mod):
366     fullname = os.path.join(base, mod)
367     if os.access(fullname, os.R_OK):
368         return fullname
369     for d in os.listdir(base):
370         dir = os.path.join(base,d)
371         if os.path.isdir(dir):
372             module = do_find_file(dir, mod)
373             if module:
374                 return module
375
376 def find_module(src_dir, modname):
377     mod = '%s.o' % (modname)
378     search = (src_dir + "/lustre", src_dir + "/portals/linux")
379     for d in search:
380         try:
381             module = do_find_file(d, mod)
382             if module:
383                 return module
384         except OSError:
385             pass
386     return None
387
388 # is the path a block device?
389 def is_block(path):
390     s = ()
391     try:
392         s =  os.stat(path)
393     except OSError:
394         return 0
395     return stat.S_ISBLK(s[stat.ST_MODE])
396
397 # build fs according to type
398 # fixme: dangerous
399 def mkfs(fstype, dev):
400     if(fstype in ('ext3', 'extN')):
401         mkfs = 'mkfs.ext2 -j -b 4096'
402     else:
403         print 'unsupported fs type: ', fstype
404     if not is_block(dev):
405         force = '-F'
406     else:
407         force = ''
408     (ret, out) = run (mkfs, force, dev)
409     if ret:
410         panic("Unable to build fs:", dev)
411     # enable hash tree indexing on fs
412     if fstype == 'extN':
413         htree = 'echo "feature FEATURE_C5" | debugfs -w'
414         (ret, out) = run (htree, dev)
415         if ret:
416             panic("Unable to enable htree:", dev)
417
418 # some systems use /dev/loopN, some /dev/loop/N
419 def loop_base():
420     import re
421     loop = '/dev/loop'
422     if not os.access(loop + str(0), os.R_OK):
423         loop = loop + '/'
424         if not os.access(loop + str(0), os.R_OK):
425             panic ("can't access loop devices")
426     return loop
427     
428 # find loop device assigned to thefile
429 def find_loop(file):
430     loop = loop_base()
431     for n in xrange(0, MAX_LOOP_DEVICES):
432         dev = loop + str(n)
433         if os.access(dev, os.R_OK):
434             (stat, out) = run('losetup', dev)
435             if (out and stat == 0):
436                 m = re.search(r'\((.*)\)', out[0])
437                 if m and file == m.group(1):
438                     return dev
439         else:
440             break
441     return ''
442
443 # create file if necessary and assign the first free loop device
444 def init_loop(file, size, fstype):
445     dev = find_loop(file)
446     if dev:
447         print 'WARNING file:', file, 'already mapped to', dev
448         return dev
449     if not os.access(file, os.R_OK | os.W_OK):
450         run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size,  file))
451     loop = loop_base()
452     # find next free loop
453     for n in xrange(0, MAX_LOOP_DEVICES):
454         dev = loop + str(n)
455         if os.access(dev, os.R_OK):
456             (stat, out) = run('losetup', dev)
457             if (stat):
458                 run('losetup', dev, file)
459                 return dev
460         else:
461             print "out of loop devices"
462             return ''
463     print "out of loop devices"
464     return ''
465
466 # undo loop assignment
467 def clean_loop(file):
468     dev = find_loop(file)
469     if dev:
470         ret, out = run('losetup -d', dev)
471         if ret:
472             log('unable to clean loop device:', dev, 'for file:', file)
473             logall(out)
474
475 # determine if dev is formatted as a <fstype> filesystem
476 def need_format(fstype, dev):
477     # FIXME don't know how to implement this    
478     return 0
479
480 # initialize a block device if needed
481 def block_dev(dev, size, fstype, format):
482     if config.noexec(): return dev
483     if not is_block(dev):
484         dev = init_loop(dev, size, fstype)
485     if config.reformat() or (need_format(fstype, dev) and format == 'yes'):
486         mkfs(fstype, dev)
487
488 #    else:
489 #        panic("device:", dev,
490 #              "not prepared, and autoformat is not set.\n",
491 #              "Rerun with --reformat option to format ALL filesystems")
492         
493     return dev
494
495 def get_local_address(net_type):
496     """Return the local address for the network type."""
497     local = ""
498     if net_type == 'tcp':
499         # host `hostname`
500         host = socket.gethostname()
501         local = socket.gethostbyname(host)
502     elif net_type == 'elan':
503         # awk '/NodeId/ { print $2 }' '/proc/elan/device0/position'
504         try:
505             fp = open('/proc/elan/device0/position', 'r')
506             lines = fp.readlines()
507             fp.close()
508             for l in lines:
509                 a = string.split(l)
510                 if a[0] == 'NodeId':
511                     local = a[1]
512                     break
513         except IOError, e:
514             log(e)
515     elif net_type == 'gm':
516         fixme("automatic local address for GM")
517     return local
518         
519         
520
521 # ============================================================
522 # Classes to prepare and cleanup the various objects
523 #
524 class Module:
525     """ Base class for the rest of the modules. The default cleanup method is
526     defined here, as well as some utilitiy funcs.
527     """
528     def __init__(self, tag_name, node):
529         self.dom_node = node
530         self.tag_name = tag_name
531         self.name = node.getAttribute('name')
532         self.uuid = node.getAttribute('uuid')
533         self.kmodule_list = []
534         
535     def info(self, *args):
536         msg = string.join(map(str,args))
537         print self.tag_name + ":", self.name, self.uuid, msg
538
539     def cleanup(self):
540         """ default cleanup, used for most modules """
541         self.info()
542         try:
543             lctl.cleanup(self.name, self.uuid)
544         except CommandError, e:
545             print "cleanup failed: ", self.name
546
547     def add_module(self, modname):
548         """Append a module to list of modules to load."""
549         self.kmodule_list.append(modname)
550
551     def mod_loaded(self, modname):
552         """Check if a module is already loaded. Look in /proc/modules for it."""
553         fp = open('/proc/modules')
554         lines = fp.readlines()
555         fp.close()
556         # please forgive my tired fingers for this one
557         ret = filter(lambda word, mod=modname: word == mod,
558                      map(lambda line: string.split(line)[0], lines))
559         return ret
560
561     def load_module(self):
562         """Load all the modules in the list in the order they appear."""
563         for mod in self.kmodule_list:
564             #  (rc, out) = run ('/sbin/lsmod | grep -s', mod)
565             if self.mod_loaded(mod) and not config.noexec():
566                 continue
567             log ('loading module:', mod)
568             if config.src_dir():
569                 module = find_module(config.src_dir(), mod)
570                 if not module:
571                     panic('module not found:', mod)
572                 (rc, out)  = run('/sbin/insmod', module)
573                 if rc:
574                     raise CommandError('insmod', out, rc)
575             else:
576                 (rc, out) = run('/sbin/modprobe', mod)
577                 if rc:
578                     raise CommandError('modprobe', out, rc)
579             
580     def cleanup_module(self):
581         """Unload the modules in the list in reverse order."""
582         rev = self.kmodule_list
583         rev.reverse()
584         for mod in rev:
585             if not self.mod_loaded(mod):
586                 continue
587             log('unloading module:', mod)
588             if config.noexec():
589                 continue
590             (rc, out) = run('/sbin/rmmod', mod)
591             if rc:
592                 log('! unable to unload module:', mod)
593                 logall(out)
594         
595
596 class Network(Module):
597     def __init__(self,node):
598         Module.__init__(self, 'NETWORK', node)
599         self.net_type = node.getAttribute('type')
600         self.nid = getText(node, 'server', '*')
601         self.port = int(getText(node, 'port', 0))
602         self.send_buf = int(getText(node, 'send_buf', 0))
603         self.read_buf = int(getText(node, 'read_buf', 0))
604         if self.nid == '*':
605             self.nid = get_local_address(self.net_type)
606             if not self.nid:
607                 panic("unable to set nid for", self.net_type)
608
609         self.add_module('portals')
610         if self.net_type == 'tcp':
611             self.add_module('ksocknal')
612         if self.net_type == 'elan':
613             self.add_module('kqswnal')
614         if self.net_type == 'gm':
615             self.add_module('kgmnal')
616         self.add_module('obdclass')
617         self.add_module('ptlrpc')
618
619     def prepare(self):
620         self.info(self.net_type, self.nid, self.port)
621         if self.net_type == 'tcp':
622             ret = run_daemon(TCP_ACCEPTOR, self.port)
623             if ret:
624                 raise CommandError(TCP_ACCEPTOR, 'failed', ret)
625         lctl.network(self.net_type, self.nid)
626         lctl.newdev(attach = "ptlrpc RPCDEV")
627
628     def cleanup(self):
629         self.info(self.net_type, self.nid, self.port)
630         try:
631             lctl.cleanup("RPCDEV", "")
632         except CommandError, e:
633             print "cleanup failed: ", self.name
634         try:
635             lctl.disconnectAll(self.net_type)
636         except CommandError, e:
637             print "cleanup failed: ", self.name
638         if self.net_type == 'tcp':
639             # yikes, this ugly! need to save pid in /var/something
640             run("killall acceptor")
641
642 class LDLM(Module):
643     def __init__(self,node):
644         Module.__init__(self, 'LDLM', node)
645         self.add_module('ldlm')
646     def prepare(self):
647         self.info()
648         lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
649                     setup ="")
650
651 class LOV(Module):
652     def __init__(self,node):
653         Module.__init__(self, 'LOV', node)
654         devs = node.getElementsByTagName('devices')[0]
655         self.stripe_sz = int(devs.getAttribute('stripesize'))
656         self.stripe_off = int(devs.getAttribute('stripeoffset'))
657         self.pattern = int(devs.getAttribute('pattern'))
658         mdsref =  node.getElementsByTagName('mds_ref')[0]
659         self.mdsuuid = mdsref.getAttribute('uuidref')
660         mds= lookup(node.parentNode, self.mdsuuid)
661         self.mdsname = getName(mds)
662         devlist = ""
663         stripe_cnt = 0
664         for child in devs.childNodes:
665             if child.nodeName == 'osc_ref':
666                 devlist = devlist +  child.getAttribute('uuidref') + " "
667                 stripe_cnt = stripe_cnt + 1
668         self.devlist = devlist
669         self.stripe_cnt = stripe_cnt
670         self.add_module('osc')
671         self.add_module('lov')
672
673     def prepare(self):
674         self.info(self.mdsuuid, self.stripe_cnt, self.stripe_sz, self.stripe_off, self.pattern,
675         self.devlist, self.mdsname)
676         lctl.lovconfig(self.uuid, self.mdsname, self.stripe_cnt,
677                        self.stripe_sz, self.stripe_off, self.pattern,
678                        self.devlist)
679
680     def cleanup(self):
681         pass
682
683 class MDS(Module):
684     def __init__(self,node):
685         Module.__init__(self, 'MDS', node)
686         self.devname, self.size = getDevice(node)
687         self.fstype = getText(node, 'fstype')
688         self.format = getText(node, 'autoformat', "no")
689         if self.fstype == 'extN':
690             self.add_module('extN') 
691         self.add_module('mds')
692         self.add_module('mds_%s' % (self.fstype))
693             
694     def prepare(self):
695         self.info(self.devname, self.fstype, self.format)
696         blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
697         lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
698                     setup ="%s %s" %(blkdev, self.fstype))
699     def cleanup(self):
700         Module.cleanup(self)
701         clean_loop(self.devname)
702
703 class MDC(Module):
704     def __init__(self,node):
705         Module.__init__(self, 'MDC', node)
706         ref = node.getElementsByTagName('mds_ref')[0]
707         self.mds_uuid = ref.getAttribute('uuidref')
708         self.add_module('mdc')
709
710     def prepare(self):
711         self.info(self.mds_uuid)
712         mds = lookup(self.dom_node.parentNode, self.mds_uuid)
713         if mds == None:
714             panic(self.mdsuuid, "not found.")
715         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
716         srv = Network(net)
717         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
718         lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
719                         setup ="%s %s" %(self.mds_uuid, srv.uuid))
720             
721     def cleanup(self):
722         self.info(self.mds_uuid)
723         net = get_ost_net(self.dom_node.parentNode, self.mds_uuid)
724         srv = Network(net)
725         try:
726             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
727         except CommandError:
728             print "disconnect failed: ", self.name
729         try:
730             lctl.cleanup(self.name, self.uuid)
731         except CommandError:
732             print "cleanup failed: ", self.name
733
734 class OBD(Module):
735     def __init__(self, node):
736         Module.__init__(self, 'OBD', node)
737         self.obdtype = node.getAttribute('type')
738         self.devname, self.size = getDevice(node)
739         self.fstype = getText(node, 'fstype')
740         self.format = getText(node, 'autoformat', 'yes')
741         if self.fstype == 'extN':
742             self.add_module('extN') 
743         self.add_module(self.obdtype)
744
745     # need to check /proc/mounts and /etc/mtab before
746     # formatting anything.
747     # FIXME: check if device is already formatted.
748     def prepare(self):
749         self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
750         if self.obdtype == 'obdecho':
751             blkdev = ''
752         else:
753             blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
754         lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
755                     setup ="%s %s" %(blkdev, self.fstype))
756     def cleanup(self):
757         Module.cleanup(self)
758         if not self.obdtype == 'obdecho':
759             clean_loop(self.devname)
760
761 class OST(Module):
762     def __init__(self,node):
763         Module.__init__(self, 'OST', node)
764         ref = node.getElementsByTagName('obd_ref')[0]
765         self.obd_uuid = ref.getAttribute('uuidref')
766         self.add_module('ost')
767
768     def prepare(self):
769         self.info(self.obd_uuid)
770         lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
771                     setup ="%s" % (self.obd_uuid))
772
773 class OSC(Module):
774     def __init__(self,node):
775         Module.__init__(self, 'OSC', node)
776         ref = node.getElementsByTagName('obd_ref')[0]
777         self.obd_uuid = ref.getAttribute('uuidref')
778         ref = node.getElementsByTagName('ost_ref')[0]
779         self.ost_uuid = ref.getAttribute('uuidref')
780         self.add_module('osc')
781
782     def prepare(self):
783         self.info(self.obd_uuid, self.ost_uuid)
784         net = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
785         srv = Network(net)
786         lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_buf, srv.read_buf)
787         lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
788                     setup ="%s %s" %(self.obd_uuid, srv.uuid))
789
790     def cleanup(self):
791         self.info(self.obd_uuid, self.ost_uuid)
792         net_uuid = get_ost_net(self.dom_node.parentNode, self.ost_uuid)
793         srv = Network(net_uuid)
794         try:
795             lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
796         except CommandError:
797             print " failed: ", self.name
798         try:
799             lctl.cleanup(self.name, self.uuid)
800         except CommandError:
801             print "cleanup failed: ", self.name
802
803 class Mountpoint(Module):
804     def __init__(self,node):
805         Module.__init__(self, 'MTPT', node)
806         self.path = getText(node, 'path')
807         ref = node.getElementsByTagName('mdc_ref')[0]
808         self.mdc_uuid = ref.getAttribute('uuidref')
809         ref = node.getElementsByTagName('osc_ref')[0]
810         self.lov_uuid = ref.getAttribute('uuidref')
811         self.add_module('osc')
812         self.add_module('llite')
813
814     def prepare(self):
815         l = lookup(self.dom_node.parentNode, self.lov_uuid)
816         if l.nodeName == 'lov':
817             lov = LOV(l)
818             for osc_uuid in string.split(lov.devlist):
819                 osc = lookup(self.dom_node.parentNode, osc_uuid)
820                 if osc:
821                     n = OSC(osc)
822                     n.prepare()
823                 else:
824                     panic('osc not found:', osc_uuid)
825             lctl.newdev(attach="lov %s %s" % (lov.name, lov.uuid),
826                         setup ="%s" % (self.mdc_uuid))
827         else:
828             osc = OSC(l)
829             osc.prepare()
830             
831         self.info(self.path, self.mdc_uuid,self.lov_uuid)
832         cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
833               (self.lov_uuid, self.mdc_uuid, self.path)
834         run("mkdir", self.path)
835         ret, val = run(cmd)
836         if ret:
837             panic("mount failed:", self.path)
838     def cleanup(self):
839         self.info(self.path, self.mdc_uuid,self.lov_uuid)
840         run("umount", self.path)
841         l = lookup(self.dom_node.parentNode, self.lov_uuid)
842         if l.nodeName == 'lov':
843             lov = LOV(l)
844             for osc_uuid in string.split(lov.devlist):
845                 osc = lookup(self.dom_node.parentNode, osc_uuid)
846                 if osc:
847                     n = OSC(osc)
848                     n.cleanup()
849                 else:
850                     panic('osc not found:', osc_uuid)
851             lov.cleanup()
852         else:
853             osc = OSC(l)
854             osc.cleanup()
855             
856 # ============================================================
857 # XML processing and query
858 # TODO: Change query funcs to use XPath, which is muc cleaner
859
860 def getDevice(obd):
861     list = obd.getElementsByTagName('device')
862     if len(list) > 0:
863         dev = list[0]
864         dev.normalize();
865         try:
866             size = int(dev.getAttribute('size'))
867         except ValueError:
868             size = 0
869         return dev.firstChild.data, size
870     return '', 0
871
872 # Get the text content from the first matching child
873 # If there is no content (or it is all whitespace), return
874 # the default
875 def getText(node, tag, default=""):
876     list = node.getElementsByTagName(tag)
877     if len(list) > 0:
878         node = list[0]
879         node.normalize()
880         if node.firstChild:
881             txt = string.strip(node.firstChild.data)
882             if txt:
883                 return txt
884     return default
885
886 def get_ost_net(node, uuid):
887     ost = lookup(node, uuid)
888     list = ost.getElementsByTagName('network_ref')
889     if list:
890         uuid = list[0].getAttribute('uuidref')
891     else:
892         return None
893     return lookup(node, uuid)
894     
895 def lookup(node, uuid):
896     for n in node.childNodes:
897         if n.nodeType == n.ELEMENT_NODE:
898             if getUUID(n) == uuid:
899                 return n
900             else:
901                 n = lookup(n, uuid)
902                 if n: return n
903     return None
904             
905 # Get name attribute of node
906 def getName(node):
907     return node.getAttribute('name')
908
909 def getRef(node):
910     return node.getAttribute('uuidref')
911
912 # Get name attribute of node
913 def getUUID(node):
914     return node.getAttribute('uuid')
915
916 # the tag name is the service type
917 # fixme: this should do some checks to make sure the node is a service
918 def getServiceType(node):
919     return node.nodeName
920
921 #
922 # determine what "level" a particular node is at.
923 # the order of iniitailization is based on level.  objects
924 # are assigned a level based on type:
925 #  net,devices,ldlm:1, obd, mdd:2  mds,ost:3 osc,mdc:4 mounts:5
926 def getServiceLevel(node):
927     type = getServiceType(node)
928     if type in ('network',):
929         return 1
930     if type in ('device', 'ldlm'):
931         return 2
932     elif type in ('obd', 'mdd'):
933         return 3
934     elif type in ('mds','ost'):
935         return 4
936     elif type in ('mdc','osc'):
937         return 5
938     elif type in ('lov',):
939         return 6
940     elif type in ('mountpoint',):
941         return 7
942     return 0
943
944 #
945 # return list of services in a profile. list is a list of tuples
946 # [(level, node),]
947 def getServices(lustreNode, profileNode):
948     list = []
949     for n in profileNode.childNodes:
950         if n.nodeType == n.ELEMENT_NODE:
951             servNode = lookup(lustreNode, getRef(n))
952             if not servNode:
953                 print n
954                 panic('service not found: ' + getRef(n))
955             level = getServiceLevel(servNode)
956             list.append((level, servNode))
957     list.sort()
958     return list
959
960 def getByName(lustreNode, tag, name):
961     ndList = lustreNode.getElementsByTagName(tag)
962     for nd in ndList:
963         if getName(nd) == name:
964             return nd
965     return None
966     
967
968 # ============================================================
969 # lconf level logic
970 # Start a service.
971 def startService(node, clean_flag, module_flag):
972     type = getServiceType(node)
973     debug('Service:', type, getName(node), getUUID(node))
974     # there must be a more dynamic way of doing this...
975     n = None
976     if type == 'ldlm':
977         n = LDLM(node)
978     elif type == 'lov':
979         n = LOV(node)
980     elif type == 'network':
981         n = Network(node)
982     elif type == 'obd':
983         n = OBD(node)
984     elif type == 'ost':
985         n = OST(node)
986     elif type == 'mds':
987         n = MDS(node)
988     elif type == 'osc':
989         n = OSC(node)
990     elif type == 'mdc':
991         n = MDC(node)
992     elif type == 'mountpoint':
993         n = Mountpoint(node)
994     else:
995         panic ("unknown service type:", type)
996
997     if module_flag:
998         if config.nomod():
999             return
1000         if clean_flag:
1001             n.cleanup_module()
1002         else:
1003             n.load_module()
1004     else:
1005         if config.nosetup():
1006             return
1007         if clean_flag:
1008             n.cleanup()
1009         else:
1010             n.prepare()
1011
1012 #
1013 # Prepare the system to run lustre using a particular profile
1014 # in a the configuration. 
1015 #  * load & the modules
1016 #  * setup networking for the current node
1017 #  * make sure partitions are in place and prepared
1018 #  * initialize devices with lctl
1019 # Levels is important, and needs to be enforced.
1020 def startProfile(lustreNode, profileNode, clean_flag, module_flag):
1021     if not profileNode:
1022         panic("profile:", profile, "not found.")
1023     services = getServices(lustreNode, profileNode)
1024     if clean_flag:
1025         services.reverse()
1026     for s in services:
1027         startService(s[1], clean_flag, module_flag)
1028
1029 #
1030 # Load profile for 
1031 def doHost(lustreNode, hosts, clean_flag):
1032     node = None
1033     for h in hosts:
1034         node = getByName(lustreNode, 'node', h)
1035         if node:
1036             break
1037
1038     if not node:
1039         print 'No host entry found.'
1040         return
1041
1042     # Two step process: (1) load modules, (2) setup lustre
1043     # if not cleaning, load modules first.
1044     module_flag = not clean_flag
1045     reflist = node.getElementsByTagName('profile')
1046     for profile in reflist:
1047             startProfile(lustreNode,  profile, clean_flag, module_flag)
1048
1049     if not clean_flag:
1050         setDebugPath()
1051         script = config.gdb_script()
1052         run(lctl.lctl, ' modules >', script)
1053         if config.gdb():
1054             # dump /tmp/ogdb and sleep/pause here
1055             log ("The GDB module script is in", script)
1056             time.sleep(5)
1057             
1058     module_flag = not module_flag
1059     for profile in reflist:
1060             startProfile(lustreNode,  profile, clean_flag, module_flag)
1061
1062 # Command line processing
1063 #
1064 def parse_cmdline(argv):
1065     short_opts = "hdnv"
1066     long_opts = ["ldap", "reformat", "lustre=", "verbose", "gdb",
1067                  "portals=", "makeldiff", "cleanup", "noexec",
1068                  "help", "node=", "get=", "nomod", "nosetup"]
1069     opts = []
1070     args = []
1071     try:
1072         opts, args = getopt.getopt(argv, short_opts, long_opts)
1073     except getopt.error:
1074         print "invalid opt"
1075         usage()
1076
1077     for o, a in opts:
1078         if o in ("-h", "--help"):
1079             usage()
1080         if o in ("-d","--cleanup"):
1081             config.cleanup(1)
1082         if o in ("-v", "--verbose"):
1083             config.verbose(1)
1084         if o in ("-n", "--noexec"):
1085             config.noexec(1)
1086             config.verbose(1)
1087         if o == "--portals":
1088             config.portals =  a
1089         if o == "--lustre":
1090             config.lustre  = a
1091         if o  == "--reformat":
1092             config.reformat(1)
1093         if o  == "--node":
1094             config.node(a)
1095         if o  == "--get":
1096             config.url(a)
1097         if o  == "--gdb":
1098             config.gdb(1)
1099         if o  == "--nomod":
1100             config.nomod(1)
1101         if o  == "--nosetup":
1102             config.nosetup(1)
1103     return args
1104
1105 def fetch(url):
1106     import urllib
1107     data = ""
1108     try:
1109         s = urllib.urlopen(url)
1110         data = s.read()
1111     except:
1112         usage()
1113     return data
1114
1115 def setupModulePath(cmd):
1116     base = os.path.dirname(cmd)
1117     if os.access(base+"/Makefile", os.R_OK):
1118         config.src_dir(base + "/../../")
1119
1120 def setDebugPath():
1121     debug("debug path: ", config.debug_path())
1122     if config.noexec():
1123         return
1124     try:
1125         fp = open('/proc/sys/portals/debug_path', 'w')
1126         fp.write(config.debug_path())
1127         fp.close()
1128     except IOError, e:
1129         print e
1130              
1131     
1132 def makeDevices():
1133     if not os.access('/dev/portals', os.R_OK):
1134         run('mknod /dev/portals c 10 240')
1135     if not os.access('/dev/obd', os.R_OK):
1136         run('mknod /dev/obd c 10 241')
1137
1138 # Initialize or shutdown lustre according to a configuration file
1139 #   * prepare the system for lustre
1140 #   * configure devices with lctl
1141 # Shutdown does steps in reverse
1142 #
1143 def main():
1144     global TCP_ACCEPTOR, lctl
1145     args = parse_cmdline(sys.argv[1:])
1146     if len(args) > 0:
1147         if not os.access(args[0], os.R_OK | os.W_OK):
1148             print 'File not found:', args[0]
1149             sys.exit(1)
1150         dom = xml.dom.minidom.parse(args[0])
1151     elif config.url():
1152         xmldata = fetch(config.url())
1153         dom = xml.dom.minidom.parseString(xmldata)
1154     else:
1155         usage()
1156
1157     node_list = []
1158     if config.node():
1159         node_list.append(config.node())
1160     else:
1161         host = socket.gethostname()
1162         if len(host) > 0:
1163             node_list.append(host)
1164         node_list.append('localhost')
1165     debug("configuring for host: ", node_list)
1166
1167     TCP_ACCEPTOR = find_prog('acceptor')
1168     if not TCP_ACCEPTOR:
1169         if config.noexec():
1170             TCP_ACCEPTOR = 'acceptor'
1171             debug('! acceptor not found')
1172         else:
1173             panic('acceptor not found')
1174
1175     lctl = LCTLInterface('lctl')
1176
1177     setupModulePath(sys.argv[0])
1178     makeDevices()
1179     doHost(dom.documentElement, node_list, config.cleanup())
1180
1181 if __name__ == "__main__":
1182     try:
1183         main()
1184     except LconfError, e:
1185         print e
1186     except CommandError, e:
1187         e.dump()
1188         
1189