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 Optional --obduuid Specifies the UUID used for the obd.
91 If --lov lov_name is used, this device is added to lov.
93 --mtpt /mnt/point mds_name lov_name|osc_name
94 Creates a client mount point.
98 --merge="xml file" Add the new objects to an existing file
99 --format Format the partitions if unformated
100 NB: The autoformat option has been disabled until a safe
101 method is implemented to determine if a block device has a
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 </lustre>""" % (ldlm_name, ldlm_uuid)
142 return dom.parseString(str)
148 """initialize auto-name generation tables"""
150 # get all elements that contain a name attribute
151 for n in doc.childNodes:
152 if n.nodeType == n.ELEMENT_NODE:
154 names[getName(n)] = 1
155 uuids[getUUID(n)] = 1
158 def get_format_flag(options):
159 if options.has_key('format'):
160 if options['format']:
164 ############################################################
165 # Build config objects using DOM
170 def __init__(self, doc):
173 def ref(self, type, uuid):
174 """ generate <[type]_ref uuidref="[uuid]"/> """
175 tag = "%s_ref" % (type)
176 ref = self.doc.createElement(tag)
177 ref.setAttribute("uuidref", uuid)
180 def newService(self, tag, name, uuid):
181 """ create a new service elmement, which requires name and uuid attributes """
182 new = self.doc.createElement(tag)
183 new.setAttribute("name", name);
184 new.setAttribute("uuid", uuid);
187 def addText(self, node, str):
188 txt = self.doc.createTextNode(str)
189 node.appendChild(txt)
191 def addElement(self, node, tag, str=None):
192 """ create a new element and add it as a child to node. If str is passed,
193 a text node is created for the new element"""
194 new = self.doc.createElement(tag)
196 self.addText(new, str)
197 node.appendChild(new)
200 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
201 """create <network> node"""
202 network = self.newService("network", name, uuid)
203 network.setAttribute("type", net);
204 self.addElement(network, "server", hostname)
206 self.addElement(network, "port", "%d" %(port))
208 self.addElement(network, "send_mem", "%d" %(tcpbuf))
209 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
213 def route(self, net_type, gw, lo, hi):
214 """ create one entry for the route table """
215 ref = self.doc.createElement('route')
216 ref.setAttribute("type", net_type)
217 ref.setAttribute("gw", gw)
218 ref.setAttribute("lo", lo)
220 ref.setAttribute("hi", hi)
223 def node(self, name, uuid):
224 """ create a host """
225 node = self.newService("node", name, uuid)
226 self.addElement(node, 'profile')
229 def ldlm(self, name, uuid):
230 """ create a ldlm """
231 ldlm = self.newService("ldlm", name, uuid)
234 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
235 obd = self.newService("obd", name, uuid)
236 obd.setAttribute('type', obdtype)
238 self.addElement(obd, "fstype", fs)
240 dev = self.addElement(obd, "device", devname)
242 dev.setAttribute("size", "%s" % (dev_size))
243 self.addElement(obd, "autoformat", format)
246 def osc(self, name, uuid, obd_uuid, net_uuid):
247 osc = self.newService("osc", name, uuid)
248 osc.appendChild(self.ref("ost", net_uuid))
249 osc.appendChild(self.ref("obd", obd_uuid))
252 def ost(self, name, uuid, obd_uuid, net_uuid):
253 ost = self.newService("ost", name, uuid)
254 ost.appendChild(self.ref("network", net_uuid))
255 ost.appendChild(self.ref("obd", obd_uuid))
258 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_count, pattern):
259 lov = self.newService("lov", name, uuid)
260 lov.appendChild(self.ref("mds", mds_uuid))
261 devs = self.addElement(lov, "devices" )
262 devs.setAttribute("stripesize", stripe_sz)
263 devs.setAttribute("stripecount", stripe_count)
264 devs.setAttribute("pattern", pattern)
267 def lovconfig(self, name, uuid, lov_uuid):
268 lovconfig = self.newService("lovconfig", name, uuid)
269 lovconfig.appendChild(self.ref("lov", lov_uuid))
272 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
273 failover_uuid = "", dev_size=0 ):
274 mds = self.newService("mds", name, uuid)
275 self.addElement(mds, "fstype", fs)
276 dev = self.addElement(mds, "device", devname)
278 dev.setAttribute("size", "%s" % (dev_size))
279 self.addElement(mds, "autoformat", format)
280 mds.appendChild(self.ref("network", net_uuid))
281 mds.appendChild(self.ref("node", node_uuid))
283 mds.appendChild(self.ref("failover", failover_uuid))
286 def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
287 mtpt = self.newService("mountpoint", name, uuid)
288 mtpt.appendChild(self.ref("mds", mds_uuid))
289 mtpt.appendChild(self.ref("osc", osc_uuid))
290 self.addElement(mtpt, "path", path)
293 ############################################################
294 # Utilities to query a DOM tree
295 # Using this functions we can treat use config information
296 # directly as a database.
298 return n.getAttribute('name')
301 return node.getAttribute('uuid')
304 def findByName(lustre, name, tag = ""):
305 for n in lustre.childNodes:
306 if n.nodeType == n.ELEMENT_NODE:
307 if tag and n.nodeName != tag:
309 if getName(n) == name:
312 n = findByName(n, name)
317 def lookup(node, uuid):
318 for n in node.childNodes:
319 if n.nodeType == n.ELEMENT_NODE:
320 if getUUID(n) == uuid:
328 def mds2node(lustre, mds_name):
329 """ Find the node a MDS is configured on """
330 mds = findByName(lustre, mds_name, 'mds')
331 ref = mds.getElementsByTagName('node_ref')
333 error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
334 node_uuid = ref[0].getAttribute('uuidref')
335 node = lookup(lustre, node_uuid)
337 error('mds2node:', "no node found for :", '"'+mds_name+'"')
341 def name2uuid(lustre, name, tag="", fatal=1):
342 ret = findByName(lustre, name, tag)
345 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
351 # XXX: assumes only one network element per node. will fix this
352 # as soon as support for routers is added
353 def get_net_uuid(lustre, node_name):
354 """ get a network uuid for a node_name """
355 node = findByName(lustre, node_name, "node")
357 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
358 net = node.getElementsByTagName('network')
360 return getUUID(net[0])
364 def lov_add_osc(gen, lov, osc_uuid):
365 devs = lov.getElementsByTagName('devices')
367 devs[0].appendChild(gen.ref("osc", osc_uuid))
369 error("No devices element found for LOV:", lov)
372 def node_add_profile(gen, node, ref, uuid):
373 ret = node.getElementsByTagName('profile')
375 error('node has no profile:', node)
376 ret[0].appendChild(gen.ref(ref, uuid))
378 def get_attr(dom_node, attr, default=""):
379 v = dom_node.getAttribute(attr)
384 ############################################################
387 def do_add_node(gen, lustre, options, node_name):
388 uuid = new_uuid(node_name)
389 node = gen.node(node_name, uuid)
390 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
391 if options.has_key('router'):
392 node.setAttribute('router', '1')
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 rlist = net.getElementsByTagName('route_tbl')
470 rtbl = gen.addElement(net, 'route_tbl')
471 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
474 def add_mds(gen, lustre, options, args):
478 if options.has_key('node'):
479 node_name = options['node']
481 error("--mds requires a --node argument")
483 mds_name = new_name(options['mds'])
490 mds_uuid = new_uuid(mds_name)
492 node_uuid = name2uuid(lustre, node_name, 'node')
494 node = findByName(lustre, node_name, "node")
495 node_add_profile(gen, node, "mds", mds_uuid)
496 net_uuid = get_net_uuid(lustre, node_name)
498 error("NODE: ", node_name, "not found")
501 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
502 net_uuid, node_uuid, dev_size=size)
503 lustre.appendChild(mds)
506 def add_ost(gen, lustre, options, args):
508 obdtype = 'obdfilter'
513 if options.has_key('node'):
514 node_name = options['node']
516 error("--ost requires a --node argument")
518 if options.has_key('lov'):
519 lovname = options['lov']
521 if options.has_key('obdtype'):
522 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 if options.has_key('obduuid'):
536 obd_uuid = options['obduuid']
537 obd = lookup(lustre, obd_uuid)
539 error("Duplicate OBD UUID:", obd_uuid)
541 obd_uuid = new_uuid(obdname)
542 ost_uuid = new_uuid(ostname)
543 osc_uuid = new_uuid(oscname)
545 net_uuid = get_net_uuid(lustre, node_name)
547 error("NODE: ", node_name, "not found")
549 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
550 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
551 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
554 lov = findByName(lustre, lovname, "lov")
556 error('add_ost:', '"'+lovname+'"', "lov element not found.")
557 lov_add_osc(gen, lov, osc_uuid)
559 node = findByName(lustre, node_name, "node")
560 node_add_profile(gen, node, 'obd', obd_uuid)
561 node_add_profile(gen, node, 'ost', ost_uuid)
563 lustre.appendChild(obd)
564 lustre.appendChild(osc)
565 lustre.appendChild(ost)
568 # this is generally only used by llecho.sh
569 def add_osc(gen, lustre, options, args):
570 """ add the osc to the profile for this node. """
574 if options.has_key('node'):
575 node_name = options['node']
577 error("--osc requires a --node argument")
578 osc_uuid = name2uuid(lustre, osc_name) # either 'osc' or 'lov'
579 node = findByName(lustre, node_name, "node")
580 node_add_profile(gen, node, 'osc', osc_uuid)
583 def add_lov(gen, lustre, options, args):
588 name = options['lov']
591 stripe_count = args[2]
593 uuid = new_uuid(name)
595 ret = findByName(lustre, name, "lov")
597 error("LOV: ", name, " already exists.")
599 mds_uuid = name2uuid(lustre, mds_name, 'mds')
600 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
601 lustre.appendChild(lov)
603 # add an lovconfig entry to the mds profile
604 lovconfig_name = new_name('LVCFG_' + name)
605 lovconfig_uuid = new_uuid(lovconfig_name)
606 node = mds2node(lustre, mds_name)
607 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
608 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
609 lustre.appendChild(lovconfig)
613 def add_mtpt(gen, lustre, options, args):
614 """ create mtpt on a node """
618 if options.has_key('node'):
619 node_name = options['node']
621 error("--mtpt requires a --node argument")
627 name = new_name('MNT_'+ node_name)
629 ret = findByName(lustre, name, "mountpoint")
631 error("MOUNTPOINT: ", name, " already exists.")
633 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
634 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
636 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
638 uuid = new_uuid(name)
639 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
640 node = findByName(lustre, node_name, "node")
642 error('node:', node_name, "not found.")
643 node_add_profile(gen, node, "mountpoint", uuid)
644 lustre.appendChild(mtpt)
647 ############################################################
648 # Command line processing
650 def parse_cmdline(argv):
651 short_opts = "ho:i:m:"
652 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
653 "route", "router", "merge=", "format", "reformat", "output=",
654 "obdtype=", "obduuid=", "in=", "help", "batch="]
659 opts, args = getopt.getopt(argv, short_opts, long_opts)
665 # Commands to create new devices
681 options['router'] = 1
685 # Options for commands
687 options['obdtype'] = a
689 options['obduuid'] = a
691 options['tcpbuf'] = a
694 if o in ("-h", "--help"):
696 if o in ("-o", "--output"):
697 options['output'] = a
698 if o in ("-m", "--merge"):
701 options['format'] = 1
702 if o == "--reformat":
703 options['reformat'] = 1
706 if o in ("--in" , "-i"):
712 # simple class for profiling
719 self._start = time.time()
720 def stop(self, msg=''):
721 self._stop = time.time()
725 return self._stop - self._start
726 def display(self, msg):
728 str = '%s: %g secs' % (msg, d)
731 ############################################################
734 def do_command(gen, lustre, options, args):
735 if options.has_key('ost'):
736 add_ost(gen, lustre, options, args)
737 elif options.has_key('osc'):
738 add_osc(gen, lustre, options, args)
739 elif options.has_key('mtpt'):
740 add_mtpt(gen, lustre, options, args)
741 elif options.has_key('mds'):
742 add_mds(gen, lustre, options, args)
743 elif options.has_key('net'):
744 add_net(gen, lustre, options, args)
745 elif options.has_key('lov'):
746 add_lov(gen, lustre, options, args)
747 elif options.has_key('route'):
748 add_route(gen, lustre, options, args)
749 elif options.has_key('node'):
750 add_node(gen, lustre, options, args)
752 print "Missing command"
756 options, args = parse_cmdline(sys.argv[1:])
759 if options.has_key('merge'):
760 outFile = options['merge']
761 if os.access(outFile, os.R_OK):
762 doc = xml.dom.minidom.parse(outFile)
764 doc = new_lustre(xml.dom.minidom)
765 elif options.has_key('in'):
766 doc = xml.dom.minidom.parse(options['in'])
768 doc = new_lustre(xml.dom.minidom)
770 if options.has_key('output'):
771 outFile = options['output']
773 lustre = doc.documentElement
775 if lustre.tagName != "lustre":
776 print "Existing config not valid."
781 if options.has_key('batch'):
782 fp = open(options['batch'])
783 batchCommands = fp.readlines()
785 for cmd in batchCommands:
786 options, args = parse_cmdline(string.split(cmd))
787 do_command(gen, lustre, options, args)
789 do_command(gen, lustre, options, args)
794 PrettyPrint(doc, open(outFile,"w"))
796 if __name__ == "__main__":