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