--- /dev/null
+#!/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()
#!/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
# 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)
#
# 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:"
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()
if __name__ == "__main__":
main()
+