#!/usr/bin/env python # GPL HEADER START # # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 only, # as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License version 2 for more details (a copy is included # in the LICENSE file that accompanied this code). # # You should have received a copy of the GNU General Public License # version 2 along with this program; If not, see # http://www.sun.com/software/products/lustre/docs/GPLv2.pdf # copy of GPLv2]. # # Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, # CA 95054 USA or visit www.sun.com if you need additional information or # have any questions. # # GPL HEADER END # # # Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. # Use is subject to license terms. # # # This file is part of Lustre, http://www.lustre.org/ # Lustre is a trademark of Sun Microsystems, Inc. # # Author: Robert Read """ lmc - lustre configuration data manager See the man page, or the Lustre Operations Manual, for documentation on lmc. """ import sys, os, getopt, string, exceptions, re import xml.dom.minidom def printDoc(doc, stream=sys.stdout): try: from xml.dom.ext import PrettyPrint PrettyPrint(doc, stream) except ImportError: stream.write(doc.toxml()) stream.write("\n") PYMOD_DIR = ["/usr/lib/lustre/python", "/usr/lib64/lustre/python"] def development_mode(): base = os.path.dirname(sys.argv[0]) if os.access(base+"/Makefile.am", os.R_OK): return 1 return 0 if not development_mode(): sys.path.extend(PYMOD_DIR) import Lustre DEFAULT_PORT = 988 DEFAULT_STRIPE_SZ = 1048576 DEFAULT_STRIPE_CNT = 1 DEFAULT_STRIPE_PATTERN = 0 UUID_MAX_LENGTH = 31 def reference(): print """usage: lmc --add object [object parameters] Object creation command summary: --add node --node node_name --timeout num --upcall path --lustre_upcall path --group_upcall path --portals_upcall path --ptldebug debug_level --subsystem subsystem_name --add net --node node_name --nid nid --cluster_id --nettype tcp|elan|gm|openib|iib|vib|ra|ptl|lnet --hostaddr ip[/netmask] --port port --tcpbuf size --irq_affinity 0|1 --router --add mds --node node_name --mds mds_name --dev path --fstype ldiskfs|ext3 --size size --nspath --group_upcall upcall --journal_size size --inode_size size --mdsuuid uuid --mkfsoptions options --mountfsoptions options --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune= --add lov --lov lov_name --mds mds_name --stripe_sz num --stripe_cnt num --stripe_pattern num --add ost --node node_name --ost ost_name --failout --failover --lov lov_name --dev path --size size --fstype ldiskfs|ext3 --journal_size size --inode_size size --osdtype obdecho|obdfilter --ostuuid uuid --mkfsoptions options --mountfsoptions options --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune= --add mtpt - Mountpoint --node node_name --path /mnt/point --mds mds_name --ost ost_name OR --lov lov_name --clientoptions options --add route --node nodename --router --gw nid --gateway_cluster_id nid --target_cluster_id nid --lo nid --hi nid --add echo_client --node nodename """ PARAM = Lustre.Options.PARAM PARAMLIST = Lustre.Options.PARAMLIST lmc_options = [ # lmc input/output options ('reference', "Print short reference for commands."), ('verbose,v', "Print system commands as they are run."), ('merge,m', "Append to the specified config file.", PARAM), ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM), ('input,i', "", PARAM), ('batch', "Used to execute lmc commands in batch mode.", PARAM), # commands ('add', "", PARAM), # node options ('node', "Add a new node in the cluster configuration.", PARAM), ('timeout', "Set timeout to initiate recovery.", PARAM), ('upcall', "Set both lustre and portals upcall scripts.", PARAM), ('lustre_upcall', "Set location of lustre upcall script.", PARAM), ('group_upcall', "Set location of extended group upcall script.", PARAM), ('portals_upcall', "Set location of portals upcall script.", PARAM), ('ptldebug', "Set the portals debug level", PARAM), ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log", PARAM), # network ('nettype', "Specify the network type. This can be tcp/elan/gm/openib/iib/vib/ra/ptl/lnet.", PARAM), ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM), ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT), ('hostaddr', "Optional argument to specify the host address.", PARAMLIST), ('cluster_id', "Specify the cluster ID", PARAM, "0"), ('nonet', "Skip the remote host networking check"), # routes ('route', "Add a new route for the cluster.", PARAM), ('router', "Optional flag to mark a node as router."), ('gw', "Specify the nid of the gateway for a route.", PARAM), ('gateway_cluster_id', "", PARAM, "0"), ('target_cluster_id', "", PARAM, "0"), ('lo', "For a range route, this is the low value nid.", PARAM), ('hi', "For a range route, this is a hi value nid.", PARAM,""), # servers: mds and ost ('mds', "Specify MDS name.", PARAM), ('ost', "Specify the OST name.", PARAM,""), ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"), ('failout', "Disable failover support on OST"), ('failover', "Enable failover support on OST"), ('group', "", PARAM), ('dev', "Path of the device on local system.", PARAM,""), ('size', "Specify the size of the device if needed.", PARAM,"0"), ('group_upcall', "Set location of supplementary group upcall.", PARAM,""), ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"), ('inode_size', "Specify new inode size for underlying ext3 file system.", PARAM,"0"), ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"), ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""), ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""), ('ostuuid', "Optional argument to specify OST UUID", PARAM,""), ('mdsuuid', "Optional argument to specify MDS UUID", PARAM,""), ('nspath', "Local mount point of server namespace.", PARAM,""), ('format', ""), ('quota', """ quotaon: enable quota, only u|g|ug is supported now. iunit: the unit for slave to acquire/release inode quota from/to master. Int type (>0), default value in Lustre is 5120 inodes. bunit: the unit for slave to acquire/release block quota from/to master. Mbytes (>0), default value in Lustre is 128(Mbytes). itune: used to tune the threthold. When inode quota usage reach the threthold, slave should acquire/release inode quota from/to master. Int type (100 > btune > 0), default value in Lustre is 50 (percentge). inode threthold = iunit * itune / 100. btune: used to tune the threthold. When block quota usage reach the threthold, slave should acquire/release block quota from/to master. Int type (100 > btune > 0), default value in Lustre is 50 (percentage). block threthold = bunit * btune / 100.""", PARAM,""), # clients: mountpoint and echo ('echo_client', "", PARAM), ('path', "Specify the mountpoint for Lustre.", PARAM), ('filesystem', "Lustre filesystem name", PARAM,""), ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""), # lov ('lov', "Specify LOV name.", PARAM,""), ('stripe_sz', "Specify the stripe size in bytes.", PARAM, DEFAULT_STRIPE_SZ), ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, DEFAULT_STRIPE_CNT), ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0), # cobd ('real_obd', "Specify the real device for the cache obd system.", PARAM), ('cache_obd', "Specify the cache device for the cache obd system.", PARAM), ] def error(*args): msg = string.join(map(str,args)) raise OptionError("Error: " + msg) def panic(cmd, msg): print "! " + cmd print msg sys.exit(1) def warning(*args): msg = string.join(map(str,args)) sys.stderr.write("WARNING: %s\n" % (msg)) def info(*args): msg = string.join(map(str,args)) sys.stderr.write("INFO: %s\n" % (msg)) # # 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 def new_name(base): ctr = 2 ret = base while names.has_key(ret): ret = "%s_%d" % (base, ctr) ctr = 1 + ctr names[ret] = 1 return ret def new_uuid(name): ctr = 2 ret = "%s_UUID" % (name) if len(ret) > UUID_MAX_LENGTH: ret = ret[-UUID_MAX_LENGTH:] while uuids.has_key(ret): ret = "%s_UUID_%d" % (name, ctr) ctr = 1 + ctr if len(ret) > UUID_MAX_LENGTH: ret = ret[-UUID_MAX_LENGTH:] uuids[ret] = 1 return ret ldlm_name = 'ldlm' ldlm_uuid = 'ldlm_UUID' def new_lustre(dom): """Create a new empty lustre document""" # adding ldlm here is a bit of a hack, but one is enough. str = """ """ % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid) return dom.parseString(str) names = {} uuids = {} def init_names(doc): """initialize auto-name generation tables""" global names, uuids # get all elements that contain a name attribute for n in doc.childNodes: if n.nodeType == n.ELEMENT_NODE: if getName(n): names[getName(n)] = 1 uuids[getUUID(n)] = 1 init_names(n) def get_format_flag(options): if options.format: return 'yes' return 'no' ############################################################ # Build config objects using DOM # class GenConfig: doc = None dom = None def __init__(self, doc): self.doc = doc def ref(self, type, uuid): """ generate <[type]_ref uuidref="[uuid]"/> """ tag = "%s_ref" % (type) ref = self.doc.createElement(tag) ref.setAttribute("uuidref", uuid) return ref def newService(self, tag, name, uuid): """ create a new service elmement, which requires name and uuid attributes """ new = self.doc.createElement(tag) new.setAttribute("uuid", uuid); new.setAttribute("name", name); return new def addText(self, node, str): txt = self.doc.createTextNode(str) node.appendChild(txt) def addElement(self, node, tag, str=None): """ create a new element and add it as a child to node. If str is passed, a text node is created for the new element""" new = self.doc.createElement(tag) if str: self.addText(new, str) node.appendChild(new) return new def recordtime(self, timestr): lustre = self.doc.getElementsByTagName("lustre") lustre[0].setAttribute("mtime", timestr) def network(self, name, uuid, nid, cluster_id, net, hostaddr="", port=0): """create node""" network = self.newService("network", name, uuid) network.setAttribute("nettype", net); self.addElement(network, "nid", nid) self.addElement(network, "clusterid", cluster_id) for host in hostaddr: self.addElement(network, "hostaddr", host) if port: self.addElement(network, "port", "%d" %(port)) return network def routetbl(self, name, uuid): """create node""" rtbl = self.newService("routetbl", name, uuid) return rtbl def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi): """ create one entry for the route table """ ref = self.doc.createElement('route') ref.setAttribute("type", gw_net_type) ref.setAttribute("gw", gw) ref.setAttribute("gwclusterid", gw_cluster_id) ref.setAttribute("tgtclusterid", tgt_cluster_id) ref.setAttribute("lo", lo) if hi: ref.setAttribute("hi", hi) return ref def profile(self, name, uuid): """ create a host """ profile = self.newService("profile", name, uuid) return profile def node(self, name, uuid, prof_uuid): """ create a host """ node = self.newService("node", name, uuid) node.appendChild(self.ref("profile", prof_uuid)) return node def ldlm(self, name, uuid): """ create a ldlm """ ldlm = self.newService("ldlm", name, uuid) return ldlm def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid, node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", mkfsoptions="", mountfsoptions="", quota=""): osd = self.newService("osd", name, uuid) osd.setAttribute('osdtype', osdtype) osd.appendChild(self.ref("target", ost_uuid)) osd.appendChild(self.ref("node", node_uuid)) if fstype: self.addElement(osd, "fstype", fstype) if devname: dev = self.addElement(osd, "devpath", devname) self.addElement(osd, "autoformat", format) if dev_size: self.addElement(osd, "devsize", "%s" % (dev_size)) if journal_size: self.addElement(osd, "journalsize", "%s" % (journal_size)) if inode_size: self.addElement(osd, "inodesize", "%s" % (inode_size)) if mkfsoptions: self.addElement(osd, "mkfsoptions", mkfsoptions) if mountfsoptions: self.addElement(osd, "mountfsoptions", mountfsoptions) if quota: self.addElement(osd, "quota", quota) if nspath: self.addElement(osd, "nspath", nspath) return osd def cobd(self, name, uuid, real_uuid, cache_uuid): cobd = self.newService("cobd", name, uuid) cobd.appendChild(self.ref("realobd",real_uuid)) cobd.appendChild(self.ref("cacheobd",cache_uuid)) return cobd def ost(self, name, uuid, osd_uuid, group=""): ost = self.newService("ost", name, uuid) ost.appendChild(self.ref("active", osd_uuid)) if group: self.addElement(ost, "group", group) return ost def oss(self, name, uuid): oss = self.newService("oss", name, uuid) return oss def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern): lov = self.newService("lov", name, uuid) lov.appendChild(self.ref("mds", mds_uuid)) lov.setAttribute("stripesize", str(stripe_sz)) lov.setAttribute("stripecount", str(stripe_cnt)) lov.setAttribute("stripepattern", str(pattern)) return lov def lovconfig(self, name, uuid, lov_uuid): lovconfig = self.newService("lovconfig", name, uuid) lovconfig.appendChild(self.ref("lov", lov_uuid)) return lovconfig def mds(self, name, uuid, mdd_uuid, group=""): mds = self.newService("mds", name, uuid) mds.appendChild(self.ref("active",mdd_uuid)) if group: self.addElement(mds, "group", group) return mds def mdsdev(self, name, uuid, fstype, devname, format, node_uuid, mds_uuid, dev_size=0, journal_size=0, inode_size=256, nspath="", mkfsoptions="", mountfsoptions="", quota="", group_upcall=""): mdd = self.newService("mdsdev", name, uuid) self.addElement(mdd, "fstype", fstype) dev = self.addElement(mdd, "devpath", devname) self.addElement(mdd, "autoformat", format) if dev_size: self.addElement(mdd, "devsize", "%s" % (dev_size)) if journal_size: self.addElement(mdd, "journalsize", "%s" % (journal_size)) if inode_size: self.addElement(mdd, "inodesize", "%s" % (inode_size)) if nspath: self.addElement(mdd, "nspath", nspath) if mkfsoptions: self.addElement(mdd, "mkfsoptions", mkfsoptions) if mountfsoptions: self.addElement(mdd, "mountfsoptions", mountfsoptions) if quota: self.addElement(mdd, "quota", quota) if group_upcall: self.addElement(mdd, "group_upcall", group_upcall) mdd.appendChild(self.ref("node", node_uuid)) mdd.appendChild(self.ref("target", mds_uuid)) return mdd def mountpoint(self, name, uuid, fs_uuid, path, clientoptions): mtpt = self.newService("mountpoint", name, uuid) mtpt.appendChild(self.ref("filesystem", fs_uuid)) self.addElement(mtpt, "path", path) if clientoptions: self.addElement(mtpt, "clientoptions", clientoptions) return mtpt def filesystem(self, name, uuid, mds_uuid, obd_uuid): fs = self.newService("filesystem", name, uuid) fs.appendChild(self.ref("mds", mds_uuid)) fs.appendChild(self.ref("obd", obd_uuid)) return fs def echo_client(self, name, uuid, osc_uuid): ec = self.newService("echoclient", name, uuid) ec.appendChild(self.ref("obd", osc_uuid)) return ec ############################################################ # Utilities to query a DOM tree # Using this functions we can treat use config information # directly as a database. def getName(n): return n.getAttribute('name') def getUUID(node): return node.getAttribute('uuid') def findByName(lustre, name, tag = ""): for n in lustre.childNodes: if n.nodeType == n.ELEMENT_NODE: if tag and n.nodeName != tag: continue if getName(n) == name: return n else: n = findByName(n, name) if n: return n return None def lookup(node, uuid): for n in node.childNodes: if n.nodeType == n.ELEMENT_NODE: if getUUID(n) == uuid: return n else: n = lookup(n, uuid) if n: return n return None def name2uuid(lustre, name, tag="", fatal=1): ret = findByName(lustre, name, tag) if not ret: if fatal: error('name2uuid:', '"'+name+'"', tag, 'element not found.') else: return "" return getUUID(ret) def lookup_filesystem(lustre, mds_uuid, ost_uuid): for n in lustre.childNodes: if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem': if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid): return getUUID(n) return None # XXX: assumes only one network element per node. will fix this # as soon as support for routers is added def get_net_uuid(lustre, node_name): """ get a network uuid for a node_name """ node = findByName(lustre, node_name, "node") if not node: error ('get_net_uuid:', '"'+node_name+'"', "node element not found.") net = node.getElementsByTagName('network') if net: return getUUID(net[0]) return None def lov_add_obd(gen, lov, osc_uuid): lov.appendChild(gen.ref("obd", osc_uuid)) def ref_exists(profile, uuid): elist = profile.childNodes for e in elist: if e.nodeType == e.ELEMENT_NODE: ref = e.getAttribute('uuidref') if ref == uuid: return 1 return 0 # ensure that uuid is not already in the profile # return true if uuid is added def node_add_profile(gen, node, ref, uuid): refname = "%s_ref" % "profile" ret = node.getElementsByTagName(refname) if not ret: error('node has no profile ref:', node) prof_uuid = ret[0].getAttribute('uuidref') profile = lookup(node.parentNode, prof_uuid) if not profile: error("no profile found:", prof_uuid) if ref_exists(profile, uuid): return 0 profile.appendChild(gen.ref(ref, uuid)) return 1 def get_attr(dom_node, attr, default=""): v = dom_node.getAttribute(attr) if v: return v return default ############################################################ # Top level commands # def runcmd(cmd): f = os.popen(cmd) ret = f.close() if ret: ret = ret >> 8 else: ret = 0 return ret def set_node_options(gen, node, options): if options.router: node.setAttribute('router', '1') if options.timeout: gen.addElement(node, "timeout", get_option(options, 'timeout')) if options.upcall: default_upcall = get_option(options, 'upcall') else: default_upcall = '' if default_upcall or options.lustre_upcall: if options.lustre_upcall: gen.addElement(node, 'lustreUpcall', options.lustre_upcall) else: gen.addElement(node, 'lustreUpcall', default_upcall) if options.group_upcall: gen.addElement(node, 'groupUpcall', options.group_upcall) if default_upcall or options.portals_upcall: if options.portals_upcall: gen.addElement(node, 'portalsUpcall', options.portals_upcall) else: gen.addElement(node, 'portalsUpcall', default_upcall) if options.ptldebug: gen.addElement(node, "ptldebug", get_option(options, 'ptldebug')) if options.subsystem: gen.addElement(node, "subsystem", get_option(options, 'subsystem')) return node def do_add_node(gen, lustre, options, node_name): uuid = new_uuid(node_name) prof_name = new_name("PROFILE_" + node_name) prof_uuid = new_uuid(prof_name) profile = gen.profile(prof_name, prof_uuid) node = gen.node(node_name, uuid, prof_uuid) lustre.appendChild(node) lustre.appendChild(profile) node_add_profile(gen, node, 'ldlm', ldlm_uuid) set_node_options(gen, node, options) return node def add_node(gen, lustre, options): """ create a node with a network config """ node_name = get_option(options, 'node') ret = findByName(lustre, node_name, "node") if ret: print "Node:", node_name, "exists." return do_add_node(gen, lustre, options, node_name) def add_net(gen, lustre, options): """ create a node with a network config """ node_name = get_option(options, 'node') nid = get_option(options, 'nid') cluster_id = get_option(options, 'cluster_id') hostaddr = get_option(options, 'hostaddr') net_type = get_option(options, 'nettype') if net_type in ('lnet','tcp','openib','ra'): port = get_option_int(options, 'port') elif net_type in ('elan','gm','iib','vib','lo','ptl'): port = 0 else: print "Unknown net_type: ", net_type sys.exit(2) real_net_type = net_type if net_type == 'lnet' and string.find(nid,'@') > 0: real_net_type = string.split(nid,'@')[1] # testing network if options.nonet: if options.verbose: print "Skipping the remote host networking test." elif (real_net_type == 'tcp') and (nid != '*'): if options.verbose: print "Testing network on", node_name target = string.split(nid,'@')[0] if target != '*' and target != '\\*': out = runcmd("ping -c 1 -w 5 %s" %target) if out != 0: print "Could not connect to %s, please check network." % node_name ret = findByName(lustre, node_name, "node") if not ret: node = do_add_node(gen, lustre, options, node_name) else: node = ret set_node_options(gen, node, options) net_name = new_name('NET_'+ node_name +'_'+ net_type) net_uuid = new_uuid(net_name) node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type, hostaddr, port)) node_add_profile(gen, node, "network", net_uuid) def add_route(gen, lustre, options): """ create a node with a network config """ node_name = get_option(options, 'node') gw_net_type = get_option(options, 'nettype') gw = get_option(options, 'gw') gw_cluster_id = get_option(options, 'gateway_cluster_id') tgt_cluster_id = get_option(options, 'target_cluster_id') lo = get_option(options, 'lo') hi = get_option(options, 'hi') if not hi: hi = lo node = findByName(lustre, node_name, "node") if not node: error (node_name, " not found.") rlist = node.getElementsByTagName('routetbl') if len(rlist) > 0: rtbl = rlist[0] else: rtbl_name = new_name("RTBL_" + node_name) rtbl_uuid = new_uuid(rtbl_name) rtbl = gen.routetbl(rtbl_name, rtbl_uuid) node.appendChild(rtbl) node_add_profile(gen, node, "routetbl", rtbl_uuid) rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi)) def add_mds(gen, lustre, options): node_name = get_option(options, 'node') mds_name = get_option(options, 'mds') mdd_name = new_name("MDD_" + mds_name +"_" + node_name) mdd_uuid = new_uuid(mdd_name) mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0) if not mds_uuid: mds_uuid = get_option(options, 'mdsuuid') if mds_uuid: if lookup(lustre, mds_uuid): error("Duplicate MDS UUID:", mds_uuid) else: mds_uuid = new_uuid(mds_name) mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group) lustre.appendChild(mds) else: mds = lookup(lustre, mds_uuid) if options.failover: mds.setAttribute('failover', "1") if options.failout: mds.setAttribute('failover,',"0") devname = get_option(options, 'dev') size = get_option(options, 'size') fstype = get_option(options, 'fstype') journal_size = get_option(options, 'journal_size') inode_size = get_option(options, 'inode_size') nspath = get_option(options, 'nspath') mkfsoptions = get_option(options, 'mkfsoptions') mountfsoptions = get_option(options, 'mountfsoptions') quota = get_option(options, 'quota') group_upcall = get_option(options, 'group_upcall') node_uuid = name2uuid(lustre, node_name, 'node') node = findByName(lustre, node_name, "node") node_add_profile(gen, node, "mdsdev", mdd_uuid) net_uuid = get_net_uuid(lustre, node_name) if not net_uuid: error("NODE: ", node_name, "not found") mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname, get_format_flag(options), node_uuid, mds_uuid, size, journal_size, inode_size, nspath, mkfsoptions, mountfsoptions, quota, group_upcall) lustre.appendChild(mdd) def add_ost(gen, lustre, options): node_name = get_option(options, 'node') lovname = get_option(options, 'lov') osdtype = get_option(options, 'osdtype') node_uuid = name2uuid(lustre, node_name, 'node') if osdtype == 'obdecho': fstype = '' devname = '' size = 0 fstype = '' journal_size = '' inode_size = '' mkfsoptions = '' mountfsoptions = '' quota = '' else: devname = get_option(options, 'dev') # can be unset for bluearcs size = get_option(options, 'size') fstype = get_option(options, 'fstype') journal_size = get_option(options, 'journal_size') inode_size = get_option(options, 'inode_size') mkfsoptions = get_option(options, 'mkfsoptions') mountfsoptions = get_option(options, 'mountfsoptions') quota = get_option(options, 'quota') nspath = get_option(options, 'nspath') ostname = get_option(options, 'ost') if not ostname: ostname = new_name('OST_'+ node_name) osdname = new_name("OSD_" + ostname + "_" + node_name) osd_uuid = new_uuid(osdname) ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0) if not ost_uuid: ost_uuid = get_option(options, 'ostuuid') if ost_uuid: if lookup(lustre, ost_uuid): error("Duplicate OST UUID:", ost_uuid) else: ost_uuid = new_uuid(ostname) ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group) lustre.appendChild(ost) if lovname: lov = findByName(lustre, lovname, "lov") if not lov: error('add_ost:', '"'+lovname+'"', "lov element not found.") lov_add_obd(gen, lov, ost_uuid) else: ost = lookup(lustre, ost_uuid) if options.failover: ost.setAttribute('failover', "1") if options.failout: ost.setAttribute('failover', "0") osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname, get_format_flag(options), ost_uuid, node_uuid, size, journal_size, inode_size, nspath, mkfsoptions, mountfsoptions, quota) node = findByName(lustre, node_name, "node") ## if node_add_profile(gen, node, 'oss', oss_uuid): ## ossname = 'OSS' ## oss_uuid = new_uuid(ossname) ## oss = gen.oss(ossname, oss_uuid) ## lustre.appendChild(oss) node_add_profile(gen, node, 'osd', osd_uuid) lustre.appendChild(osd) def add_cobd(gen, lustre, options): node_name = get_option(options, 'node') name = new_name('COBD_' + node_name) uuid = new_uuid(name) real_name = get_option(options, 'real_obd') cache_name = get_option(options, 'cache_obd') real_uuid = name2uuid(lustre, real_name, tag='obd') cache_uuid = name2uuid(lustre, cache_name, tag='obd') node = findByName(lustre, node_name, "node") node_add_profile(gen, node, "cobd", uuid) cobd = gen.cobd(name, uuid, real_uuid, cache_uuid) lustre.appendChild(cobd) def add_echo_client(gen, lustre, options): """ add an echo client to the profile for this node. """ node_name = get_option(options, 'node') lov_name = get_option(options, 'ost') node = findByName(lustre, node_name, 'node') echoname = new_name('ECHO_'+ node_name) echo_uuid = new_uuid(echoname) node_add_profile(gen, node, 'echoclient', echo_uuid) lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0) if not lov_uuid: lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1) echo = gen.echo_client(echoname, echo_uuid, lov_uuid) lustre.appendChild(echo) def add_lov(gen, lustre, options): """ create a lov """ lov_orig = get_option(options, 'lov') name = new_name(lov_orig) if name != lov_orig: warning("name:", lov_orig, "already used. using:", name) mds_name = get_option(options, 'mds') stripe_sz = get_option_int(options, 'stripe_sz') stripe_cnt = get_option_int(options, 'stripe_cnt') if stripe_cnt == 0: info("default stripe count (0) - will use %d stripe(s) per file" \ % DEFAULT_STRIPE_CNT) pattern = get_option_int(options, 'stripe_pattern') uuid = new_uuid(name) ret = findByName(lustre, name, "lov") if ret: error("LOV: ", name, " already exists.") mds_uuid = name2uuid(lustre, mds_name, 'mds') lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern) lustre.appendChild(lov) # add an lovconfig entry to the active mdsdev profile lovconfig_name = new_name('LVCFG_' + name) lovconfig_uuid = new_uuid(lovconfig_name) mds = findByName(lustre, mds_name, "mds") mds.appendChild(gen.ref("lovconfig", lovconfig_uuid)) lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid) lustre.appendChild(lovconfig) def add_default_lov(gen, lustre, mds_name, lov_name): """ create a default lov """ stripe_sz = DEFAULT_STRIPE_SZ stripe_cnt = DEFAULT_STRIPE_CNT pattern = DEFAULT_STRIPE_PATTERN uuid = new_uuid(lov_name) ret = findByName(lustre, lov_name, "lov") if ret: error("LOV: ", lov_name, " already exists.") mds_uuid = name2uuid(lustre, mds_name, 'mds') lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern) lustre.appendChild(lov) # add an lovconfig entry to the active mdsdev profile lovconfig_name = new_name('LVCFG_' + lov_name) lovconfig_uuid = new_uuid(lovconfig_name) mds = findByName(lustre, mds_name) mds.appendChild(gen.ref("lovconfig", lovconfig_uuid)) lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid) lustre.appendChild(lovconfig) def new_filesystem(gen, lustre, mds_uuid, obd_uuid): fs_name = new_name("FS_fsname") fs_uuid = new_uuid(fs_name) mds = lookup(lustre, mds_uuid) mds.appendChild(gen.ref("filesystem", fs_uuid)) fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid) lustre.appendChild(fs) return fs_uuid def get_fs_uuid(gen, lustre, mds_name, obd_name): mds_uuid = name2uuid(lustre, mds_name, tag='mds') obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0) fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid) if not fs_uuid: fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid) return fs_uuid def add_mtpt(gen, lustre, options): """ create mtpt on a node """ node_name = get_option(options, 'node') path = get_option(options, 'path') clientoptions = get_option(options, "clientoptions") fs_name = get_option(options, 'filesystem') lov_name = get_option(options, 'lov') ost_name = get_option(options, 'ost') mds_name = get_option(options, 'mds') if lov_name == '': if ost_name == '': error("--add mtpt requires --lov lov_name or --ost ost_name") else: warning("use default value for lov, due no --lov lov_name provided") lov_name = new_name("lov_default") add_default_lov(gen, lustre, mds_name, lov_name) ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0) if not ost_uuid: error('add_mtpt:', '"'+ost_name+'"', "ost element not found.") lov = findByName(lustre, lov_name, "lov") lov_add_obd(gen, lov, ost_uuid) if fs_name == '': fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name) else: fs_uuid = name2uuid(lustre, fs_name, tag='filesystem') name = new_name('MNT_'+ node_name) ret = findByName(lustre, name, "mountpoint") if ret: # this can't happen, because new_name creates unique names error("MOUNTPOINT: ", name, " already exists.") uuid = new_uuid(name) mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions) node = findByName(lustre, node_name, "node") if not node: error('node:', node_name, "not found.") node_add_profile(gen, node, "mountpoint", uuid) lustre.appendChild(mtpt) ############################################################ # Command line processing # class OptionError (exceptions.Exception): def __init__(self, args): self.args = args def get_option(options, tag): """Look for tag in options hash and return the value if set. If not set, then if return default it is set, otherwise exception.""" if options.__getattr__(tag) != None: return options.__getattr__(tag) else: raise OptionError("--add %s requires --%s " % (options.add, tag)) def get_option_int(options, tag): """Return an integer option. Raise exception if the value is not an int""" val = get_option(options, tag) try: n = int(val) except ValueError: raise OptionError("--%s (value must be integer)" % (tag)) return n # simple class for profiling import time class chrono: def __init__(self): self._start = 0 def start(self): self._stop = 0 self._start = time.time() def stop(self, msg=''): self._stop = time.time() if msg: self.display(msg) def dur(self): return self._stop - self._start def display(self, msg): d = self.dur() str = '%s: %g secs' % (msg, d) print str ################################################################# # function cmdlinesplit used to split cmd line from batch file # def cmdlinesplit(cmdline): double_quote = re.compile(r'"(([^"\\]|\\.)*)"') single_quote = re.compile(r"'(.*?)'") escaped = re.compile(r'\\(.)') esc_quote = re.compile(r'\\([\\"])') outside = re.compile(r"""([^\s\\'"]+)""") arg_list = [] i = 0; arg = None while i < len(cmdline): c = cmdline[i] if c == '"': match = double_quote.match(cmdline, i) if not match: print "Unmatched double quote:", cmdline sys.exit(1) i = match.end() if arg is None: arg = esc_quote.sub(r'\1', match.group(1)) else: arg = arg + esc_quote.sub(r'\1', match.group(1)) elif c == "'": match = single_quote.match(cmdline, i) if not match: print "Unmatched single quote:", cmdline sys.exit(1) i = match.end() if arg is None: arg = match.group(1) else: arg = arg + match.group(1) elif c == "\\": match = escaped.match(cmdline, i) if not match: print "Unmatched backslash", cmdline sys.exit(1) i = match.end() if arg is None: arg = match.group(1) else: arg = arg + match.group(1) elif c in string.whitespace: if arg != None: arg_list.append(str(arg)) arg = None while i < len(cmdline) and cmdline[i] in string.whitespace: i = i + 1 else: match = outside.match(cmdline, i) assert match i = match.end() if arg is None: arg = match.group() else: arg = arg + match.group() if arg != None: arg_list.append(str(arg)) return arg_list ############################################################ # Main # def add(devtype, gen, lustre, options): if devtype == 'net': add_net(gen, lustre, options) elif devtype == 'mtpt': add_mtpt(gen, lustre, options) elif devtype == 'mds': add_mds(gen, lustre, options) elif devtype == 'ost': add_ost(gen, lustre, options) elif devtype == 'lov': add_lov(gen, lustre, options) elif devtype == 'route': add_route(gen, lustre, options) elif devtype == 'node': add_node(gen, lustre, options) elif devtype == 'echo_client': add_echo_client(gen, lustre, options) elif devtype == 'cobd': add_cobd(gen, lustre, options) else: error("unknown device type:", devtype) def do_command(gen, lustre, options, args): if options.add: add(options.add, gen, lustre, options) else: error("Missing command") def main(): cl = Lustre.Options("lmc", "", lmc_options) try: options, args = cl.parse(sys.argv[1:]) except Lustre.OptionError, e: panic("lmc", e) if len(args) > 0: panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args)) if options.reference: reference() sys.exit(0) outFile = '-' if options.merge: outFile = options.merge if os.access(outFile, os.R_OK): doc = xml.dom.minidom.parse(outFile) else: doc = new_lustre(xml.dom.minidom) elif options.input: doc = xml.dom.minidom.parse(options.input) else: doc = new_lustre(xml.dom.minidom) if options.output: outFile = options.output lustre = doc.documentElement init_names(lustre) if lustre.tagName != "lustre": print "Existing config not valid." sys.exit(1) gen = GenConfig(doc) if options.batch: fp = open(options.batch) batchCommands = fp.readlines() fp.close() for cmd in batchCommands: try: options, args = cl.parse(cmdlinesplit(cmd)) if options.merge or options.input or options.output: print "The batchfile should not contain --merge, --input or --output." sys.exit(1) do_command(gen, lustre, options, args) except OptionError, e: panic(cmd, e) except Lustre.OptionError, e: panic(cmd, e) else: try: do_command(gen, lustre, options, args) except OptionError, e: panic(string.join(sys.argv),e) except Lustre.OptionError, e: panic("lmc", e) #record timestamp timestr = string.split(str(time.time()), '.') gen.recordtime(timestr[0]) if outFile == '-': printDoc(doc) else: printDoc(doc, open(outFile,"w")) if __name__ == "__main__": main()