#!/usr/bin/env python # Copyright (C) 2002 Cluster File Systems, Inc. # Author: Robert Read # This file is part of Lustre, http://www.lustre.org. # # Lustre is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. # # Lustre 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 for more details. # # You should have received a copy of the GNU General Public License # along with Lustre; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # """ lmc - lustre configuration data manager See Lustre book (http://www.lustre.org/docs/lustre.pdf) 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" 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.append(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 --portals_upcall path --ptldebug debug_level --subsystem subsystem_name --add net --node node_name --nid nid --cluster_id --nettype tcp|elan|gm --hostaddr addr --port port --tcpbuf size --irq_affinity 0|1 --router --add mds --node node_name --mds mds_name --failover --dev path --backdev path --fstype ldiskfs|ext3 --backfstype ldiskfs|ext3|tmpfs --size size --nspath --journal_size size --inode_size size --lmv lmv_name --mkfsoptions options --mountfsoptions options --add lov --lov lov_name --mds mds_name --stripe_sz num --stripe_cnt num --stripe_pattern num --lmv lmv_name --add ost --node node_name --ost ost_name --failover --lov lov_name --index index --dev path --backdev path --size size --fstype ldiskfs|ext3 --backfstype ldiskfs|ext3|tmpfs --journal_size size --inode_size size --osdtype obdecho|obdfilter --ostuuid uuid --mkfsoptions options --mountfsoptions options --nspath --delete ost --node node_name --ost ost_name --deactivate ost --node node_name --ost ost_name --add mtpt - Mountpoint --node node_name --path /mnt/point --mds mds_name --lmv lmv_name --ost ost_name OR --lov lov_name --add route --node nodename --router --gw nid --gateway_cluster_id nid --target_cluster_id nid --lo nid --hi nid --add echo_client --node nodename --add mgmt - Management/monitoring service --node node_name --mgmt mgmt_service_name --add lmv --lmv lmv_name --add cobd --node node_name --real_obd obd_name --cache_obd obd_name --commit - Close a configuration version, and start a new one """ PARAM = Lustre.Options.PARAM 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), ('delete', "", PARAM), ('deactivate', "", PARAM), ('commit', "Commit all config changes and start a new version"), # 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), ('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.", PARAM), ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM), ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"), ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT), ('irq_affinity', "Optional argument.", PARAM, 0), ('hostaddr', "", PARAM,""), ('cluster_id', "Specify the cluster ID", PARAM, "0"), # 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"), ('failover', "Enable failover support on OSTs or MDS?"), ('group', "", PARAM), ('dev', "Path of the device on local system.", PARAM,""), ('backdev', "Path of the device for backing storage on local system.", PARAM,""), ('size', "Specify the size of the device if needed.", PARAM,"0"), ('journal_size', "Specify new journal size for underlying file system.", PARAM,"0"), ('inode_size', "Specify new inode size for underlying file system.", PARAM,"0"), ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"), ('backfstype', "Optional argument to specify the backing filesystem type.", PARAM, "ext3"), ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""), ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""), ('ostuuid', "", PARAM,""), ('nspath', "Local mount point of server namespace.", PARAM,""), ('format', ""), # clients: mountpoint and echo ('echo_client', "", PARAM), ('path', "Specify the mountpoint for Lustre.", PARAM), ('filesystem', "Lustre filesystem name", PARAM,""), # lov ('lov', "Specify LOV name.", PARAM,""), ('index', "Specify index for OBD in LOV target table.", PARAM), ('stripe_sz', "Specify the stripe size in bytes.", PARAM), ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0), ('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), ('cobd', "Specify COBD name", PARAM), ('mgmt', "Specify management/monitoring service name.", PARAM, ""), # lmv ('lmv', "Specify LMV name.", 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)) print "Warning: ", 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 network(self, name, uuid, nid, cluster_id, net, hostaddr="", port=0, tcpbuf=0, irq_aff=0): """create node""" network = self.newService("network", name, uuid) network.setAttribute("nettype", net); self.addElement(network, "nid", nid) self.addElement(network, "clusterid", cluster_id) if hostaddr: self.addElement(network, "hostaddr", hostaddr) if port: self.addElement(network, "port", "%d" %(port)) if tcpbuf: self.addElement(network, "sendmem", "%d" %(tcpbuf)) self.addElement(network, "recvmem", "%d" %(tcpbuf)) if irq_aff: self.addElement(network, "irqaffinity", "%d" %(irq_aff)) 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="", backfstype="", backdevname=""): 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 backfstype: self.addElement(osd, "backfstype", backfstype) if backdevname: self.addElement(osd, "backdevpath", backdevname) 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 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 lov_tgt(self, obd_uuid, index, generation): tgt = self.doc.createElement('lov_tgt') tgt.setAttribute("uuidref", obd_uuid) tgt.setAttribute("index", index) tgt.setAttribute("generation", generation) tgt.setAttribute("active", '1') return tgt def lovconfig(self, name, uuid, lov_uuid): lovconfig = self.newService("lovconfig", name, uuid) lovconfig.appendChild(self.ref("lov", lov_uuid)) return lovconfig def lmv(self, name, uuid): lmv = self.newService("lmv", name, uuid) return lmv def mds(self, name, uuid, mdd_uuid, group="", lmv=""): 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="", backfstype="", backdevname="", lmv_uuid=""): mdd = self.newService("mdsdev", name, uuid) self.addElement(mdd, "fstype", fstype) if backfstype: self.addElement(mdd, "backfstype", backfstype) dev = self.addElement(mdd, "devpath", devname) if backdevname: self.addElement(mdd, "backdevpath", backdevname) 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) mdd.appendChild(self.ref("node", node_uuid)) mdd.appendChild(self.ref("target", mds_uuid)) if lmv_uuid: mdd.appendChild(self.ref("lmv", lmv_uuid)) self.addElement(mdd, "lmv", lmv_uuid) return mdd def mgmt(self, mgmt_name, mgmt_uuid, node_uuid): mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid) mgmt.appendChild(self.ref("node", node_uuid)) # Placeholder until mgmt-service failover. mgmt.appendChild(self.ref("active", mgmt_uuid)) return mgmt def mountpoint(self, name, uuid, fs_uuid, path): mtpt = self.newService("mountpoint", name, uuid) mtpt.appendChild(self.ref("filesystem", fs_uuid)) self.addElement(mtpt, "path", path) return mtpt def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid): fs = self.newService("filesystem", name, uuid) fs.appendChild(self.ref("mds", mds_uuid)) fs.appendChild(self.ref("obd", obd_uuid)) if mgmt_uuid: fs.appendChild(self.ref("mgmt", mgmt_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 def update(self, version): new = self.doc.createElement("update") new.setAttribute("version", version) return new def add(self, lov, ost, index, gen): new = self.doc.createElement("add") new.setAttribute("lov_uuidref", lov) new.setAttribute("ost_uuidref", ost) new.setAttribute("index", index) new.setAttribute("generation", gen) return new def delete(self, lov, ost, index, gen, options): if options.delete: new = self.doc.createElement("delete") else: new = self.doc.createElement("deactivate") new.setAttribute("lov_uuidref", lov) new.setAttribute("ost_uuidref", ost) new.setAttribute("index", index) new.setAttribute("generation", gen) return new ############################################################ # 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 findLastUpdate(lustre): node = None version = 0 for n in lustre.childNodes: if n.nodeType == n.ELEMENT_NODE: if n.nodeName != 'update': continue tmp = int(n.getAttribute('version')) if not tmp: error('malformed XML: update tag without a version attribute') if tmp != version + 1: error('malformed XML: expecting update record '+str(version + 1)+', found '+str(tmp)+'.') version = tmp node = n return node def addUpdate(gen, lustre, node): update = findLastUpdate(lustre) if not update: return #add_record = update.getElementsByTagName('add') #if not add_record: # add_record = gen.add() # update.appendChild(add_record) #else: # add_record = add_record[0] #add_record.appendChild(node) update.appendChild(node) def delUpdate(gen, lustre, node): update = findLastUpdate(lustre) if not update: return update.appendChild(node) 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, lustre, lov, osc_uuid, options): lov_name = getName(lov) if options.index: lov_index = get_option_int(options, 'index') for tgt in lustre.getElementsByTagName('lov_tgt'): if str(lov_index) == tgt.getAttribute('index'): uuidref = tgt.getAttribute('uuidref') if uuidref != '': raise OptionError("%s --index %d is still in use: %s" % (lov_name, lov_index, uuidref)) tgt.setAttribute('uuidref', osc_uuid) gener = int(tgt.getAttribute('generation')) + 1 tgt.setAttribute('generation', str(gener)) addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), str(gener))) return lov.appendChild(gen.lov_tgt(osc_uuid, str(lov_index), '1')) addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), str(gener))) return index = -1 for tgt in lustre.getElementsByTagName('lov_tgt'): uuidref = tgt.getAttribute('uuidref') tmp = int(tgt.getAttribute('index')) if tmp != index + 1: error('malformed xml: LOV targets are not ordered; found index '+str(tmp)+', expected '+str(index + 1)+'.') index = tmp lov.appendChild(gen.lov_tgt(osc_uuid, str(index + 1), '1')) addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(index + 1), '1')) def lov_del_obd(gen, lustre, lov, osc_uuid, options): lov_name = getName(lov) if options.index: lov_index = get_option_int(options, 'index') for tgt in lustre.getElementsByTagName('lov_tgt'): index = tgt.getAttribute('index') if index == lov_index: uuidref = tgt.getAttribute('uuidref') if uuidref != osc_uuid: raise OptionError("%s --index %d contains %s, not %s" % (lov_name, lov_index, osc_uuid, uuidref)) if options.delete: tgt.setAttribute('uuidref', '') else: tgt.setAttribute('active', '0') # bump the generation just in case... gen = int(tgt.getAttribute('generation')) + 1 tgt.setAttribute('generation', str(gen)) return raise OptionError("%s --index %d not in use by %s." % (lov_name, lov_index, osc_uuid)) for tgt in lustre.getElementsByTagName('lov_tgt'): uuidref = tgt.getAttribute('uuidref') if uuidref == osc_uuid: genera = int(tgt.getAttribute('generation')) delete_rec = gen.delete(getUUID(lov), osc_uuid,tgt.getAttribute('index'), str(genera), options) delUpdate(gen, lustre, delete_rec) if options.delete: tgt.setAttribute('uuidref', '') else: tgt.setAttribute('active', '0') genera = genera + 1 tgt.setAttribute('generation', str(genera)) def lmv_add_obd(gen, lmv, mdc_uuid): lmv.appendChild(gen.ref("mds", mdc_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 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 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 ('tcp',): port = get_option_int(options, 'port') tcpbuf = get_option_int(options, 'tcpbuf') irq_aff = get_option_int(options, 'irq_affinity') elif net_type in ('elan', 'gm'): port = 0 tcpbuf = 0 irq_aff = 0 else: print "Unknown net_type: ", net_type sys.exit(2) 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, tcpbuf, irq_aff)) 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') lmv_name = get_option(options, 'lmv') mdd_name = new_name("MDD_" + mds_name +"_" + node_name) mdd_uuid = new_uuid(mdd_name) lmv_uuid = "" if lmv_name: lmv = findByName(lustre, lmv_name, "lmv") if not lmv: error('add_mds:', '"' + lmv_name + '"', "lmv element not found.") lmv_uuid = name2uuid(lustre, lmv_name, fatal=0) mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0) if not mds_uuid: mds_uuid = new_uuid(mds_name) mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group) lustre.appendChild(mds) if lmv_name: lmv_add_obd(gen, lmv, mds_uuid) else: mds = lookup(lustre, mds_uuid) if options.failover: mds.setAttribute('failover', "1") devname = get_option(options, 'dev') backdevname = get_option(options, 'backdev') size = get_option(options, 'size') fstype = get_option(options, 'fstype') backfstype = get_option(options, 'backfstype') 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') 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") if lmv_name: mds.appendChild(gen.ref("lmv", lmv_uuid)) 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, backfstype, backdevname, lmv_uuid) lustre.appendChild(mdd) def add_mgmt(gen, lustre, options): node_name = get_option(options, 'node') node_uuid = name2uuid(lustre, node_name, 'node') mgmt_name = get_option(options, 'mgmt') if not mgmt_name: mgmt_name = new_name('MGMT_' + node_name) mgmt_uuid = name2uuid(lustre, mgmt_name, 'mgmt', fatal=0) if not mgmt_uuid: mgmt_uuid = new_uuid(mgmt_name) mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid) lustre.appendChild(mgmt) else: mgmt = lookup(lustre, mgmt_uuid) node = findByName(lustre, node_name, "node") node_add_profile(gen, node, 'mgmt', mgmt_uuid) 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 = '' backfstype = '' devname = '' backdevname = '' size = 0 journal_size = '' inode_size = '' mkfsoptions = '' mountfsoptions = '' else: devname = get_option(options, 'dev') # can be unset for bluearcs backdevname = get_option(options, 'backdev') size = get_option(options, 'size') fstype = get_option(options, 'fstype') backfstype = get_option(options, 'backfstype') journal_size = get_option(options, 'journal_size') inode_size = get_option(options, 'inode_size') mkfsoptions = get_option(options, 'mkfsoptions') mountfsoptions = get_option(options, 'mountfsoptions') 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, lustre, lov, ost_uuid, options) else: ost = lookup(lustre, ost_uuid) if options.failover: ost.setAttribute('failover', "1") 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, backfstype, backdevname) 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 del_ost(gen, lustre, options): ostname = get_option(options, 'ost') if not ostname: raise OptionError("del_ost: --ost requires a ") ost = findByName(lustre, ostname, "ost") if not ost: error('del_ost: ', 'Unable to find ', ostname) ost_uuid = name2uuid(lustre, ostname, fatal=0) if not ost_uuid: error('del_ost: ', 'Unable to find uuid for ', ostname) lovname = get_option(options, 'lov') if lovname: lov = findByName(lustre, lovname, "lov") if not lov: error('del_ost:', '"'+lovname+'"', "lov element not found.") lov_del_obd(gen, lustre, lov, ost_uuid, options) # if the user specified a speficic LOV don't delete the OST itself return # remove OSD references from all LOVs for n in lustre.getElementsByTagName('lov'): lov_del_obd(gen, lustre, n, ost_uuid, options) return # delete the OSDs for osd in lustre.getElementsByTagName('osd'): if ref_exists(osd, ost_uuid): osd_uuid = osd.getAttribute('uuid') # delete all profile references to this OSD for profile in lustre.getElementsByTagName('profile'): for osd_ref in profile.getElementsByTagName('osd_ref'): if osd_uuid == osd_ref.getAttribute('uuidref'): profile.removeChild(osd_ref) lustre.removeChild(osd) # delete the OST lustre.removeChild(ost) def add_cobd(gen, lustre, options): node_name = get_option(options, 'node') name = get_option(options, 'cobd') 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='lov', fatal=0) cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0) if real_uuid: node = lookup(lustre, real_uuid) rets = node.getElementsByTagName('obd_ref') for ret in rets: ost_uuid = ret.getAttribute('uuidref') ost_node = lookup(lustre, ost_uuid) ret = ost_node.getElementsByTagName('active_ref') if ret: osd_uuid = ret[0].getAttribute('uuidref') osd_node = lookup(lustre, osd_uuid) gen.addElement(osd_node, 'cachetype', 'master') if cache_uuid: node = lookup(lustre, cache_uuid) rets = node.getElementsByTagName('obd_ref') for ret in rets: ost_uuid = ret.getAttribute('uuidref') ost_node = lookup(lustre, ost_uuid) ret = ost_node.getElementsByTagName('active_ref') if ret: osd_uuid = ret[0].getAttribute('uuidref') osd_node = lookup(lustre, osd_uuid) gen.addElement(osd_node, 'cachetype', 'cache') if not real_uuid or not cache_uuid: real_uuid = name2uuid(lustre,real_name, tag='mds') cache_uuid = name2uuid(lustre,cache_name, tag='mds') if real_uuid: mds_node = lookup(lustre, real_uuid) ret = mds_node.getElementsByTagName('active_ref') if ret: mdsdev_uuid = ret[0].getAttribute('uuidref') mdsdev_node = lookup(lustre, mdsdev_uuid) gen.addElement(mdsdev_node, 'cachetype', 'master') if cache_uuid: mds_node = lookup(lustre, cache_uuid) ret = mds_node.getElementsByTagName('active_ref') if ret: mdsdev_uuid = ret[0].getAttribute('uuidref') mdsdev_node = lookup(lustre, mdsdev_uuid) gen.addElement(mdsdev_node, 'cachetype', 'cache') node = findByName(lustre, node_name, "node") 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 """ lmv_name = get_option(options, 'lmv') 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') if not mds_name: if not lmv_name: error("LOV: MDS or LMV must be specified."); stripe_sz = get_option_int(options, 'stripe_sz') stripe_cnt = get_option_int(options, '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.") if not mds_name: mds_uuid = name2uuid(lustre, lmv_name, 'lmv') else: 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) if mds_name: mds = findByName(lustre, mds_name, "mds") mds.appendChild(gen.ref("lovconfig", lovconfig_uuid)) if lmv_name: lmv = findByName(lustre, lmv_name, "lmv") lmv.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 add_lmv(gen, lustre, options): """ create a lmv """ lmv_orig = get_option(options, 'lmv') name = new_name(lmv_orig) if name != lmv_orig: warning("name:", lmv_orig, "already used. using:", name) uuid = new_uuid(name) ret = findByName(lustre, name, "lmv") if ret: error("LMV: ", name, " already exists.") lmv = gen.lmv(name, uuid) lustre.appendChild(lmv) def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid): fs_name = new_name("FS_fsname") fs_uuid = new_uuid(fs_name) cobd = lookup(lustre, mds_uuid) #SHOULD appendChild filesystem to real mds not cobd ret = cobd.getElementsByTagName("cacheobd_ref") if ret: cacheobd_uuid = ret[0].getAttribute('uuidref') cacheobd = lookup(lustre, cacheobd_uuid) cacheobd.appendChild(gen.ref("filesystem", fs_uuid)) ret = cobd.getElementsByTagName("realobd_ref") if ret: realobd_uuid = ret[0].getAttribute('uuidref') realobd = lookup(lustre, realobd_uuid) realobd.appendChild(gen.ref("filesystem", fs_uuid)) else: cobd.appendChild(gen.ref("filesystem", fs_uuid)) fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid) lustre.appendChild(fs) return fs_uuid def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name): mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0) if not mds_uuid: mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0) if not mds_uuid: mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=1) obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0) if obd_uuid == '': obd_uuid = name2uuid(lustre, obd_name, tag='cobd') if mgmt_name: mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1) else: mgmt_uuid = '' fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid) if not fs_uuid: fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_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') 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 mds_name == '': mds_name = get_option(options, 'lmv') if mds_name == '': error("--add mtpt requires either --mds or --lmv.") 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, lustre, lov, ost_uuid, options) if fs_name == '': mgmt_name = get_option(options, 'mgmt') fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_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) 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) def commit_version(gen, lustre): update = findLastUpdate(lustre) if update: version = int(update.getAttribute("version")) + 1 else: version = 1 new = gen.update(str(version)) lustre.appendChild(new) ############################################################ # 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\\'"]+)""") #" fucking emacs. 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) elif devtype == 'mgmt': add_mgmt(gen, lustre, options) elif devtype == 'lmv': add_lmv(gen, lustre, options) else: error("unknown device type:", devtype) def delete(devtype, gen, lustre, options): if devtype == 'ost': del_ost(gen, lustre, options) elif options.delete: error("delete not supported for device type:", devtype) elif options.deactivate: error("deactivate not supported for device type:", devtype) else: error("in delete(), but neither .delete nor .deactivate are set. Tell CFS.") def commit(gen, lustre): commit_version(gen, lustre) def do_command(gen, lustre, options, args): if options.add: add(options.add, gen, lustre, options) elif options.delete: delete(options.delete, gen, lustre, options) elif options.deactivate: delete(options.deactivate, gen, lustre, options) elif options.commit: commit(gen, lustre) 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) if outFile == '-': printDoc(doc) else: printDoc(doc, open(outFile,"w")) if __name__ == "__main__": main()