#!/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 configurtion data manager Basic plan for lmc usage: # create nodes ./lmc --output config.xml --node server --net server1 tcp ./lmc --merge config.xml --node client --net client1 tcp # configure server ./lmc --merge config.xml --node server --mds /tmp/mds1 50000 ./lmc --merge config.xml --node server --mdc MDC_server # create lov ./lmc --merge config.xml --lov lov1 MDC_server 65536 0 0 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost1 100000 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost2 100000 # create client config ./lmc --merge config.xml --node client --mtpt /mnt/lustre MDC_server lov1 """ import sys, getopt, string import xml.dom.minidom from xml.dom.ext import PrettyPrint from xml.xpath import Evaluate DEFAULT_PORT = 888 # XXX What is the right default acceptor port to use? def usage(): print """usage: lmc [--node --ost | --mtpt | --lov] args Commands: --node node_name Node_name by itself it will create a new node. When used with other commands it specifies the node to modify --net hostname nettype [port, recv_buf, send_buf] Nettype is either tcp, elan, or gm. Requires a node argument --lov lov_name [mdc_name stripe_sz stripe_off pattern] Creates a logical volume When used with other commands, it specifics the lov to modify --mds device [size] Create a MDS using the device Requires --node --mdc mdc_name Configures a MDC for a node. Requires --node --ost device [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. Requires --node If --lov lov_name is used, this device is added to lov. --mtpt /mnt/point mdc_name lov_name|osc_name Creates a client mount point. Requires --node 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) """ sys.exit(1) def error(*args): msg = string.join(map(str,args)) print msg sys.exit(1) # # 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 get_uuid(name): return "%s_UUID" % (name) 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 = """ """ % (ldlm_name, ldlm_uuid) return dom.parseString(str) names = {} uuids = {} def init_names(lustre): """initialize auto-name generation tables""" global names, uuids # get all elements that contain a name attribute for n in Evaluate("//@name/..", lustre): names[n.getAttribute("name")] = 1 uuids[n.getAttribute("uuid")] = 1 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("name", name); new.setAttribute("uuid", uuid); 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, hostname, net, port=0): """create node""" network = self.newService("network", name, uuid) network.setAttribute("type", net); self.addElement(network, "server", hostname) if port: self.addElement(network, "port", "%d" %(port)) return network def node(self, name, uuid): """ create a host """ node = self.newService("node", name, uuid) self.addElement(node, 'profile') return node def ldlm(self, name, uuid): """ create a ldlm """ ldlm = self.newService("ldlm", name, uuid) return ldlm def obd(self, name, uuid, fs, devname, format, dev_size=0): obd = self.newService("obd", name, uuid) obd.setAttribute('type', 'obdfilter') self.addElement(obd, "fstype", fs) dev = self.addElement(obd, "device", devname) if (dev_size): dev.setAttribute("size", "%s" % (dev_size)) self.addElement(obd, "autoformat", format) return obd def osc(self, name, uuid, obd_uuid, net_uuid): osc = self.newService("osc", name, uuid) osc.appendChild(self.ref("ost", net_uuid)) osc.appendChild(self.ref("obd", obd_uuid)) return osc def ost(self, name, uuid, obd_uuid, net_uuid): ost = self.newService("ost", name, uuid) ost.appendChild(self.ref("network", net_uuid)) ost.appendChild(self.ref("obd", obd_uuid)) return ost def lov(self, name, uuid, mdc_uuid, stripe_sz, stripe_off, pattern): lov = self.newService("lov", name, uuid) lov.appendChild(self.ref("mdc", mdc_uuid)) devs = self.addElement(lov, "devices" ) devs.setAttribute("stripesize", stripe_sz) devs.setAttribute("stripeoffset", stripe_off) devs.setAttribute("pattern", pattern) return lov def mds(self, name, uuid, fs, devname, format, net_uuid, failover_uuid = "", dev_size=0 ): mds = self.newService("mds", name, uuid) self.addElement(mds, "fstype", fs) dev = self.addElement(mds, "device", devname) if dev_size: dev.setAttribute("size", "%s" % (dev_size)) self.addElement(mds, "autoformat", format) mds.appendChild(self.ref("network", net_uuid)) if failover_uuid: mds.appendChild(self.ref("failover", failover_uuid)) return mds def mdc(self, name, uuid, mds_uuid): mdc = self.newService("mdc", name, uuid) mdc.appendChild(self.ref("mds", mds_uuid)) return mdc def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path): mtpt = self.newService("mountpoint", name, uuid) mtpt.appendChild(self.ref("mdc", mdc_uuid)) mtpt.appendChild(self.ref("osc", osc_uuid)) self.addElement(mtpt, "path", path) return mtpt def findByName(lustre, name, tag = "*"): path = '//%s[@name="%s"]' % (tag, name) ret = Evaluate(path, lustre) if ret: return ret[0] 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 ("node not found:", node_name) net = Evaluate("./network", node) if net: return net[0].getAttribute("uuid") return None def lov_add_osc(gen, lov, osc_uuid): devs = Evaluate("devices", lov) if len(devs) == 1: devs[0].appendChild(gen.ref("osc", osc_uuid)) else: error("No devices element found for LOV:", lov) def node_add_profile(gen, node, ref, uuid): ret = Evaluate("./profile", node) if not ret: error('node has no profile:', node) ret[0].appendChild(gen.ref(ref, uuid)) # # Create a new obd, osc, and ost. Add them to the DOM. # def add_ost(gen, lustre, options, args): if len(args) < 1: usage() if options.has_key('node'): node_name = options['node'] else: error("--ost requires a --node argument") if options.has_key('lov'): lovname = options['lov'] else: lovname = '' devname = args[0] if len(args) > 1: size = args[1] else: size = 0 obdname = new_name('OBD_'+ node_name) oscname = new_name('OSC_'+ node_name) ostname = new_name('OST_'+ node_name) obd_uuid = get_uuid(obdname) ost_uuid = get_uuid(ostname) osc_uuid = get_uuid(oscname) net_uuid = get_net_uuid(lustre, node_name) if not net_uuid: error("NODE: ", node_name, "not found") obd = gen.obd(obdname, obd_uuid, "extN", devname, "no", size) ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid) osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid) if lovname: lov = findByName(lustre, lovname, "lov") if not lov: error("LOV:", lovname, "not found.") lov_add_osc(gen, lov, osc_uuid) node = findByName(lustre, node_name, "node") node_add_profile(gen, node, 'obd', obd_uuid) node_add_profile(gen, node, 'ost', ost_uuid) lustre.appendChild(obd) lustre.appendChild(osc) lustre.appendChild(ost) def add_net(gen, lustre, options, args): """ create a node with a network config """ if len(args) < 2: usage() node_name = options['node'] nid = args[0] net_type = args[1] if net_type == 'tcp': if len(args) > 2: port = int(args[2]) else: port = DEFAULT_PORT # add send, recv buffer size here elif net_type in ('elan', 'gm'): port = 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, node_name) else: node = ret net_name = new_name('NET_'+ node_name +'_'+ net_type) net_uuid = get_uuid(net_name) node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port)) node_add_profile(gen, node, "network", net_uuid) def do_add_node(gen, lustre, node_name): uuid = get_uuid(node_name) node = gen.node(node_name, uuid) node_add_profile(gen, node, 'ldlm', ldlm_uuid) lustre.appendChild(node) return node def add_node(gen, lustre, options, args): """ create a node with a network config """ if len(args) > 1: usage() node_name = options['node'] ret = findByName(lustre, node_name, "node") if ret: print "Node:", node_name, "exists." return do_add_node(gen, lustre, node_name) def add_lov(gen, lustre, options, args): """ create a lov """ if len(args) < 4: usage() name = options['lov'] mdc_name = args[0] stripe_sz = args[1] stripe_off = args[2] pattern = args[3] ret = findByName(lustre, name, "lov") if ret: error("LOV: ", name, " already exists.") ret = findByName(lustre, mdc_name, "mdc") if not ret: error(mdc_name, "not found.") mdc_uuid = ret.getAttribute("uuid") uuid = get_uuid(name) lov = gen.lov(name, uuid, mdc_uuid, stripe_sz, stripe_off, pattern) lustre.appendChild(lov) def add_mtpt(gen, lustre, options, args): """ create mtpt on a node """ if len(args) < 3: usage() if options.has_key('node'): node_name = options['node'] else: error("--mtpt requires a --node argument") path = args[0] mdc_name = args[1] lov_name = args[2] name = new_name('MNT_'+ node_name) ret = findByName(lustre, name, "mountpoint") if ret: error("MOUNTPOINT: ", name, " already exists.") ret = findByName(lustre, lov_name, "lov") if not ret: ret = findByName(lustre, lov_name, "osc") if not ret: error(lov_name, "not found.") lov_uuid = ret.getAttribute("uuid") ret = findByName(lustre, mdc_name, "mdc") if not ret: error("MDC: ", mdc_name, "not found.") mdc_uuid = ret.getAttribute("uuid") uuid = get_uuid(name) mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path) node = findByName(lustre, node_name, "node") if not node: error('node:', node_name, "not found.") node_add_profile(gen, node, "mountpoint", uuid) node_add_profile(gen, node, "mdc", mdc_uuid) node_add_profile(gen, node, "lov", lov_uuid) lustre.appendChild(mtpt) def add_mdc(gen, lustre, options, args): """ create mtpt on a node """ if len(args) < 1: usage() if options.has_key('node'): node_name = options['node'] else: error("--mdc requires a --node argument") mdc_name = args[0] ret = findByName(lustre, mdc_name, "mdc") if not ret: error("MDC: ", mdc_name, "not found.") mdc_uuid = ret.getAttribute("uuid") node = findByName(lustre, node_name, "node") if not node: error('node:', node_name, "not found.") node_add_profile(gen, node, "mdc", mdc_uuid) def add_mds(gen, lustre, options, args): if len(args) < 1: usage() if options.has_key('node'): node_name = options['node'] else: error("--mds requires a --node argument") devname = args[0] if len(args) > 1: size = args[1] else: size = 0 mds_name = new_name('MDS_'+ node_name) mdc_name = new_name('MDC_'+ node_name) mds_uuid = get_uuid(mds_name) mdc_uuid = get_uuid(mdc_name) node = findByName(lustre, node_name, "node") if not node: error('node:', node_name, 'not found') node_add_profile(gen, node, "mds", mds_uuid) net_uuid = get_net_uuid(lustre, node_name) if not net_uuid: error("NODE: ", node_name, "not found") mds = gen.mds(mds_name, mds_uuid, "extN", devname, "no", net_uuid, dev_size=size) mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid) lustre.appendChild(mds) lustre.appendChild(mdc) # # Command line processing # def parse_cmdline(argv): short_opts = "ho:i:m:" long_opts = ["ost", "mtpt", "lov=", "node=", "mds", "net", "mdc", "merge=", "format", "reformat", "output=", "in=", "help"] opts = [] args = [] options = {} try: opts, args = getopt.getopt(argv, short_opts, long_opts) except getopt.GetoptError: print "invalid opt" usage() for o, a in opts: if o in ("-h", "--help"): usage() if o in ("-o", "--output"): options['output'] = a if o == "--ost": options['ost'] = 1 if o == "--mds": options['mds'] = 1 if o == "--mdc": options['mdc'] = 1 if o == "--net": options['net'] = 1 if o == "--mtpt": options['mtpt'] = 1 if o == "--node": options['node'] = a if o == "--lov": options['lov'] = a if o in ("-m", "--merge"): options['merge'] = a if o == "--format": options['format'] = 1 if o == "--reformat": options['reformat'] = 1 if o in ("--in" , "-i"): options['in'] = a return options, args def main(): options, args = parse_cmdline(sys.argv[1:]) outFile = '-' if options.has_key('merge'): outFile = options['merge'] doc = xml.dom.minidom.parse(outFile) elif options.has_key('in'): doc = xml.dom.minidom.parse(options['in']) else: doc = new_lustre(xml.dom.minidom) if options.has_key('output'): outFile = options['output'] lustre = doc.documentElement init_names(lustre) lustre = doc.documentElement if lustre.tagName != "lustre": print "Existing config not valid." sys.exit(1) gen = GenConfig(doc) if options.has_key('ost'): add_ost(gen, lustre, options, args) elif options.has_key('mtpt'): add_mtpt(gen, lustre, options, args) elif options.has_key('mds'): add_mds(gen, lustre, options, args) elif options.has_key('mdc'): add_mdc(gen, lustre, options, args) elif options.has_key('net'): add_net(gen, lustre, options, args) elif options.has_key('lov'): add_lov(gen, lustre, options, args) elif options.has_key('node'): add_node(gen, lustre, options, args) else: print "Missing command" usage() if outFile == '-': PrettyPrint(doc) else: PrettyPrint(doc, open(outFile,"w")) if __name__ == "__main__": main()