3 # Copyright (C) 2002 Cluster File Systems, Inc.
4 # Author: Robert Read <rread@clusterfs.com>
5 # This file is part of Lustre, http://www.lustre.org.
7 # Lustre is free software; you can redistribute it and/or
8 # modify it under the terms of version 2 of the GNU General Public
9 # License as published by the Free Software Foundation.
11 # Lustre is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Lustre; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 # lconf - lustre configuration tool
22 # lconf is the main driver script for starting and stopping
23 # lustre filesystem services.
25 # Based in part on the XML obdctl modifications done by Brian Behlendorf
28 import string, os, stat, popen2, socket, time, random
30 import xml.dom.minidom
35 DEFAULT_TCPBUF = 1048576
37 # Maximum number of devices to search for.
38 # (the /dev/loop* nodes need to be created beforehand)
39 MAX_LOOP_DEVICES = 256
40 PORTALS_DIR = '@PORTALSLOC@'
42 first_cleanup_error = 0
43 def cleanup_error(rc):
44 global first_cleanup_error
45 if not first_cleanup_error:
46 first_cleanup_error = rc
50 print """usage: lconf config.xml
52 config.xml Lustre configuration in xml format.
53 --get <url> URL to fetch a config file
54 --node <nodename> Load config for <nodename>
55 -d | --cleanup Cleans up config. (Shutdown)
56 -f | --force Forced unmounting and/or obd detach during cleanup
57 -v | --verbose Print system commands as they are run
58 -h | --help Print this help
59 --gdb Prints message after creating gdb module script
60 and sleeps for 5 seconds.
61 -n | --noexec Prints the commands and steps that will be run for a
62 config without executing them. This can used to check if a
63 config file is doing what it should be doing. (Implies -v)
64 --nomod Skip load/unload module step.
65 --nosetup Skip device setup/cleanup step.
66 --reformat Reformat all devices (without question)
67 --dump <file> Dump the kernel debug log before portals is unloaded
68 --minlevel <num> Specify the minimum level of services to configure/cleanup (default 0)
69 --maxlevel <num> Specify the maximum level of services to configure/cleanup (default 100)
70 Levels are aproximatly like:
77 70 - mountpoint, echo_client
78 --lustre=src_dir Base directory of lustre sources. This parameter will cause lconf
79 to load modules from a source tree.
80 --portals=src_dir Portals source directory. If this is a relative path, then it is
81 assumed to be relative to lustre.
85 --ldap server LDAP server with lustre config database
86 --makeldiff Translate xml source to LDIFF
87 This are perhaps not needed:
91 # ============================================================
92 # Config parameters, encapsulated in a class
108 self._gdb_script = '/tmp/ogdb'
109 self._debug_path = '/tmp/lustre-log'
110 self._dump_file = None
111 self._lustre_dir = ''
112 self._portals_dir = ''
116 def verbose(self, flag = None):
117 if flag: self._verbose = flag
120 def noexec(self, flag = None):
121 if flag: self._noexec = flag
124 def reformat(self, flag = None):
125 if flag: self._reformat = flag
126 return self._reformat
128 def cleanup(self, flag = None):
129 if flag: self._cleanup = flag
132 def gdb(self, flag = None):
133 if flag: self._gdb = flag
136 def nomod(self, flag = None):
137 if flag: self._nomod = flag
140 def nosetup(self, flag = None):
141 if flag: self._nosetup = flag
144 def force(self, flag = None):
145 if flag: self._force = flag
148 def node(self, val = None):
149 if val: self._node = val
152 def url(self, val = None):
153 if val: self._url = val
156 def gdb_script(self):
157 if os.path.isdir('/r'):
158 return '/r' + self._gdb_script
160 return self._gdb_script
162 def debug_path(self):
163 if os.path.isdir('/r'):
164 return '/r' + self._debug_path
166 return self._debug_path
168 def dump_file(self, val = None):
169 if val: self._dump_file = val
170 return self._dump_file
172 def minlevel(self, val = None):
173 if val: self._minlevel = int(val)
174 return self._minlevel
176 def maxlevel(self, val = None):
177 if val: self._maxlevel = int(val)
178 return self._maxlevel
180 def portals_dir(self, val = None):
181 if val: self._portals_dir = val
182 return self._portals_dir
184 def lustre_dir(self, val = None):
185 if val: self._lustre_dir = val
186 return self._lustre_dir
191 # ============================================================
192 # debugging and error funcs
194 def fixme(msg = "this feature"):
195 raise LconfError, msg + ' not implmemented yet.'
198 msg = string.join(map(str,args))
199 if not config.noexec():
200 raise LconfError(msg)
205 msg = string.join(map(str,args))
210 print string.strip(s)
214 msg = string.join(map(str,args))
217 # ============================================================
218 # locally defined exceptions
219 class CommandError (exceptions.Exception):
220 def __init__(self, cmd_name, cmd_err, rc=None):
221 self.cmd_name = cmd_name
222 self.cmd_err = cmd_err
227 if type(self.cmd_err) == types.StringType:
229 print "! %s (%d): %s" % (self.cmd_name, self.rc, self.cmd_err)
231 print "! %s: %s" % (self.cmd_name, self.cmd_err)
232 elif type(self.cmd_err) == types.ListType:
234 print "! %s (error %d):" % (self.cmd_name, self.rc)
236 print "! %s:" % (self.cmd_name)
237 for s in self.cmd_err:
238 print "> %s" %(string.strip(s))
242 class LconfError (exceptions.Exception):
243 def __init__(self, args):
247 # ============================================================
248 # handle lctl interface
251 Manage communication with lctl
254 def __init__(self, cmd):
256 Initialize close by finding the lctl binary.
258 self.lctl = find_prog(cmd)
261 debug('! lctl not found')
264 raise CommandError('lctl', "unable to find lctl binary.")
269 the cmds are written to stdin of lctl
270 lctl doesn't return errors when run in script mode, so
272 should modify command line to accept multiple commands, or
273 create complex command line options
275 debug("+", self.lctl, cmds)
276 if config.noexec(): return (0, [])
277 p = popen2.Popen3(self.lctl, 1)
278 p.tochild.write(cmds + "\n")
280 out = p.fromchild.readlines()
281 err = p.childerr.readlines()
283 if os.WIFEXITED(ret):
284 rc = os.WEXITSTATUS(ret)
288 raise CommandError(self.lctl, err, rc)
291 def runcmd(self, *args):
293 run lctl using the command line
295 cmd = string.join(map(str,args))
296 debug("+", self.lctl, cmd)
297 rc, out = run(self.lctl, cmd)
299 raise CommandError(self.lctl, out, rc)
303 def network(self, net, nid):
304 """ initialized network and add "self" """
305 # Idea: "mynid" could be used for all network types to add "self," and then
306 # this special case would be gone and the "self" hack would be hidden.
307 if net in ('tcp', 'toe'):
312 quit""" % (net, nid, nid)
321 # create a new connection
322 def connect(self, net, nid, port, servuuid, send_mem, recv_mem):
323 if net in ('tcp', 'toe'):
330 quit""" % (net, servuuid, nid, send_mem, recv_mem, nid, port, )
336 quit""" % (net, servuuid, nid, nid, port, )
340 # add a route to a range
341 def add_route(self, net, gw, lo, hi):
345 quit """ % (net, gw, lo, hi)
349 def del_route(self, net, gw, lo, hi):
357 # add a route to a host
358 def add_route_host(self, net, uuid, gw, tgt):
363 quit """ % (net, uuid, tgt, gw, tgt)
366 # add a route to a range
367 def del_route_host(self, net, uuid, gw, tgt):
373 quit """ % (net, uuid, tgt)
376 # disconnect one connection
377 def disconnect(self, net, nid, port, servuuid):
383 quit""" % (net, nid, servuuid)
387 def disconnectAll(self, net):
396 # create a new device with lctl
397 def newdev(self, attach, setup = ""):
402 quit""" % (attach, setup)
406 def cleanup(self, name, uuid):
412 quit""" % (name, ('', 'force')[config.force()])
416 def lov_setconfig(self, uuid, mdsuuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist):
420 lov_setconfig %s %d %d %d %s %s
421 quit""" % (mdsuuid, uuid, stripe_cnt, stripe_sz, stripe_off, pattern, devlist)
425 def dump(self, dump_file):
428 quit""" % (dump_file)
431 # get list of devices
432 def device_list(self):
433 rc, out = self.runcmd('device_list')
437 def lustre_version(self):
438 rc, out = self.runcmd('version')
441 # ============================================================
442 # Various system-level functions
443 # (ideally moved to their own module)
445 # Run a command and return the output and status.
446 # stderr is sent to /dev/null, could use popen3 to
447 # save it if necessary
449 cmd = string.join(map(str,args))
451 if config.noexec(): return (0, [])
452 f = os.popen(cmd + ' 2>&1')
461 # Run a command in the background.
462 def run_daemon(*args):
463 cmd = string.join(map(str,args))
465 if config.noexec(): return 0
466 f = os.popen(cmd + ' 2>&1')
474 # Determine full path to use for an external command
475 # searches dirname(argv[0]) first, then PATH
477 syspath = string.split(os.environ['PATH'], ':')
478 cmdpath = os.path.dirname(sys.argv[0])
479 syspath.insert(0, cmdpath);
480 if config.portals_dir():
481 syspath.insert(0, os.path.join(cmdpath, config.portals_dir()+'/linux/utils/'))
483 prog = os.path.join(d,cmd)
485 if os.access(prog, os.X_OK):
489 # Recursively look for file starting at base dir
490 def do_find_file(base, mod):
491 fullname = os.path.join(base, mod)
492 if os.access(fullname, os.R_OK):
494 for d in os.listdir(base):
495 dir = os.path.join(base,d)
496 if os.path.isdir(dir):
497 module = do_find_file(dir, mod)
501 def find_module(src_dir, dev_dir, modname):
502 mod = '%s.o' % (modname)
503 module = src_dir +'/'+ dev_dir +'/'+ mod
505 if os.access(module, os.R_OK):
511 # is the path a block device?
518 return stat.S_ISBLK(s[stat.ST_MODE])
520 # build fs according to type
522 def mkfs(fstype, dev):
523 if(fstype in ('ext3', 'extN')):
524 mkfs = 'mkfs.ext2 -j -b 4096'
525 elif (fstype == 'reiserfs'):
526 mkfs = 'mkfs.reiserfs -f'
528 print 'unsupported fs type: ', fstype
529 if not is_block(dev):
530 if(fstype in ('ext3', 'extN')):
532 elif (fstype == 'reiserfs'):
535 print 'unsupported fs type: ', fstype
538 (ret, out) = run (mkfs, force, dev)
540 panic("Unable to build fs:", dev)
541 # enable hash tree indexing on fsswe
542 # FIXME: this check can probably go away on 2.5
544 htree = 'echo "feature FEATURE_C5" | debugfs -w'
545 (ret, out) = run (htree, dev)
547 panic("Unable to enable htree:", dev)
549 # some systems use /dev/loopN, some /dev/loop/N
553 if not os.access(loop + str(0), os.R_OK):
555 if not os.access(loop + str(0), os.R_OK):
556 panic ("can't access loop devices")
559 # find loop device assigned to thefile
562 for n in xrange(0, MAX_LOOP_DEVICES):
564 if os.access(dev, os.R_OK):
565 (stat, out) = run('losetup', dev)
566 if (out and stat == 0):
567 m = re.search(r'\((.*)\)', out[0])
568 if m and file == m.group(1):
574 # create file if necessary and assign the first free loop device
575 def init_loop(file, size, fstype):
576 dev = find_loop(file)
578 print 'WARNING file:', file, 'already mapped to', dev
580 if config.reformat() or not os.access(file, os.R_OK | os.W_OK):
582 error(file, "size must be larger than 8MB")
583 run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size, file))
585 # find next free loop
586 for n in xrange(0, MAX_LOOP_DEVICES):
588 if os.access(dev, os.R_OK):
589 (stat, out) = run('losetup', dev)
591 run('losetup', dev, file)
594 print "out of loop devices"
596 print "out of loop devices"
599 # undo loop assignment
600 def clean_loop(file):
601 dev = find_loop(file)
603 ret, out = run('losetup -d', dev)
605 log('unable to clean loop device:', dev, 'for file:', file)
608 # determine if dev is formatted as a <fstype> filesystem
609 def need_format(fstype, dev):
610 # FIXME don't know how to implement this
613 # initialize a block device if needed
614 def block_dev(dev, size, fstype, format):
615 if config.noexec(): return dev
616 if not is_block(dev):
617 dev = init_loop(dev, size, fstype)
618 if config.reformat() or (need_format(fstype, dev) and format == 'yes'):
622 # panic("device:", dev,
623 # "not prepared, and autoformat is not set.\n",
624 # "Rerun with --reformat option to format ALL filesystems")
629 """lookup IP address for an interface"""
630 rc, out = run("/sbin/ifconfig", iface)
633 addr = string.split(out[1])[1]
634 ip = string.split(addr, ':')[1]
637 def get_local_address(net_type, wildcard):
638 """Return the local address for the network type."""
640 if net_type in ('tcp', 'toe'):
642 iface, star = string.split(wildcard, ':')
643 local = if2addr(iface)
645 panic ("unable to determine ip for:", wildcard)
647 host = socket.gethostname()
648 local = socket.gethostbyname(host)
649 elif net_type == 'elan':
650 # awk '/NodeId/ { print $2 }' '/proc/elan/device0/position'
652 fp = open('/proc/elan/device0/position', 'r')
653 lines = fp.readlines()
662 elif net_type == 'gm':
663 fixme("automatic local address for GM")
667 def is_prepared(uuid):
668 """Return true if a device exists for the uuid"""
669 # expect this format:
670 # 1 UP ldlm ldlm ldlm_UUID 2
672 out = lctl.device_list()
674 if uuid == string.split(s)[4]:
676 except CommandError, e:
680 def fs_is_mounted(path):
681 """Return true if path is a mounted lustre filesystem"""
683 fp = open('/proc/mounts')
684 lines = fp.readlines()
688 if a[1] == path and a[2] == 'lustre_lite':
695 # ============================================================
696 # Classes to prepare and cleanup the various objects
699 """ Base class for the rest of the modules. The default cleanup method is
700 defined here, as well as some utilitiy funcs.
702 def __init__(self, module_name, dom_node):
703 self.dom_node = dom_node
704 self.module_name = module_name
705 self.name = get_attr(dom_node, 'name')
706 self.uuid = get_attr(dom_node, 'uuid')
707 self.kmodule_list = []
711 def info(self, *args):
712 msg = string.join(map(str,args))
713 print self.module_name + ":", self.name, self.uuid, msg
716 def lookup_server(self, srv_uuid):
717 """ Lookup a server's network information """
718 net = get_ost_net(self.dom_node.parentNode, srv_uuid)
720 panic ("Unable to find a server for:", srv_uuid)
721 self._server = Network(net)
723 def get_server(self):
727 """ default cleanup, used for most modules """
729 srv = self.get_server()
730 if srv and local_net(srv):
732 lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
733 except CommandError, e:
734 log(self.module_name, "disconnect failed: ", self.name)
738 lctl.cleanup(self.name, self.uuid)
739 except CommandError, e:
740 log(self.module_name, "cleanup failed: ", self.name)
744 def add_portals_module(self, dev_dir, modname):
745 """Append a module to list of modules to load."""
746 self.kmodule_list.append((config.portals_dir(), dev_dir, modname))
748 def add_lustre_module(self, dev_dir, modname):
749 """Append a module to list of modules to load."""
750 self.kmodule_list.append((config.lustre_dir(), dev_dir, modname))
752 def mod_loaded(self, modname):
753 """Check if a module is already loaded. Look in /proc/modules for it."""
754 fp = open('/proc/modules')
755 lines = fp.readlines()
757 # please forgive my tired fingers for this one
758 ret = filter(lambda word, mod=modname: word == mod,
759 map(lambda line: string.split(line)[0], lines))
762 def load_module(self):
763 """Load all the modules in the list in the order they appear."""
764 for src_dir, dev_dir, mod in self.kmodule_list:
765 # (rc, out) = run ('/sbin/lsmod | grep -s', mod)
766 if self.mod_loaded(mod) and not config.noexec():
768 log ('loading module:', mod)
770 module = find_module(src_dir, dev_dir, mod)
772 panic('module not found:', mod)
773 (rc, out) = run('/sbin/insmod', module)
775 raise CommandError('insmod', out, rc)
777 (rc, out) = run('/sbin/modprobe', mod)
779 raise CommandError('modprobe', out, rc)
781 def cleanup_module(self):
782 """Unload the modules in the list in reverse order."""
783 rev = self.kmodule_list
785 for src_dir, dev_dir, mod in rev:
786 if not self.mod_loaded(mod):
789 if mod == 'portals' and config.dump_file():
790 lctl.dump(config.dump_file())
791 log('unloading module:', mod)
794 (rc, out) = run('/sbin/rmmod', mod)
796 log('! unable to unload module:', mod)
800 class Network(Module):
801 def __init__(self,dom_node):
802 Module.__init__(self, 'NETWORK', dom_node)
803 self.net_type = get_attr(dom_node,'type')
804 self.nid = get_text(dom_node, 'server', '*')
805 self.port = get_text_int(dom_node, 'port', 0)
806 self.send_mem = get_text_int(dom_node, 'send_mem', DEFAULT_TCPBUF)
807 self.recv_mem = get_text_int(dom_node, 'recv_mem', DEFAULT_TCPBUF)
809 self.nid = get_local_address(self.net_type, self.nid)
811 panic("unable to set nid for", self.net_type, self.nid)
812 debug("nid:", self.nid)
814 self.add_portals_module("linux/oslib", 'portals')
815 if node_needs_router():
816 self.add_portals_module("linux/router", 'kptlrouter')
817 if self.net_type == 'tcp':
818 self.add_portals_module("linux/socknal", 'ksocknal')
819 if self.net_type == 'toe':
820 self.add_portals_odule("/linux/toenal", 'ktoenal')
821 if self.net_type == 'elan':
822 self.add_portals_module("/linux/rqswnal", 'kqswnal')
823 if self.net_type == 'gm':
824 self.add_portals_module("/linux/gmnal", 'kgmnal')
825 self.add_lustre_module('obdclass', 'obdclass')
826 self.add_lustre_module('ptlrpc', 'ptlrpc')
829 self.info(self.net_type, self.nid, self.port)
830 if self.net_type in ('tcp', 'toe'):
831 nal_id = '' # default is socknal
832 if self.net_type == 'toe':
834 ret, out = run(TCP_ACCEPTOR, '-s', self.send_mem, '-r', self.recv_mem, nal_id, self.port)
836 raise CommandError(TCP_ACCEPTOR, out, ret)
837 ret = self.dom_node.getElementsByTagName('route_tbl')
839 for r in a.getElementsByTagName('route'):
840 net_type = get_attr(r, 'type')
841 gw = get_attr(r, 'gw')
842 lo = get_attr(r, 'lo')
843 hi = get_attr(r,'hi', '')
844 lctl.add_route(net_type, gw, lo, hi)
845 if net_type in ('tcp', 'toe') and net_type == self.net_type and hi == '':
846 srv = nid2server(self.dom_node.parentNode.parentNode, lo)
848 panic("no server for nid", lo)
850 lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
853 lctl.network(self.net_type, self.nid)
854 lctl.newdev(attach = "ptlrpc RPCDEV RPCDEV_UUID")
857 self.info(self.net_type, self.nid, self.port)
858 ret = self.dom_node.getElementsByTagName('route_tbl')
860 for r in a.getElementsByTagName('route'):
861 lo = get_attr(r, 'lo')
862 hi = get_attr(r,'hi', '')
863 if self.net_type in ('tcp', 'toe') and hi == '':
864 srv = nid2server(self.dom_node.parentNode.parentNode, lo)
866 panic("no server for nid", lo)
869 lctl.disconnect(srv.net_type, srv.nid, srv.port, srv.uuid)
870 except CommandError, e:
871 print "disconnect failed: ", self.name
875 lctl.del_route(self.net_type, self.nid, lo, hi)
876 except CommandError, e:
877 print "del_route failed: ", self.name
882 lctl.cleanup("RPCDEV", "RPCDEV_UUID")
883 except CommandError, e:
884 print "cleanup failed: ", self.name
888 lctl.disconnectAll(self.net_type)
889 except CommandError, e:
890 print "disconnectAll failed: ", self.name
893 if self.net_type in ('tcp', 'toe'):
894 # yikes, this ugly! need to save pid in /var/something
895 run("killall acceptor")
898 def __init__(self,dom_node):
899 Module.__init__(self, 'LDLM', dom_node)
900 self.add_lustre_module('ldlm', 'ldlm')
902 if is_prepared(self.uuid):
905 lctl.newdev(attach="ldlm %s %s" % (self.name, self.uuid),
909 def __init__(self,dom_node):
910 Module.__init__(self, 'LOV', dom_node)
911 self.mds_uuid = get_first_ref(dom_node, 'mds')
912 mds= lookup(dom_node.parentNode, self.mds_uuid)
913 self.mds_name = getName(mds)
914 devs = dom_node.getElementsByTagName('devices')
917 self.stripe_sz = get_attr_int(dev_node, 'stripesize', 65536)
918 self.stripe_off = get_attr_int(dev_node, 'stripeoffset', 0)
919 self.pattern = get_attr_int(dev_node, 'pattern', 0)
920 self.devlist = get_all_refs(dev_node, 'osc')
921 self.stripe_cnt = get_attr_int(dev_node, 'stripecount', len(self.devlist))
922 self.add_lustre_module('mdc', 'mdc')
923 self.add_lustre_module('lov', 'lov')
926 if is_prepared(self.uuid):
928 for osc_uuid in self.devlist:
929 osc = lookup(self.dom_node.parentNode, osc_uuid)
933 # Ignore connection failures, because the LOV will DTRT with
934 # an unconnected OSC.
935 n.prepare(ignore_connect_failure=1)
937 print "Error preparing OSC %s (inactive)\n" % osc_uuid
939 panic('osc not found:', osc_uuid)
940 mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
941 self.info(self.mds_uuid, self.stripe_cnt, self.stripe_sz,
942 self.stripe_off, self.pattern, self.devlist, self.mds_name)
943 lctl.newdev(attach="lov %s %s" % (self.name, self.uuid),
944 setup ="%s" % (mdc_uuid))
947 if not is_prepared(self.uuid):
949 for osc_uuid in self.devlist:
950 osc = lookup(self.dom_node.parentNode, osc_uuid)
955 panic('osc not found:', osc_uuid)
957 cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
960 def load_module(self):
961 for osc_uuid in self.devlist:
962 osc = lookup(self.dom_node.parentNode, osc_uuid)
968 panic('osc not found:', osc_uuid)
969 Module.load_module(self)
972 def cleanup_module(self):
973 Module.cleanup_module(self)
974 for osc_uuid in self.devlist:
975 osc = lookup(self.dom_node.parentNode, osc_uuid)
981 panic('osc not found:', osc_uuid)
983 class LOVConfig(Module):
984 def __init__(self,dom_node):
985 Module.__init__(self, 'LOVConfig', dom_node)
986 self.lov_uuid = get_first_ref(dom_node, 'lov')
987 l = lookup(dom_node.parentNode, self.lov_uuid)
992 self.info(lov.mds_uuid, lov.stripe_cnt, lov.stripe_sz, lov.stripe_off,
993 lov.pattern, lov.devlist, lov.mds_name)
994 lctl.lov_setconfig(lov.uuid, lov.mds_name, lov.stripe_cnt,
995 lov.stripe_sz, lov.stripe_off, lov.pattern,
996 string.join(lov.devlist))
1004 def __init__(self,dom_node):
1005 Module.__init__(self, 'MDS', dom_node)
1006 self.devname, self.size = get_device(dom_node)
1007 self.fstype = get_text(dom_node, 'fstype')
1008 # FIXME: if fstype not set, then determine based on kernel version
1009 self.format = get_text(dom_node, 'autoformat', "no")
1010 if self.fstype == 'extN':
1011 self.add_lustre_module('extN', 'extN')
1012 self.add_lustre_module('mds', 'mds')
1013 self.add_lustre_module('obdclass', 'fsfilt_%s'%(self.fstype))
1016 if is_prepared(self.uuid):
1018 self.info(self.devname, self.fstype, self.format)
1019 blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
1020 if not is_prepared('MDT_UUID'):
1021 lctl.newdev(attach="mdt %s %s" % ('MDT', 'MDT_UUID'),
1023 lctl.newdev(attach="mds %s %s" % (self.name, self.uuid),
1024 setup ="%s %s" %(blkdev, self.fstype))
1026 if is_prepared('MDT_UUID'):
1028 lctl.cleanup("MDT", "MDT_UUID")
1029 except CommandError, e:
1030 print "cleanup failed: ", self.name
1033 if not is_prepared(self.uuid):
1035 Module.cleanup(self)
1036 clean_loop(self.devname)
1038 # Very unusual case, as there is no MDC element in the XML anymore
1039 # Builds itself from an MDS node
1041 def __init__(self,dom_node):
1042 self.mds = MDS(dom_node)
1043 self.dom_node = dom_node
1044 self.module_name = 'MDC'
1045 self.kmodule_list = []
1049 host = socket.gethostname()
1050 self.name = 'MDC_%s' % (self.mds.name)
1051 self.uuid = '%s_%05x_%05x' % (self.name, int(random.random() * 1048576),
1052 int(random.random() * 1048576))
1054 self.lookup_server(self.mds.uuid)
1055 self.add_lustre_module('mdc', 'mdc')
1058 if is_prepared(self.uuid):
1060 self.info(self.mds.uuid)
1061 srv = self.get_server()
1062 lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1063 lctl.newdev(attach="mdc %s %s" % (self.name, self.uuid),
1064 setup ="%s %s" %(self.mds.uuid, srv.uuid))
1067 def __init__(self, dom_node):
1068 Module.__init__(self, 'OBD', dom_node)
1069 self.obdtype = get_attr(dom_node, 'type')
1070 self.devname, self.size = get_device(dom_node)
1071 self.fstype = get_text(dom_node, 'fstype')
1072 # FIXME: if fstype not set, then determine based on kernel version
1073 self.format = get_text(dom_node, 'autoformat', 'yes')
1074 if self.fstype == 'extN':
1075 self.add_lustre_module('extN', 'extN')
1076 self.add_lustre_module(self.obdtype, self.obdtype)
1078 self.add_lustre_module('obdclass' , 'fsfilt_%s' % (self.fstype))
1080 # need to check /proc/mounts and /etc/mtab before
1081 # formatting anything.
1082 # FIXME: check if device is already formatted.
1084 if is_prepared(self.uuid):
1086 self.info(self.obdtype, self.devname, self.size, self.fstype, self.format)
1087 if self.obdtype == 'obdecho':
1090 blkdev = block_dev(self.devname, self.size, self.fstype, self.format)
1091 lctl.newdev(attach="%s %s %s" % (self.obdtype, self.name, self.uuid),
1092 setup ="%s %s" %(blkdev, self.fstype))
1094 if not is_prepared(self.uuid):
1096 Module.cleanup(self)
1097 if not self.obdtype == 'obdecho':
1098 clean_loop(self.devname)
1101 def __init__(self, dom_node):
1102 Module.__init__(self, 'COBD', dom_node)
1103 self.real_uuid = get_first_ref(dom_node, 'real_obd')
1104 self.cache_uuid = get_first_ref(dom_node, 'cache_obd')
1105 self.add_lustre_module('cobd' , 'cobd')
1107 # need to check /proc/mounts and /etc/mtab before
1108 # formatting anything.
1109 # FIXME: check if device is already formatted.
1111 if is_prepared(self.uuid):
1113 self.info(self.real_uuid, self.cache_uuid)
1114 lctl.newdev(attach="cobd %s %s" % (self.name, self.uuid),
1115 setup ="%s %s" %(self.real_uuid, self.cache_uuid))
1118 def __init__(self,dom_node):
1119 Module.__init__(self, 'OST', dom_node)
1120 self.obd_uuid = get_first_ref(dom_node, 'obd')
1121 self.add_lustre_module('ost', 'ost')
1124 if is_prepared(self.uuid):
1126 self.info(self.obd_uuid)
1127 lctl.newdev(attach="ost %s %s" % (self.name, self.uuid),
1128 setup ="%s" % (self.obd_uuid))
1131 # virtual interface for OSC and LOV
1133 def __init__(self,dom_node):
1134 Module.__init__(self, 'VOSC', dom_node)
1135 if dom_node.nodeName == 'lov':
1136 self.osc = LOV(dom_node)
1138 self.osc = OSC(dom_node)
1143 def load_module(self):
1144 self.osc.load_module()
1145 def cleanup_module(self):
1146 self.osc.cleanup_module()
1150 def __init__(self,dom_node):
1151 Module.__init__(self, 'OSC', dom_node)
1152 self.obd_uuid = get_first_ref(dom_node, 'obd')
1153 self.ost_uuid = get_first_ref(dom_node, 'ost')
1154 self.lookup_server(self.ost_uuid)
1155 self.add_lustre_module('osc', 'osc')
1157 def prepare(self, ignore_connect_failure = 0):
1158 if is_prepared(self.uuid):
1160 self.info(self.obd_uuid, self.ost_uuid)
1161 srv = self.get_server()
1164 lctl.connect(srv.net_type, srv.nid, srv.port, srv.uuid, srv.send_mem, srv.recv_mem)
1168 lctl.add_route_host(r[0], srv.uuid, r[1], r[2])
1170 panic ("no route to", srv.nid)
1171 except CommandError:
1172 if (ignore_connect_failure == 0):
1175 lctl.newdev(attach="osc %s %s" % (self.name, self.uuid),
1176 setup ="%s %s" %(self.obd_uuid, srv.uuid))
1179 if not is_prepared(self.uuid):
1181 srv = self.get_server()
1183 Module.cleanup(self)
1185 self.info(self.obd_uuid, self.ost_uuid)
1189 lctl.del_route_host(r[0], srv.uuid, r[1], r[2])
1190 except CommandError, e:
1191 print "del_route failed: ", self.name
1194 Module.cleanup(self)
1197 class ECHO_CLIENT(Module):
1198 def __init__(self,dom_node):
1199 Module.__init__(self, 'ECHO_CLIENT', dom_node)
1200 self.add_lustre_module('obdecho', 'obdecho')
1201 self.lov_uuid = get_first_ref(dom_node, 'osc')
1202 l = lookup(self.dom_node.parentNode, self.lov_uuid)
1206 if is_prepared(self.uuid):
1208 self.osc.prepare() # XXX This is so cheating. -p
1209 self.info(self.lov_uuid)
1211 lctl.newdev(attach="echo_client %s %s" % (self.name, self.uuid),
1212 setup = self.lov_uuid)
1215 if not is_prepared(self.uuid):
1219 def load_module(self):
1220 self.osc.load_module()
1221 Module.load_module(self)
1222 def cleanup_module(self):
1223 Module.cleanup_module(self)
1224 self.osc.cleanup_module()
1227 class Mountpoint(Module):
1228 def __init__(self,dom_node):
1229 Module.__init__(self, 'MTPT', dom_node)
1230 self.path = get_text(dom_node, 'path')
1231 self.mds_uuid = get_first_ref(dom_node, 'mds')
1232 self.lov_uuid = get_first_ref(dom_node, 'osc')
1233 self.add_lustre_module('mdc', 'mdc')
1234 self.add_lustre_module('llite', 'llite')
1235 l = lookup(self.dom_node.parentNode, self.lov_uuid)
1240 mdc_uuid = prepare_mdc(self.dom_node.parentNode, self.mds_uuid)
1241 self.info(self.path, self.mds_uuid, self.lov_uuid)
1242 cmd = "mount -t lustre_lite -o osc=%s,mdc=%s none %s" % \
1243 (self.lov_uuid, mdc_uuid, self.path)
1244 run("mkdir", self.path)
1247 panic("mount failed:", self.path)
1250 self.info(self.path, self.mds_uuid,self.lov_uuid)
1251 if fs_is_mounted(self.path):
1253 (rc, out) = run("umount", "-f", self.path)
1255 (rc, out) = run("umount", self.path)
1257 raise CommandError('umount', out, rc)
1259 if fs_is_mounted(self.path):
1260 panic("fs is still mounted:", self.path)
1262 l = lookup(self.dom_node.parentNode, self.lov_uuid)
1264 cleanup_mdc(self.dom_node.parentNode, self.mds_uuid)
1266 def load_module(self):
1267 self.osc.load_module()
1268 Module.load_module(self)
1269 def cleanup_module(self):
1270 Module.cleanup_module(self)
1271 self.osc.cleanup_module()
1274 # ============================================================
1275 # XML processing and query
1276 # TODO: Change query funcs to use XPath, which is muc cleaner
1277 # Or not. Originally both lconf and lmc used XPath, but it was many
1278 # orders of magnitute slower, and lmc was unusable. - robert
1280 def get_device(obd):
1281 list = obd.getElementsByTagName('device')
1285 size = get_attr_int(dev, 'size', 0)
1286 return dev.firstChild.data, size
1289 # Get the text content from the first matching child
1290 # If there is no content (or it is all whitespace), return
1292 def get_text(dom_node, tag, default=""):
1293 list = dom_node.getElementsByTagName(tag)
1296 dom_node.normalize()
1297 if dom_node.firstChild:
1298 txt = string.strip(dom_node.firstChild.data)
1303 def get_text_int(dom_node, tag, default=0):
1304 list = dom_node.getElementsByTagName(tag)
1308 dom_node.normalize()
1309 if dom_node.firstChild:
1310 txt = string.strip(dom_node.firstChild.data)
1315 panic("text value is not integer:", txt)
1318 def get_attr(dom_node, attr, default=""):
1319 v = dom_node.getAttribute(attr)
1324 def get_attr_int(dom_node, attr, default=0):
1326 v = dom_node.getAttribute(attr)
1331 panic("attr value is not integer", v)
1334 def get_first_ref(dom_node, tag):
1335 """ Get the first uuidref of the type TAG. Used one only
1336 one is expected. Returns the uuid."""
1338 refname = '%s_ref' % tag
1339 list = dom_node.getElementsByTagName(refname)
1341 uuid = getRef(list[0])
1344 def get_all_refs(dom_node, tag):
1345 """ Get all the refs of type TAG. Returns list of uuids. """
1347 refname = '%s_ref' % tag
1348 list = dom_node.getElementsByTagName(refname)
1351 uuids.append(getRef(i))
1354 def get_ost_net(dom_node, uuid):
1355 ost = lookup(dom_node, uuid)
1356 uuid = get_first_ref(ost, 'network')
1359 return lookup(dom_node, uuid)
1361 def nid2server(dom_node, nid):
1362 netlist = dom_node.getElementsByTagName('network')
1363 for net_node in netlist:
1364 if get_text(net_node, 'server') == nid:
1365 return Network(net_node)
1368 def lookup(dom_node, uuid):
1369 for n in dom_node.childNodes:
1370 if n.nodeType == n.ELEMENT_NODE:
1371 if getUUID(n) == uuid:
1378 # Get name attribute of dom_node
1379 def getName(dom_node):
1380 return dom_node.getAttribute('name')
1382 def getRef(dom_node):
1383 return dom_node.getAttribute('uuidref')
1385 # Get name attribute of dom_node
1386 def getUUID(dom_node):
1387 return dom_node.getAttribute('uuid')
1389 # the tag name is the service type
1390 # fixme: this should do some checks to make sure the dom_node is a service
1391 def getServiceType(dom_node):
1392 return dom_node.nodeName
1395 # determine what "level" a particular node is at.
1396 # the order of iniitailization is based on level.
1397 def getServiceLevel(dom_node):
1398 type = getServiceType(dom_node)
1400 if type in ('network',):
1402 elif type in ('device', 'ldlm'):
1404 elif type in ('obd', 'mdd', 'cobd'):
1406 elif type in ('mds','ost'):
1408 elif type in ('mdc','osc'):
1410 elif type in ('lov', 'lovconfig'):
1412 elif type in ('mountpoint', 'echo_client'):
1415 if ret < config.minlevel() or ret > config.maxlevel():
1420 # return list of services in a profile. list is a list of tuples
1421 # [(level, dom_node),]
1422 def getServices(lustreNode, profileNode):
1424 for n in profileNode.childNodes:
1425 if n.nodeType == n.ELEMENT_NODE:
1426 servNode = lookup(lustreNode, getRef(n))
1429 panic('service not found: ' + getRef(n))
1430 level = getServiceLevel(servNode)
1432 list.append((level, servNode))
1436 def getByName(lustreNode, name, tag):
1437 ndList = lustreNode.getElementsByTagName(tag)
1439 if getName(nd) == name:
1444 ############################################################
1446 # FIXME: clean this mess up!
1449 def prepare_mdc(dom_node, mds_uuid):
1451 mds_node = lookup(dom_node, mds_uuid);
1453 panic("no mds:", mds_uuid)
1454 if saved_mdc.has_key(mds_uuid):
1455 return saved_mdc[mds_uuid]
1458 saved_mdc[mds_uuid] = mdc.uuid
1461 def cleanup_mdc(dom_node, mds_uuid):
1463 mds_node = lookup(dom_node, mds_uuid);
1465 panic("no mds:", mds_uuid)
1466 if not saved_mdc.has_key(mds_uuid):
1469 saved_mdc[mds_uuid] = mdc.uuid
1472 ############################################################
1473 # routing ("rooting")
1479 def init_node(dom_node):
1480 global local_node, router_flag
1481 netlist = dom_node.getElementsByTagName('network')
1482 for dom_net in netlist:
1483 type = get_attr(dom_net, 'type')
1484 gw = get_text(dom_net, 'server')
1485 local_node.append((type, gw))
1487 def node_needs_router():
1490 def get_routes(type, gw, dom_net):
1491 """ Return the routes as a list of tuples of the form:
1492 [(type, gw, lo, hi),]"""
1494 tbl = dom_net.getElementsByTagName('route_tbl')
1496 routes = t.getElementsByTagName('route')
1498 lo = get_attr(r, 'lo')
1499 hi = get_attr(r, 'hi', '')
1500 res.append((type, gw, lo, hi))
1504 def init_route_config(lustre):
1505 """ Scan the lustre config looking for routers. Build list of
1507 global routes, router_flag
1509 list = lustre.getElementsByTagName('node')
1511 if get_attr(node, 'router'):
1513 for (local_type, local_nid) in local_node:
1515 netlist = node.getElementsByTagName('network')
1516 for dom_net in netlist:
1517 if local_type == get_attr(dom_net, 'type'):
1518 gw = get_text(dom_net, 'server')
1522 for dom_net in netlist:
1523 if local_type != get_attr(dom_net, 'type'):
1524 for route in get_routes(local_type, gw, dom_net):
1525 routes.append(route)
1530 for iface in local_node:
1531 if net.net_type == iface[0]:
1535 def find_route(net):
1536 global local_node, routes
1537 frm_type = local_node[0][0]
1538 to_type = net.net_type
1540 debug ('looking for route to', to_type,to)
1549 ############################################################
1552 def startService(dom_node, module_flag):
1553 type = getServiceType(dom_node)
1554 debug('Service:', type, getName(dom_node), getUUID(dom_node))
1555 # there must be a more dynamic way of doing this...
1561 elif type == 'lovconfig':
1562 n = LOVConfig(dom_node)
1563 elif type == 'network':
1564 n = Network(dom_node)
1567 elif type == 'cobd':
1577 elif type == 'mountpoint':
1578 n = Mountpoint(dom_node)
1579 elif type == 'echo_client':
1580 n = ECHO_CLIENT(dom_node)
1582 panic ("unknown service type:", type)
1587 if config.cleanup():
1592 if config.nosetup():
1594 if config.cleanup():
1600 # Prepare the system to run lustre using a particular profile
1601 # in a the configuration.
1602 # * load & the modules
1603 # * setup networking for the current node
1604 # * make sure partitions are in place and prepared
1605 # * initialize devices with lctl
1606 # Levels is important, and needs to be enforced.
1607 def startProfile(lustreNode, profileNode, module_flag):
1609 panic("profile:", profile, "not found.")
1610 services = getServices(lustreNode, profileNode)
1611 if config.cleanup():
1614 startService(s[1], module_flag)
1619 def doHost(lustreNode, hosts):
1623 dom_node = getByName(lustreNode, h, 'node')
1627 print 'No host entry found.'
1630 if not get_attr(dom_node, 'router'):
1632 init_route_config(lustreNode)
1637 # Two step process: (1) load modules, (2) setup lustre
1638 # if not cleaning, load modules first.
1639 module_flag = not config.cleanup()
1640 reflist = dom_node.getElementsByTagName('profile')
1641 for profile in reflist:
1642 startProfile(lustreNode, profile, module_flag)
1644 if not config.cleanup():
1645 sys_set_debug_path()
1646 script = config.gdb_script()
1647 run(lctl.lctl, ' modules >', script)
1649 # dump /tmp/ogdb and sleep/pause here
1650 log ("The GDB module script is in", script)
1653 module_flag = not module_flag
1654 for profile in reflist:
1655 startProfile(lustreNode, profile, module_flag)
1657 ############################################################
1658 # Command line processing
1660 def parse_cmdline(argv):
1661 short_opts = "hdnvf"
1662 long_opts = ["ldap", "reformat", "lustre=", "verbose", "gdb",
1663 "portals=", "makeldiff", "cleanup", "noexec",
1664 "help", "node=", "nomod", "nosetup",
1665 "dump=", "force", "minlevel=", "maxlevel="]
1670 opts, args = getopt.getopt(argv, short_opts, long_opts)
1671 except getopt.error:
1676 if o in ("-h", "--help"):
1678 if o in ("-d","--cleanup"):
1680 if o in ("-v", "--verbose"):
1682 if o in ("-n", "--noexec"):
1685 if o == "--portals":
1686 config.portals_dir(a)
1688 config.lustre_dir(a)
1689 if o == "--reformat":
1697 if o == "--nosetup":
1701 if o in ("-f", "--force"):
1703 if o in ("--minlevel",):
1705 if o in ("--maxlevel",):
1713 s = urllib.urlopen(url)
1719 def setupModulePath(cmd, portals_dir = PORTALS_DIR):
1720 base = os.path.dirname(cmd)
1721 if os.access(base+"/Makefile", os.R_OK):
1722 if not config.lustre_dir():
1723 config.lustre_dir(os.path.join(base, ".."))
1724 # normalize the portals dir, using command line arg if set
1725 if config.portals_dir():
1726 portals_dir = config.portals_dir()
1727 dir = os.path.join(config.lustre_dir(), portals_dir)
1728 config.portals_dir(dir)
1729 elif config.lustre_dir() and config.portals_dir():
1731 # if --lustre and --portals, normalize portals
1732 # can ignore POTRALS_DIR here, since it is probly useless here
1733 dir = config.portals_dir()
1734 dir = os.path.join(config.lustre_dir(), dir)
1735 config.portals_dir(dir)
1737 def sys_set_debug_path():
1738 debug("debug path: ", config.debug_path())
1742 fp = open('/proc/sys/portals/debug_path', 'w')
1743 fp.write(config.debug_path())
1748 #/proc/sys/net/core/rmem_max
1749 #/proc/sys/net/core/wmem_max
1750 def sys_set_netmem_max(path, max):
1751 debug("setting", path, "to at least", max)
1759 fp = open(path, 'w')
1760 fp.write('%d\n' %(max))
1764 def sys_make_devices():
1765 if not os.access('/dev/portals', os.R_OK):
1766 run('mknod /dev/portals c 10 240')
1767 if not os.access('/dev/obd', os.R_OK):
1768 run('mknod /dev/obd c 10 241')
1771 # Add dir to the global PATH, if not already there.
1772 def add_to_path(new_dir):
1773 syspath = string.split(os.environ['PATH'], ':')
1774 if new_dir in syspath:
1776 os.environ['PATH'] = os.environ['PATH'] + ':' + new_dir
1779 DEFAULT_PATH = ('/sbin', '/usr/sbin', '/bin', '/usr/bin')
1780 # ensure basic elements are in the system path
1781 def sanitise_path():
1782 for dir in DEFAULT_PATH:
1785 # Initialize or shutdown lustre according to a configuration file
1786 # * prepare the system for lustre
1787 # * configure devices with lctl
1788 # Shutdown does steps in reverse
1791 global TCP_ACCEPTOR, lctl, MAXTCPBUF
1793 host = socket.gethostname()
1795 # the PRNG is normally seeded with time(), which is not so good for starting
1796 # time-synchronized clusters
1797 input = open('/dev/urandom', 'r')
1799 print 'Unable to open /dev/urandom!'
1801 seed = input.read(32)
1807 args = parse_cmdline(sys.argv[1:])
1809 if not os.access(args[0], os.R_OK):
1810 print 'File not found or readable:', args[0]
1812 dom = xml.dom.minidom.parse(args[0])
1814 xmldata = fetch(config.url())
1815 dom = xml.dom.minidom.parseString(xmldata)
1821 node_list.append(config.node())
1824 node_list.append(host)
1825 node_list.append('localhost')
1826 debug("configuring for host: ", node_list)
1829 config._debug_path = config._debug_path + '-' + host
1830 config._gdb_script = config._gdb_script + '-' + host
1832 setupModulePath(sys.argv[0])
1834 TCP_ACCEPTOR = find_prog('acceptor')
1835 if not TCP_ACCEPTOR:
1837 TCP_ACCEPTOR = 'acceptor'
1838 debug('! acceptor not found')
1840 panic('acceptor not found')
1842 lctl = LCTLInterface('lctl')
1845 sys_set_netmem_max('/proc/sys/net/core/rmem_max', MAXTCPBUF)
1846 sys_set_netmem_max('/proc/sys/net/core/wmem_max', MAXTCPBUF)
1847 doHost(dom.documentElement, node_list)
1849 if __name__ == "__main__":
1852 except LconfError, e:
1854 except CommandError, e:
1858 if first_cleanup_error:
1859 sys.exit(first_cleanup_error)