From 9ba3edce9e9f53337b292e0aa8489b54bca8dd44 Mon Sep 17 00:00:00 2001 From: rread Date: Thu, 4 Jul 2002 01:00:50 +0000 Subject: [PATCH] initial version --- lustre/utils/lconf | 343 ++++++++++++++++++++++++++++++++++++++++++++++++ lustre/utils/lmc | 72 +++++----- lustre/utils/lustre.dtd | 14 +- 3 files changed, 386 insertions(+), 43 deletions(-) create mode 100755 lustre/utils/lconf diff --git a/lustre/utils/lconf b/lustre/utils/lconf new file mode 100755 index 0000000..bbe3239 --- /dev/null +++ b/lustre/utils/lconf @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# +# lconf - lustre configuration tool +# +# lconf is the main driver script for starting and stopping +# lustre filesystem services. + +import sys, getopt +import string, os, stat +import re +import xml.dom.minidom + +def fixme(): + raise RuntimeError, 'This feature not implmemented yet.' + +def panic(msg): + raise RuntimeError, msg + +# +# Maximum number of devices to search for. +# (the /dev/loop* nodes need to be created beforehand) +MAX_LOOP_DEVICES = 256 + + +def usage(): + print """usage: lconf --ldap server | config.xml + +config.xml Lustre configuration in xml format. +--ldap server LDAP server with lustre config database + +Options: +--reformat Reformat all devices (will confirm) +--dev="lustre src" Base directory of lustre sources. Used to search + for modules. +--portals=src Portals source +--makeldiff Translate xml source to LDIFF +--cleanup Cleans up config. (Shutdown) +--iam myname ?? + +(SCRIPT STILL UNDER DEVELOPMENT, MOST FUNCTIONALITY UNIMPLEMENTED) +""" + +# ============================================================ +# Various system-level functions +# (ideally moved to their own module) + +# Run a command and return the output and status. +# stderr is sent to /dev/null, could use popen3 to +# save it if necessary +def run(*args): + cmd = string.join(map(str,args)) + print "+", cmd + f = os.popen(cmd + ' 2> /dev/null') + out = f.readlines() + ret = f.close() + if ret: + ret = ret >> 8 + else: + ret = 0 + return (out, ret) + +# is the path a block device? +def is_block(path): + s = () + try: + s = os.stat(path) + except OSError: + return 0 + return stat.S_ISBLK(s[stat.ST_MODE]) + +# build fs according to type +# fixme: dangerous +def mkfs(fstype, dev): + if(fstype == 'ext3'): + mkfs = 'mkfs.ext2 -j' + elif (fstype == 'extN'): + mkfs = 'mkfs.ext2 -j' + else: + print 'unsupported fs type: ', fstype + if not is_block(dev): + force = '-F' + else: + force = '' + run (mkfs, force, dev) + +# some systems use /dev/loopN, some /dev/loop/N +def loop_base(): + import re + loop = '/dev/loop' + if not os.access(loop + str(0), os.R_OK): + loop = loop + '/' + if not os.access(loop + str(0), os.R_OK): + panic ("can't access loop devices") + return loop + +# find loop device assigned to thefile +def find_loop(file): + loop = loop_base() + for n in xrange(0, MAX_LOOP_DEVICES): + dev = loop + str(n) + if os.access(dev, os.R_OK): + (out, stat) = run('losetup', dev) + if (stat == 0 ): + m = re.search(r'\((.*)\)', out[0]) + if m and file == m.group(1): + return dev + else: + break + return '' + +# create file if necessary and assign the first free loop device +def init_loop(file, size, fstype): + dev = find_loop(file) + if dev: + print 'WARNING file:', file, 'already mapped to', dev + return dev + if not os.access(file, os.R_OK | os.W_OK): + run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size, file)) + loop = loop_base() + # find next free loop + for n in xrange(0, MAX_LOOP_DEVICES): + dev = loop + str(n) + if os.access(dev, os.R_OK): + (out, stat) = run('losetup', dev) + if (stat): + run('losetup', dev, file) + return dev + else: + print "out of loop devices" + return '' + print "out of loop devices" + return '' + +# undo loop assignment +def clean_loop(file): + dev = find_loop(file) + if dev: + run('losetup -d', dev) + +# ============================================================ +# Functions to prepare the various objects + +def prepare_ldlm(node): + print 'prepare ldlm' + +def prepare_network(node): + print 'prepare network' + +# need to check /proc/mounts and /etc/mtab before +# formatting anything. +# FIXME: check if device is already formatted. +def prepare_obd(obd): + obdname = obd.getAttribute('name') + (dev, size, fstype, format) = getDeviceInfo(obd) + print "OBD: ", dev, size, fstype, format +## if not is_block(dev): +## dev = init_loop(dev, size, fstype) +## if (format == 'yes'): +## mkfs(fstype, dev) + +def prepare_ost(node): + print 'prepare ost' + +def prepare_mds(node): + print 'prepare mds' + +def prepare_osc(node): + print 'prepare osc' + +def prepare_mdc(node): + print 'prepare mdc' + +def prepare_mountpoint(node): + print 'prepare mtpt' + +# ============================================================ +# XML processing + +# extract device attributes for an obd +def getDeviceInfo(obd): + dev = obd.getElementsByTagName('device')[0] + dev.normalize(); + try: + size = int(dev.getAttribute('size')) + except ValueError: + size = 0 + + fstype = getText(obd, 'fstype') + format = getText(obd, 'autoformat') + return (dev.firstChild.data, size, fstype, format) + +# Get the text content from the first matching child +def getText(node, tag): + node = node.getElementsByTagName(tag)[0] + node.normalize() + return node.firstChild.data + +# Recusively search for a particular node by uuid +def getByUUID(node, uuid): + for n in node.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if getUUID(n) == uuid: + return n + return None + +# Recusively search for a particular node by name +def getByName(node, name): + for n in node.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if getName(n) == name: + return n + return None + +# Get name attribute of node +def getName(node): + return node.getAttribute('name') + +# Get name attribute of node +def getUUID(node): + return node.getAttribute('uuid') + +# the tag name is the service type +# fixme: this should do some checks to make sure the node is a service +def getServiceType(node): + return node.nodeName + +# +# determine what "level" a particular node is at. +# the order of iniitailization is based on level. objects +# are assigned a level based on type: +# net,devices:1, obd, mdd:2 mds,ost:3 osc,mdc:4 mounts:5 +def getServiceLevel(node): + type = getServiceType(node) + if type in ('network', 'device', 'ldlm'): + return 1 + elif type in ('obd', 'mdd'): + return 2 + elif type in ('mds','ost'): + return 3 + elif type in ('mdc','osc'): + return 4 + elif type in ('mountpoint',): + return 5 + return 0 + +# +# return list of services in a profile. list is a list of tuples +# [(level, node),] +def getServices(lustreNode, profileNode): + list = [] + for n in profileNode.childNodes: + if n.nodeType == n.ELEMENT_NODE: + servNode = getByName(lustreNode, getName(n)) + if not servNode: + panic('service not found: ' + servNode) + level = getServiceLevel(servNode) + list.append((level, servNode)) + list.sort() + return list + +def getProfile(lustreNode, profile): + profList = lustreNode.getElementsByTagName('profile') + for prof in profList: + if getName(prof) == profile: + return prof + return None + +# +# Start a service. +def startService(node): + type = getServiceType(node) + print 'Starting service:', type, getName(node), getUUID(node) + # there must be a more dynamic way of doing this... + if type == 'ldlm': + prepare_ldlm(node) + elif type == 'network': + prepare_network(node) + elif type == 'obd': + prepare_obd(node) + elif type == 'ost': + prepare_ost(node) + elif type == 'mds': + prepare_mds(node) + elif type == 'osc': + prepare_osc(node) + elif type == 'mdc': + prepare_mdc(node) + elif type == 'mountpoint': + prepare_mountpoint(node) + +# +# Prepare the system to run lustre using a particular profile +# in a the configuration. +# * load & the modules +# * setup networking for the current node +# * make sure partitions are in place and prepared +# * initialize devices with lctl +# Levels is important, and needs to be enforced. +def startProfile(lustreNode, profile): + profileNode = getProfile(lustreNode, profile) + if not profileNode: + print "profile:", profile, "not found." + sys.exit(1) + services = getServices(lustreNode, profileNode) + for s in services: + startService(s[1]) + + #obdlist = lustreNode.getElementsByTagName("obd") + #for obd in obdlist: + # prepareDevice(obd) + +# +# Initialize or shutdown lustre according to a configuration file +# * prepare the system for lustre +# * configure devices with lctl +# Shutdown does steps in reverse +# +def main(): + dom = xml.dom.minidom.parse(sys.argv[1]) + startProfile(dom.childNodes[0], 'local-profile') + +# +# try a different traceback style. (dare ya to try this in java) +def my_traceback(file=None): + """Print the list of tuples as returned by extract_tb() or + extract_stack() as a formatted stack trace to the given file.""" + import traceback + (t,v, tb) = sys.exc_info() + list = traceback.extract_tb(tb) + if not file: + file = sys.stderr + for filename, lineno, name, line in list: + if line: + print '%s:%04d %-14s %s' % (filename,lineno, name, line.strip()) + else: + print '%s:%04d %s' % (filename,lineno, name) + print '%s: %s' % (t, v) + +if __name__ == "__main__": + try: + main() + except: + my_traceback() diff --git a/lustre/utils/lmc b/lustre/utils/lmc index c68652d..db18847 100755 --- a/lustre/utils/lmc +++ b/lustre/utils/lmc @@ -1,24 +1,40 @@ #!/usr/bin/env python - +# +# lmc - lustre configurtion data manager +# import sys, getopt import xml.dom.minidom -# -# Example of walking the tree -# -def handleLustre(lustre): - handleHost(lustre.getElementsByTagName("node")) -def handleHost(hosts): - for host in hosts: - name = host.getAttribute("name") - uuid = host.getAttribute("uuid") +def usage(): + print """usage: lmc [--ost | --mtpt | --lov] cmd args +Commands: +--ost "device" "host" [size] + Creates an OBD/OST/OSC configuration triplet for a new device. + When used on "host", the device will be initialized and the OST + will be enabled. On client nodes, the OSC will be avaiable. + +--mtpt "mds" "ost/lov-name" /mnt/point + Creates a client mount point. + +--lov "mds" "lov name < "all-ost.xml" + Produces a logical volum striped over the OSTs found in all-ost.xml. + (Not sure how all-ost.xml is created, exactly.) + +Options: +--merge="xml file" Add the new objects to an existing file +--format Format the partitions if unformated +--reformat Reformat partitions (this should be an lconf arg, + I think) +(SCRIPT STILL UNDER DEVELOPMENT, MOST COMMANDS/OPTIONS UNIMPLEMENTED) +""" # # manage names and uuids # need to initialize this by walking tree to ensure # no duplicate names or uuids are created. # this are just place holders for now. +# consider changing this to be like OBD-dev-host name_ctr = 1 def new_name(base): global name_ctr @@ -105,12 +121,13 @@ def add_OST(dom, options, args): # XXX need some error checking devname = args[0] host = args[1] + size = args[2] obdname = new_name("obd") oscname = new_name("osc") ostname = new_name("ost") - obd = new_OBD(dom, obdname, "extN", devname, "no") + obd = new_OBD(dom, obdname, "extN", devname, "no", size) osc = new_OSC(dom, oscname, obdname) ost = new_OST(dom, ostname, host, 2020, obdname) @@ -122,28 +139,6 @@ def add_OST(dom, options, args): # # Command line processing # -def usage(): - print """usage: lmc [--ost | --mtpt | --lov] cmd args -Commands: ---ost "device" "host" - Creates an OBD/OST/OSC configuration triplet for a new device. - When used on "host", the device will be initialized and the OST - will be enabled. On client nodes, the OSC will be avaiable. - ---mtpt "mds" "ost/lov-name" /mnt/point - Creates a client mount point. - ---lov "mds" "lov name < "all-ost.xml" - Produces a logical volum striped over the OSTs found in all-ost.xml. - (Not sure how all-ost.xml is created, exactly.) - -Options: ---merge="xml file" Add the new objects to an existing file ---format Format the partitions if unformated ---reformat Reformat partitions (this should be an lconf arg, - I think) -(SCRIPT STILL UNDER DEVELOPMENT, MOST COMMANDS/OPTIONS UNIMPLEMENTED) -""" def cmdline(argv): short_opts = "ho:" @@ -179,23 +174,23 @@ def cmdline(argv): def main(): options, args = cmdline(sys.argv[1:]) + outFile = '-' if options.has_key('merge'): outFile = options['merge'] dom = xml.dom.minidom.parse(outFile) else: - if options.has_key('output'): - outFile = options['output'] - else: - outFile = '-' dom = new_Lustre() + if options.has_key('output'): + outFile = options['output'] + if options.has_key('ost'): add_OST(dom, options, args) elif options.has_key('mtpt'): print "--mtpt not implemented" elif options.has_key('lov'): - print "--mtpt not implemented" + print "--lov not implemented" else: print "Missing command" usage() @@ -209,3 +204,4 @@ def main(): if __name__ == "__main__": main() + diff --git a/lustre/utils/lustre.dtd b/lustre/utils/lustre.dtd index 72ce628..7716c01 100644 --- a/lustre/utils/lustre.dtd +++ b/lustre/utils/lustre.dtd @@ -14,20 +14,20 @@ - + - + - + - + @@ -41,7 +41,11 @@ - + + + + -- 1.8.3.1