Whamcloud - gitweb
initial version
authorrread <rread>
Thu, 4 Jul 2002 01:00:50 +0000 (01:00 +0000)
committerrread <rread>
Thu, 4 Jul 2002 01:00:50 +0000 (01:00 +0000)
lustre/utils/lconf [new file with mode: 0755]
lustre/utils/lmc
lustre/utils/lustre.dtd

diff --git a/lustre/utils/lconf b/lustre/utils/lconf
new file mode 100755 (executable)
index 0000000..bbe3239
--- /dev/null
@@ -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()
index c68652d..db18847 100755 (executable)
@@ -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()
+
index 72ce628..7716c01 100644 (file)
 <!ELEMENT lustre (node | profile | mountpoint | ldlm |\r
                   mds | mdc | obd | ost | osc | lov | router)*>\r
 \r
-<!ELEMENT profile (service_id | mountpoint_id)*>\r
+<!ELEMENT profile (service_id)*>\r
 <!ATTLIST profile %tag.attr;>\r
 <!ELEMENT mountpoint (path | fileset | mds_id | lov_id)*>\r
 <!ATTLIST mountpoint %tag.attr;>\r
-<!ELEMENT node (profile_id)>\r
+<!ELEMENT node (network*, profile_id)>\r
 <!ATTLIST node %tag.attr;>\r
 <!ELEMENT ldlm EMPTY>\r
 <!ATTLIST ldlm %tag.attr;>\r
 \r
 <!ELEMENT obd (fstype | device | autoformat)*>\r
 <!ATTLIST obd %tag.attr; type (obdfilter | obdext2 | obdecho) 'obdfilter'>\r
-<!ELEMENT ost (network | server_id | failover_id)*>\r
+<!ELEMENT ost (network_id | server_id | failover_id)*>\r
 <!ATTLIST ost %tag.attr;>\r
-<!ELEMENT mds (network | fstype | device | server_id | failover_id)*>\r
+<!ELEMENT mds (network_id | fstype | device | server_id | failover_id)*>\r
 <!ATTLIST mds %tag.attr;>\r
 \r
 <!ELEMENT osc (service_id)>\r
 \r
 <!-- basic elements -->\r
 <!ELEMENT network (server | port)*>\r
-<!ATTLIST network type (tcp | elan | myrinet) 'tcp'>\r
+<!ATTLIST network type (tcp | elan | myrinet) 'tcp'\r
+                  %tag.attr;>\r
+\r
+<!ELEMENT network_id    %tag.content;>\r
+<!ATTLIST network_id    %tag.attr;>\r
 \r
 <!ELEMENT fstype        %tag.content;>\r
 <!ELEMENT device        %tag.content;>\r