2 # Copyright (C) 2002 Cluster File Systems, Inc.
3 # Author: Robert Read <rread@clusterfs.com>
5 # This file is part of Lustre, http://www.lustre.org.
7 # Lustre is free software; you can redistribute it and/or
8 # modify it under the terms of version 2 of the GNU General Public
9 # License as published by the Free Software Foundation.
11 # Lustre is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Lustre; if not, write to the Free Software
18 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 lmc - lustre configurtion data manager
24 Basic plan for lmc usage:
26 ./lmc --output config.xml --node server --net server1 tcp
27 ./lmc --merge config.xml --node client --net client1 tcp
28 ./lmc --merge config.xml --node client --route gw lo [hi]
29 ./lmc --merge config.xml --router --node gw1 --net gw1 tcp
30 ./lmc --merge config.xml --node gw1 --net 1 elan
32 ./lmc --merge config.xml --route elan 1 1 100
33 ./lmc --merge config.xml --route tcp gw1 ba1
38 ./lmc --merge config.xml --node server --mds mds1 /tmp/mds1 50000
41 ./lmc --merge config.xml --lov lov1 mds1 65536 0 0
42 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost1 100000
43 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost2 100000
45 # create client config
46 ./lmc --merge config.xml --node client --mtpt /mnt/lustre mds1 lov1
50 import sys, os, getopt, string
51 import xml.dom.minidom
52 from xml.dom.ext import PrettyPrint
55 DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use?
58 print """usage: lmc [--node --ost | --mtpt | --lov] args
61 Node_name by itself it will create a new node. If the --router
62 option is used when creating a new node, then that node will also
63 be configured as a router. When used with other commands it
64 specifies the node to modify.
66 --net hostname nettype [port, recv_buf, send_buf]
67 Nettype is either tcp, elan, or gm.
70 --route net gw lo [hi]
71 This command is used to create routes. NET is the
72 network type this route will be used on. The GW is an address of
73 one of the local interfaces. LO and HI represent a range of
74 addresses that can be reached through the gateway. If HI is not
75 set, then a route to the specific host in LO is created.
78 Create a MDS using the device
81 --lov lov_name [mdc_name stripe_sz stripe_off pattern]
82 Creates a logical volume
83 When used with other commands, it specifics the lov to modify
86 Configures a MDC for a node.
90 Creates an OBD/OST/OSC configuration triplet for a new device.
91 When used on "host", the device will be initialized and the OST
92 will be enabled. On client nodes, the OSC will be avaiable.
94 If --lov lov_name is used, this device is added to lov.
96 --mtpt /mnt/point mds_name lov_name|osc_name
97 Creates a client mount point.
101 --merge="xml file" Add the new objects to an existing file
102 --format Format the partitions if unformated
103 --reformat Reformat partitions (this should be an lconf arg,
105 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
106 This is only useful for the --ost command.
107 The device parameters are ignored for the obdecho type.
112 msg = string.join(map(str,args))
117 # manage names and uuids
118 # need to initialize this by walking tree to ensure
119 # no duplicate names or uuids are created.
120 # this are just place holders for now.
121 # consider changing this to be like OBD-dev-host
125 while names.has_key(ret):
126 ret = "%s_%d" % (base, ctr)
132 return "%s_UUID" % (name)
135 ldlm_uuid = 'ldlm_UUID'
137 """Create a new empty lustre document"""
138 # adding ldlm here is a bit of a hack, but one is enough.
140 <ldlm name="%s" uuid="%s"/>
141 <ptlrouter name="PTLROUTER" uuid="PTLROUTER_UUID"/>
142 </lustre>""" % (ldlm_name, ldlm_uuid)
143 return dom.parseString(str)
149 """initialize auto-name generation tables"""
151 # get all elements that contain a name attribute
152 for n in doc.childNodes:
153 if n.nodeType == n.ELEMENT_NODE:
155 names[getName(n)] = 1
156 uuids[getUUID(n)] = 1
159 def get_format_flag(options):
160 if options.has_key('format'):
161 if options['format']:
165 ############################################################
166 # Build config objects using DOM
171 def __init__(self, doc):
174 def ref(self, type, uuid):
175 """ generate <[type]_ref uuidref="[uuid]"/> """
176 tag = "%s_ref" % (type)
177 ref = self.doc.createElement(tag)
178 ref.setAttribute("uuidref", uuid)
181 def newService(self, tag, name, uuid):
182 """ create a new service elmement, which requires name and uuid attributes """
183 new = self.doc.createElement(tag)
184 new.setAttribute("name", name);
185 new.setAttribute("uuid", uuid);
188 def addText(self, node, str):
189 txt = self.doc.createTextNode(str)
190 node.appendChild(txt)
192 def addElement(self, node, tag, str=None):
193 """ create a new element and add it as a child to node. If str is passed,
194 a text node is created for the new element"""
195 new = self.doc.createElement(tag)
197 self.addText(new, str)
198 node.appendChild(new)
201 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
202 """create <network> node"""
203 network = self.newService("network", name, uuid)
204 network.setAttribute("type", net);
205 self.addElement(network, "server", hostname)
207 self.addElement(network, "port", "%d" %(port))
209 self.addElement(network, "send_mem", "%d" %(tcpbuf))
210 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
214 def route(self, lo, hi):
215 """ create one entry for the route table """
216 ref = self.doc.createElement('route')
217 ref.setAttribute("lo", lo)
219 ref.setAttribute("hi", hi)
222 def node(self, name, uuid):
223 """ create a host """
224 node = self.newService("node", name, uuid)
225 self.addElement(node, 'profile')
228 def ldlm(self, name, uuid):
229 """ create a ldlm """
230 ldlm = self.newService("ldlm", name, uuid)
233 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
234 obd = self.newService("obd", name, uuid)
235 obd.setAttribute('type', obdtype)
237 self.addElement(obd, "fstype", fs)
239 dev = self.addElement(obd, "device", devname)
241 dev.setAttribute("size", "%s" % (dev_size))
242 self.addElement(obd, "autoformat", format)
245 def osc(self, name, uuid, obd_uuid, net_uuid):
246 osc = self.newService("osc", name, uuid)
247 osc.appendChild(self.ref("ost", net_uuid))
248 osc.appendChild(self.ref("obd", obd_uuid))
251 def ost(self, name, uuid, obd_uuid, net_uuid):
252 ost = self.newService("ost", name, uuid)
253 ost.appendChild(self.ref("network", net_uuid))
254 ost.appendChild(self.ref("obd", obd_uuid))
257 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
258 lov = self.newService("lov", name, uuid)
259 lov.appendChild(self.ref("mds", mds_uuid))
260 devs = self.addElement(lov, "devices" )
261 devs.setAttribute("stripesize", stripe_sz)
262 devs.setAttribute("stripeoffset", stripe_off)
263 devs.setAttribute("pattern", pattern)
266 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
267 failover_uuid = "", dev_size=0 ):
268 mds = self.newService("mds", name, uuid)
269 self.addElement(mds, "fstype", fs)
270 dev = self.addElement(mds, "device", devname)
272 dev.setAttribute("size", "%s" % (dev_size))
273 self.addElement(mds, "autoformat", format)
274 mds.appendChild(self.ref("network", net_uuid))
275 mds.appendChild(self.ref("node", node_uuid))
277 mds.appendChild(self.ref("failover", failover_uuid))
280 def mdc(self, name, uuid, mds_uuid):
281 mdc = self.newService("mdc", name, uuid)
282 mdc.appendChild(self.ref("mds", mds_uuid))
285 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
286 mtpt = self.newService("mountpoint", name, uuid)
287 mtpt.appendChild(self.ref("mdc", mdc_uuid))
288 mtpt.appendChild(self.ref("osc", osc_uuid))
289 self.addElement(mtpt, "path", path)
292 ############################################################
293 # Utilities to query a DOM tree
294 # Using this functions we can treat use config information
295 # directly as a database.
297 return n.getAttribute('name')
300 return node.getAttribute('uuid')
303 def findByName(lustre, name, tag = ""):
304 for n in lustre.childNodes:
305 if n.nodeType == n.ELEMENT_NODE:
306 if tag and n.nodeName != tag:
308 if getName(n) == name:
311 n = findByName(n, name)
316 def lookup(node, uuid):
317 for n in node.childNodes:
318 if n.nodeType == n.ELEMENT_NODE:
319 if getUUID(n) == uuid:
327 def mds2node(lustre, mds_name):
328 """ Find the node a MDS is configured on """
329 mds = findByName(lustre, mds_name, 'mds')
330 ref = mds.getElementsByTagName('node_ref')
332 error("no node found for:", mds_name)
333 node_uuid = ref[0].getAttribute('uuidref')
334 node = lookup(lustre, node_uuid)
336 error("no node found for :", mds_name)
340 def name2uuid(lustre, name, tag="", fatal=1):
341 ret = findByName(lustre, name, tag)
344 error('name2uuid:', name, "not found.")
350 # XXX: assumes only one network element per node. will fix this
351 # as soon as support for routers is added
352 def get_net_uuid(lustre, node_name):
353 """ get a network uuid for a node_name """
354 node = findByName(lustre, node_name, "node")
356 error ("node not found:", node_name)
357 net = node.getElementsByTagName('network')
359 return getUUID(net[0])
363 def lov_add_osc(gen, lov, osc_uuid):
364 devs = lov.getElementsByTagName('devices')
366 devs[0].appendChild(gen.ref("osc", osc_uuid))
368 error("No devices element found for LOV:", lov)
371 def node_add_profile(gen, node, ref, uuid):
372 ret = node.getElementsByTagName('profile')
374 error('node has no profile:', node)
375 ret[0].appendChild(gen.ref(ref, uuid))
377 def get_attr(dom_node, attr, default=""):
378 v = dom_node.getAttribute(attr)
383 ############################################################
386 def do_add_node(gen, lustre, options, node_name):
387 uuid = new_uuid(node_name)
388 node = gen.node(node_name, uuid)
389 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
390 if options.has_key('router'):
391 node.setAttribute('router', '1')
392 node_add_profile(gen, node, "ptlrouter", 'PTLROUTER_UUID')
393 lustre.appendChild(node)
397 def add_node(gen, lustre, options, args):
398 """ create a node with a network config """
402 node_name = options['node']
404 ret = findByName(lustre, node_name, "node")
406 print "Node:", node_name, "exists."
408 do_add_node(gen, lustre, options, node_name)
411 def add_net(gen, lustre, options, args):
412 """ create a node with a network config """
416 node_name = options['node']
422 if net_type == 'tcp':
427 if options.has_key('tcpbuf'):
428 tcpbuf = int(options['tcpbuf'])
429 elif net_type in ('elan', 'gm'):
432 print "Unknown net_type: ", net_type
435 ret = findByName(lustre, node_name, "node")
437 node = do_add_node(gen, lustre, options, node_name)
440 net_name = new_name('NET_'+ node_name +'_'+ net_type)
441 net_uuid = new_uuid(net_name)
442 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
443 node_add_profile(gen, node, "network", net_uuid)
446 def add_route(gen, lustre, options, args):
447 """ create a node with a network config """
451 node_name = options['node']
460 node = findByName(lustre, node_name, "node")
462 error (node_name, " not found.")
464 netlist = node.getElementsByTagName('network')
466 if get_attr(net, 'type') == net_type:
467 rlist = net.getElementsByTagName('route_tbl')
471 rtbl = gen.addElement(net, 'route_tbl')
472 rtbl.appendChild(gen.route(lo, hi))
475 def add_mds(gen, lustre, options, args):
479 if options.has_key('node'):
480 node_name = options['node']
482 error("--mds requires a --node argument")
484 mds_name = new_name(options['mds'])
491 mdc_name = 'MDC_' + mds_name
492 mds_uuid = new_uuid(mds_name)
493 mdc_uuid = new_uuid(mdc_name)
495 node_uuid = name2uuid(lustre, node_name)
497 node = findByName(lustre, node_name, "node")
498 node_add_profile(gen, node, "mds", mds_uuid)
499 net_uuid = get_net_uuid(lustre, node_name)
501 error("NODE: ", node_name, "not found")
504 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
505 net_uuid, node_uuid, dev_size=size)
506 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
507 lustre.appendChild(mds)
508 lustre.appendChild(mdc)
511 def add_mdc(gen, lustre, options, args):
512 """ create mtpt on a node """
516 if options.has_key('node'):
517 node_name = options['node']
519 error("--mdc requires a --node argument")
522 mdc_uuid = name2uuid(lustre, mdc_name)
524 node = findByName(lustre, node_name, "node")
526 error('node:', node_name, "not found.")
527 node_add_profile(gen, node, "mdc", mdc_uuid)
530 def add_ost(gen, lustre, options, args):
532 obdtype = 'obdfilter'
537 if options.has_key('node'):
538 node_name = options['node']
540 error("--ost requires a --node argument")
542 if options.has_key('lov'):
543 lovname = options['lov']
545 if options.has_key('obdtype'):
546 obdtype = options['obdtype']
548 if obdtype == 'obdecho':
557 obdname = new_name('OBD_'+ node_name)
558 oscname = new_name('OSC_'+ node_name)
559 ostname = new_name('OST_'+ node_name)
560 obd_uuid = new_uuid(obdname)
561 ost_uuid = new_uuid(ostname)
562 osc_uuid = new_uuid(oscname)
564 net_uuid = get_net_uuid(lustre, node_name)
566 error("NODE: ", node_name, "not found")
568 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
569 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
570 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
573 lov = findByName(lustre, lovname, "lov")
575 error("LOV:", lovname, "not found.")
576 lov_add_osc(gen, lov, osc_uuid)
578 node = findByName(lustre, node_name, "node")
579 node_add_profile(gen, node, 'obd', obd_uuid)
580 node_add_profile(gen, node, 'ost', ost_uuid)
582 lustre.appendChild(obd)
583 lustre.appendChild(osc)
584 lustre.appendChild(ost)
587 # this is generally only used by llecho.sh
588 def add_osc(gen, lustre, options, args):
589 """ add the osc to the profile for this node. """
593 if options.has_key('node'):
594 node_name = options['node']
596 error("--osc requires a --node argument")
597 osc_uuid = name2uuid(lustre, osc_name)
598 node = findByName(lustre, node_name, "node")
599 node_add_profile(gen, node, 'osc', osc_uuid)
602 def add_lov(gen, lustre, options, args):
607 name = options['lov']
612 uuid = new_uuid(name)
614 ret = findByName(lustre, name, "lov")
616 error("LOV: ", name, " already exists.")
618 mds_uuid = name2uuid(lustre, mds_name)
620 node = mds2node(lustre, mds_name)
621 node_add_profile(gen, node, "lov", uuid)
622 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
623 lustre.appendChild(lov)
626 def add_mtpt(gen, lustre, options, args):
627 """ create mtpt on a node """
631 if options.has_key('node'):
632 node_name = options['node']
634 error("--mtpt requires a --node argument")
639 mdc_name = 'MDC_' + mds_name
641 name = new_name('MNT_'+ node_name)
643 ret = findByName(lustre, name, "mountpoint")
645 error("MOUNTPOINT: ", name, " already exists.")
647 mdc_uuid = name2uuid(lustre, mdc_name)
648 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
650 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
652 uuid = new_uuid(name)
653 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
654 node = findByName(lustre, node_name, "node")
656 error('node:', node_name, "not found.")
657 node_add_profile(gen, node, "mountpoint", uuid)
658 node_add_profile(gen, node, "mdc", mdc_uuid)
659 lustre.appendChild(mtpt)
662 ############################################################
663 # Command line processing
665 def parse_cmdline(argv):
666 short_opts = "ho:i:m:"
667 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
668 "mdc", "route", "router", "merge=", "format", "reformat", "output=",
669 "obdtype=", "in=", "help"]
674 opts, args = getopt.getopt(argv, short_opts, long_opts)
680 if o in ("-h", "--help"):
682 if o in ("-o", "--output"):
683 options['output'] = a
701 options['router'] = 1
704 if o in ("-m", "--merge"):
707 options['obdtype'] = a
709 options['tcpbuf'] = a
711 options['format'] = 1
712 if o == "--reformat":
713 options['reformat'] = 1
714 if o in ("--in" , "-i"):
720 # simple class for profiling
727 self._start = time.time()
728 def stop(self, msg=''):
729 self._stop = time.time()
733 return self._stop - self._start
734 def display(self, msg):
736 str = '%s: %g secs' % (msg, d)
739 ############################################################
743 options, args = parse_cmdline(sys.argv[1:])
746 if options.has_key('merge'):
747 outFile = options['merge']
748 if os.access(outFile, os.R_OK):
749 doc = xml.dom.minidom.parse(outFile)
751 doc = new_lustre(xml.dom.minidom)
752 elif options.has_key('in'):
753 doc = xml.dom.minidom.parse(options['in'])
755 doc = new_lustre(xml.dom.minidom)
757 if options.has_key('output'):
758 outFile = options['output']
760 lustre = doc.documentElement
762 if lustre.tagName != "lustre":
763 print "Existing config not valid."
767 if options.has_key('ost'):
768 add_ost(gen, lustre, options, args)
769 elif options.has_key('osc'):
770 add_osc(gen, lustre, options, args)
771 elif options.has_key('mtpt'):
772 add_mtpt(gen, lustre, options, args)
773 elif options.has_key('mds'):
774 add_mds(gen, lustre, options, args)
775 elif options.has_key('mdc'):
776 add_mdc(gen, lustre, options, args)
777 elif options.has_key('net'):
778 add_net(gen, lustre, options, args)
779 elif options.has_key('lov'):
780 add_lov(gen, lustre, options, args)
781 elif options.has_key('route'):
782 add_route(gen, lustre, options, args)
783 elif options.has_key('node'):
784 add_node(gen, lustre, options, args)
786 print "Missing command"
792 PrettyPrint(doc, open(outFile,"w"))
794 if __name__ == "__main__":