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