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 [mds_name stripe_sz sub_stripe_count pattern]
82 Creates a logical volume
83 When used with other commands, it specifics the lov to modify
86 Creates an OBD/OST/OSC configuration triplet for a new device.
87 When used on "host", the device will be initialized and the OST
88 will be enabled. On client nodes, the OSC will be avaiable.
90 If --lov lov_name is used, this device is added to lov.
92 --mtpt /mnt/point mds_name lov_name|osc_name
93 Creates a client mount point.
97 --merge="xml file" Add the new objects to an existing file
98 --format Format the partitions if unformated
99 NB: The autoformat option has been disabled until a safe
100 method is implemented to determine if a block device has a
102 --reformat Reformat partitions (this should be an lconf arg,
104 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
105 This is only useful for the --ost command.
106 The device parameters are ignored for the obdecho type.
111 msg = string.join(map(str,args))
116 # manage names and uuids
117 # need to initialize this by walking tree to ensure
118 # no duplicate names or uuids are created.
119 # this are just place holders for now.
120 # consider changing this to be like OBD-dev-host
124 while names.has_key(ret):
125 ret = "%s_%d" % (base, ctr)
131 return "%s_UUID" % (name)
134 ldlm_uuid = 'ldlm_UUID'
136 """Create a new empty lustre document"""
137 # adding ldlm here is a bit of a hack, but one is enough.
139 <ldlm name="%s" uuid="%s"/>
140 </lustre>""" % (ldlm_name, ldlm_uuid)
141 return dom.parseString(str)
147 """initialize auto-name generation tables"""
149 # get all elements that contain a name attribute
150 for n in doc.childNodes:
151 if n.nodeType == n.ELEMENT_NODE:
153 names[getName(n)] = 1
154 uuids[getUUID(n)] = 1
157 def get_format_flag(options):
158 if options.has_key('format'):
159 if options['format']:
163 ############################################################
164 # Build config objects using DOM
169 def __init__(self, doc):
172 def ref(self, type, uuid):
173 """ generate <[type]_ref uuidref="[uuid]"/> """
174 tag = "%s_ref" % (type)
175 ref = self.doc.createElement(tag)
176 ref.setAttribute("uuidref", uuid)
179 def newService(self, tag, name, uuid):
180 """ create a new service elmement, which requires name and uuid attributes """
181 new = self.doc.createElement(tag)
182 new.setAttribute("name", name);
183 new.setAttribute("uuid", uuid);
186 def addText(self, node, str):
187 txt = self.doc.createTextNode(str)
188 node.appendChild(txt)
190 def addElement(self, node, tag, str=None):
191 """ create a new element and add it as a child to node. If str is passed,
192 a text node is created for the new element"""
193 new = self.doc.createElement(tag)
195 self.addText(new, str)
196 node.appendChild(new)
199 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
200 """create <network> node"""
201 network = self.newService("network", name, uuid)
202 network.setAttribute("type", net);
203 self.addElement(network, "server", hostname)
205 self.addElement(network, "port", "%d" %(port))
207 self.addElement(network, "send_mem", "%d" %(tcpbuf))
208 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
212 def route(self, net_type, gw, lo, hi):
213 """ create one entry for the route table """
214 ref = self.doc.createElement('route')
215 ref.setAttribute("type", net_type)
216 ref.setAttribute("gw", gw)
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_count, 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("stripecount", stripe_count)
263 devs.setAttribute("pattern", pattern)
266 def lovconfig(self, name, uuid, lov_uuid):
267 lovconfig = self.newService("lovconfig", name, uuid)
268 lovconfig.appendChild(self.ref("lov", lov_uuid))
271 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
272 failover_uuid = "", dev_size=0 ):
273 mds = self.newService("mds", name, uuid)
274 self.addElement(mds, "fstype", fs)
275 dev = self.addElement(mds, "device", devname)
277 dev.setAttribute("size", "%s" % (dev_size))
278 self.addElement(mds, "autoformat", format)
279 mds.appendChild(self.ref("network", net_uuid))
280 mds.appendChild(self.ref("node", node_uuid))
282 mds.appendChild(self.ref("failover", failover_uuid))
285 def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
286 mtpt = self.newService("mountpoint", name, uuid)
287 mtpt.appendChild(self.ref("mds", mds_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 lustre.appendChild(node)
396 def add_node(gen, lustre, options, args):
397 """ create a node with a network config """
401 node_name = options['node']
403 ret = findByName(lustre, node_name, "node")
405 print "Node:", node_name, "exists."
407 do_add_node(gen, lustre, options, node_name)
410 def add_net(gen, lustre, options, args):
411 """ create a node with a network config """
415 node_name = options['node']
421 if net_type == 'tcp':
426 if options.has_key('tcpbuf'):
427 tcpbuf = int(options['tcpbuf'])
428 elif net_type in ('elan', 'gm'):
431 print "Unknown net_type: ", net_type
434 ret = findByName(lustre, node_name, "node")
436 node = do_add_node(gen, lustre, options, node_name)
439 net_name = new_name('NET_'+ node_name +'_'+ net_type)
440 net_uuid = new_uuid(net_name)
441 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
442 node_add_profile(gen, node, "network", net_uuid)
445 def add_route(gen, lustre, options, args):
446 """ create a node with a network config """
450 node_name = options['node']
459 node = findByName(lustre, node_name, "node")
461 error (node_name, " not found.")
463 netlist = node.getElementsByTagName('network')
465 rlist = net.getElementsByTagName('route_tbl')
469 rtbl = gen.addElement(net, 'route_tbl')
470 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
473 def add_mds(gen, lustre, options, args):
477 if options.has_key('node'):
478 node_name = options['node']
480 error("--mds requires a --node argument")
482 mds_name = new_name(options['mds'])
489 mds_uuid = new_uuid(mds_name)
491 node_uuid = name2uuid(lustre, node_name)
493 node = findByName(lustre, node_name, "node")
494 node_add_profile(gen, node, "mds", mds_uuid)
495 net_uuid = get_net_uuid(lustre, node_name)
497 error("NODE: ", node_name, "not found")
500 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
501 net_uuid, node_uuid, dev_size=size)
502 lustre.appendChild(mds)
505 def add_ost(gen, lustre, options, args):
507 obdtype = 'obdfilter'
512 if options.has_key('node'):
513 node_name = options['node']
515 error("--ost requires a --node argument")
517 if options.has_key('lov'):
518 lovname = options['lov']
520 if options.has_key('obdtype'):
521 obdtype = options['obdtype']
523 if obdtype == 'obdecho':
532 obdname = new_name('OBD_'+ node_name)
533 oscname = new_name('OSC_'+ node_name)
534 ostname = new_name('OST_'+ node_name)
535 obd_uuid = new_uuid(obdname)
536 ost_uuid = new_uuid(ostname)
537 osc_uuid = new_uuid(oscname)
539 net_uuid = get_net_uuid(lustre, node_name)
541 error("NODE: ", node_name, "not found")
543 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
544 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
545 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
548 lov = findByName(lustre, lovname, "lov")
550 error("LOV:", lovname, "not found.")
551 lov_add_osc(gen, lov, osc_uuid)
553 node = findByName(lustre, node_name, "node")
554 node_add_profile(gen, node, 'obd', obd_uuid)
555 node_add_profile(gen, node, 'ost', ost_uuid)
557 lustre.appendChild(obd)
558 lustre.appendChild(osc)
559 lustre.appendChild(ost)
562 # this is generally only used by llecho.sh
563 def add_osc(gen, lustre, options, args):
564 """ add the osc to the profile for this node. """
568 if options.has_key('node'):
569 node_name = options['node']
571 error("--osc requires a --node argument")
572 osc_uuid = name2uuid(lustre, osc_name)
573 node = findByName(lustre, node_name, "node")
574 node_add_profile(gen, node, 'osc', osc_uuid)
577 def add_lov(gen, lustre, options, args):
582 name = options['lov']
585 stripe_count = args[2]
587 uuid = new_uuid(name)
589 ret = findByName(lustre, name, "lov")
591 error("LOV: ", name, " already exists.")
593 mds_uuid = name2uuid(lustre, mds_name)
594 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
595 lustre.appendChild(lov)
597 # add an lovconfig entry to the mds profile
598 lovconfig_name = new_name('LVCFG_' + name)
599 lovconfig_uuid = new_uuid(lovconfig_name)
600 node = mds2node(lustre, mds_name)
601 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
602 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
603 lustre.appendChild(lovconfig)
607 def add_mtpt(gen, lustre, options, args):
608 """ create mtpt on a node """
612 if options.has_key('node'):
613 node_name = options['node']
615 error("--mtpt requires a --node argument")
621 name = new_name('MNT_'+ node_name)
623 ret = findByName(lustre, name, "mountpoint")
625 error("MOUNTPOINT: ", name, " already exists.")
627 mds_uuid = name2uuid(lustre, mds_name)
628 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
630 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
632 uuid = new_uuid(name)
633 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
634 node = findByName(lustre, node_name, "node")
636 error('node:', node_name, "not found.")
637 node_add_profile(gen, node, "mountpoint", uuid)
638 lustre.appendChild(mtpt)
641 ############################################################
642 # Command line processing
644 def parse_cmdline(argv):
645 short_opts = "ho:i:m:"
646 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
647 "route", "router", "merge=", "format", "reformat", "output=",
648 "obdtype=", "in=", "help", "batch="]
653 opts, args = getopt.getopt(argv, short_opts, long_opts)
659 if o in ("-h", "--help"):
661 if o in ("-o", "--output"):
662 options['output'] = a
678 options['router'] = 1
681 if o in ("-m", "--merge"):
684 options['obdtype'] = a
686 options['tcpbuf'] = a
688 options['format'] = 1
689 if o == "--reformat":
690 options['reformat'] = 1
693 if o in ("--in" , "-i"):
699 # simple class for profiling
706 self._start = time.time()
707 def stop(self, msg=''):
708 self._stop = time.time()
712 return self._stop - self._start
713 def display(self, msg):
715 str = '%s: %g secs' % (msg, d)
718 ############################################################
721 def do_command(gen, lustre, options, args):
722 if options.has_key('ost'):
723 add_ost(gen, lustre, options, args)
724 elif options.has_key('osc'):
725 add_osc(gen, lustre, options, args)
726 elif options.has_key('mtpt'):
727 add_mtpt(gen, lustre, options, args)
728 elif options.has_key('mds'):
729 add_mds(gen, lustre, options, args)
730 elif options.has_key('net'):
731 add_net(gen, lustre, options, args)
732 elif options.has_key('lov'):
733 add_lov(gen, lustre, options, args)
734 elif options.has_key('route'):
735 add_route(gen, lustre, options, args)
736 elif options.has_key('node'):
737 add_node(gen, lustre, options, args)
739 print "Missing command"
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."
768 if options.has_key('batch'):
769 fp = open(options['batch'])
770 batchCommands = fp.readlines()
772 for cmd in batchCommands:
773 options, args = parse_cmdline(string.split(cmd))
774 do_command(gen, lustre, options, args)
776 do_command(gen, lustre, options, args)
781 PrettyPrint(doc, open(outFile,"w"))
783 if __name__ == "__main__":