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 msg = string.join(map(str,args))
118 print "Warning: ", msg
121 # manage names and uuids
122 # need to initialize this by walking tree to ensure
123 # no duplicate names or uuids are created.
124 # this are just place holders for now.
125 # consider changing this to be like OBD-dev-host
129 while names.has_key(ret):
130 ret = "%s_%d" % (base, ctr)
136 return "%s_UUID" % (name)
139 ldlm_uuid = 'ldlm_UUID'
141 """Create a new empty lustre document"""
142 # adding ldlm here is a bit of a hack, but one is enough.
144 <ldlm name="%s" uuid="%s"/>
145 </lustre>""" % (ldlm_name, ldlm_uuid)
146 return dom.parseString(str)
152 """initialize auto-name generation tables"""
154 # get all elements that contain a name attribute
155 for n in doc.childNodes:
156 if n.nodeType == n.ELEMENT_NODE:
158 names[getName(n)] = 1
159 uuids[getUUID(n)] = 1
162 def get_format_flag(options):
163 if options.has_key('format'):
164 if options['format']:
168 ############################################################
169 # Build config objects using DOM
174 def __init__(self, doc):
177 def ref(self, type, uuid):
178 """ generate <[type]_ref uuidref="[uuid]"/> """
179 tag = "%s_ref" % (type)
180 ref = self.doc.createElement(tag)
181 ref.setAttribute("uuidref", uuid)
184 def newService(self, tag, name, uuid):
185 """ create a new service elmement, which requires name and uuid attributes """
186 new = self.doc.createElement(tag)
187 new.setAttribute("name", name);
188 new.setAttribute("uuid", uuid);
191 def addText(self, node, str):
192 txt = self.doc.createTextNode(str)
193 node.appendChild(txt)
195 def addElement(self, node, tag, str=None):
196 """ create a new element and add it as a child to node. If str is passed,
197 a text node is created for the new element"""
198 new = self.doc.createElement(tag)
200 self.addText(new, str)
201 node.appendChild(new)
204 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
205 """create <network> node"""
206 network = self.newService("network", name, uuid)
207 network.setAttribute("type", net);
208 self.addElement(network, "server", hostname)
210 self.addElement(network, "port", "%d" %(port))
212 self.addElement(network, "send_mem", "%d" %(tcpbuf))
213 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
217 def route(self, net_type, gw, lo, hi):
218 """ create one entry for the route table """
219 ref = self.doc.createElement('route')
220 ref.setAttribute("type", net_type)
221 ref.setAttribute("gw", gw)
222 ref.setAttribute("lo", lo)
224 ref.setAttribute("hi", hi)
227 def node(self, name, uuid):
228 """ create a host """
229 node = self.newService("node", name, uuid)
230 self.addElement(node, 'profile')
233 def ldlm(self, name, uuid):
234 """ create a ldlm """
235 ldlm = self.newService("ldlm", name, uuid)
238 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
239 obd = self.newService("obd", name, uuid)
240 obd.setAttribute('type', obdtype)
242 self.addElement(obd, "fstype", fs)
244 dev = self.addElement(obd, "device", devname)
246 dev.setAttribute("size", "%s" % (dev_size))
247 self.addElement(obd, "autoformat", format)
250 def osc(self, name, uuid, obd_uuid, net_uuid):
251 osc = self.newService("osc", name, uuid)
252 osc.appendChild(self.ref("network", net_uuid))
253 osc.appendChild(self.ref("obd", obd_uuid))
256 def ost(self, name, uuid, obd_uuid, net_uuid):
257 ost = self.newService("ost", name, uuid)
258 ost.appendChild(self.ref("network", net_uuid))
259 ost.appendChild(self.ref("obd", obd_uuid))
262 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_count, pattern):
263 lov = self.newService("lov", name, uuid)
264 lov.appendChild(self.ref("mds", mds_uuid))
265 devs = self.addElement(lov, "devices" )
266 devs.setAttribute("stripesize", stripe_sz)
267 devs.setAttribute("stripecount", stripe_count)
268 devs.setAttribute("pattern", pattern)
271 def lovconfig(self, name, uuid, lov_uuid):
272 lovconfig = self.newService("lovconfig", name, uuid)
273 lovconfig.appendChild(self.ref("lov", lov_uuid))
276 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
277 failover_uuid = "", dev_size=0 ):
278 mds = self.newService("mds", name, uuid)
279 self.addElement(mds, "fstype", fs)
280 dev = self.addElement(mds, "device", devname)
282 dev.setAttribute("size", "%s" % (dev_size))
283 self.addElement(mds, "autoformat", format)
284 mds.appendChild(self.ref("network", net_uuid))
285 mds.appendChild(self.ref("node", node_uuid))
287 mds.appendChild(self.ref("failover", failover_uuid))
290 def mdc(self, name, uuid, mds_uuid, net_uuid):
291 mdc = self.newService("mdc",name,uuid)
292 mdc.appendChild(self.ref("mds",mds_uuid))
293 mdc.appendChild(self.ref("network",net_uuid))
296 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
297 mtpt = self.newService("mountpoint", name, uuid)
298 mtpt.appendChild(self.ref("mdc", mdc_uuid))
299 mtpt.appendChild(self.ref("osc", osc_uuid))
300 self.addElement(mtpt, "path", path)
303 ############################################################
304 # Utilities to query a DOM tree
305 # Using this functions we can treat use config information
306 # directly as a database.
308 return n.getAttribute('name')
311 return node.getAttribute('uuid')
314 def findByName(lustre, name, tag = ""):
315 for n in lustre.childNodes:
316 if n.nodeType == n.ELEMENT_NODE:
317 if tag and n.nodeName != tag:
319 if getName(n) == name:
322 n = findByName(n, name)
327 def lookup(node, uuid):
328 for n in node.childNodes:
329 if n.nodeType == n.ELEMENT_NODE:
330 if getUUID(n) == uuid:
338 def mds2node(lustre, mds_name):
339 """ Find the node a MDS is configured on """
340 mds = findByName(lustre, mds_name, 'mds')
341 ref = mds.getElementsByTagName('node_ref')
343 error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
344 node_uuid = ref[0].getAttribute('uuidref')
345 node = lookup(lustre, node_uuid)
347 error('mds2node:', "no node found for :", '"'+mds_name+'"')
351 def name2uuid(lustre, name, tag="", fatal=1):
352 ret = findByName(lustre, name, tag)
355 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
361 # XXX: assumes only one network element per node. will fix this
362 # as soon as support for routers is added
363 def get_net_uuid(lustre, node_name):
364 """ get a network uuid for a node_name """
365 node = findByName(lustre, node_name, "node")
367 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
368 net = node.getElementsByTagName('network')
370 return getUUID(net[0])
374 def lov_add_osc(gen, lov, osc_uuid):
375 devs = lov.getElementsByTagName('devices')
377 devs[0].appendChild(gen.ref("osc", osc_uuid))
379 error("No devices element found for LOV:", lov)
382 def node_add_profile(gen, node, ref, uuid):
383 ret = node.getElementsByTagName('profile')
385 error('node has no profile:', node)
386 ret[0].appendChild(gen.ref(ref, uuid))
388 def get_attr(dom_node, attr, default=""):
389 v = dom_node.getAttribute(attr)
394 ############################################################
397 def do_add_node(gen, lustre, options, node_name):
398 uuid = new_uuid(node_name)
399 node = gen.node(node_name, uuid)
400 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
401 if options.has_key('router'):
402 node.setAttribute('router', '1')
403 lustre.appendChild(node)
407 def add_node(gen, lustre, options, args):
408 """ create a node with a network config """
412 node_name = options['node']
414 ret = findByName(lustre, node_name, "node")
416 print "Node:", node_name, "exists."
418 do_add_node(gen, lustre, options, node_name)
421 def add_net(gen, lustre, options, args):
422 """ create a node with a network config """
426 node_name = options['node']
432 if net_type == 'tcp':
437 if options.has_key('tcpbuf'):
438 tcpbuf = int(options['tcpbuf'])
439 elif net_type in ('elan', 'gm'):
442 print "Unknown net_type: ", net_type
445 ret = findByName(lustre, node_name, "node")
447 node = do_add_node(gen, lustre, options, node_name)
450 net_name = new_name('NET_'+ node_name +'_'+ net_type)
451 net_uuid = new_uuid(net_name)
452 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
453 node_add_profile(gen, node, "network", net_uuid)
456 def add_route(gen, lustre, options, args):
457 """ create a node with a network config """
461 node_name = options['node']
470 node = findByName(lustre, node_name, "node")
472 error (node_name, " not found.")
474 netlist = node.getElementsByTagName('network')
476 rlist = net.getElementsByTagName('route_tbl')
480 rtbl = gen.addElement(net, 'route_tbl')
481 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
484 def add_mds(gen, lustre, options, args):
488 if options.has_key('node'):
489 node_name = options['node']
491 error("--mds requires a --node argument")
493 mds_name = new_name(options['mds'])
494 if mds_name != options['mds']:
495 warning("name:", options['mds'], "already used. using:", mds_name)
502 mds_uuid = new_uuid(mds_name)
504 node_uuid = name2uuid(lustre, node_name, 'node')
506 node = findByName(lustre, node_name, "node")
507 node_add_profile(gen, node, "mds", mds_uuid)
508 net_uuid = get_net_uuid(lustre, node_name)
510 error("NODE: ", node_name, "not found")
513 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
514 net_uuid, node_uuid, dev_size=size)
515 lustre.appendChild(mds)
518 def add_ost(gen, lustre, options, args):
520 obdtype = 'obdfilter'
525 if options.has_key('node'):
526 node_name = options['node']
528 error("--ost requires a --node argument")
530 if options.has_key('lov'):
531 lovname = options['lov']
533 if options.has_key('obdtype'):
534 obdtype = options['obdtype']
536 if obdtype == 'obdecho':
545 obdname = new_name('OBD_'+ node_name)
546 oscname = new_name('OSC_'+ node_name)
547 ostname = new_name('OST_'+ node_name)
548 if options.has_key('obduuid'):
549 obd_uuid = options['obduuid']
550 obd = lookup(lustre, obd_uuid)
552 error("Duplicate OBD UUID:", obd_uuid)
554 obd_uuid = new_uuid(obdname)
555 ost_uuid = new_uuid(ostname)
556 osc_uuid = new_uuid(oscname)
558 net_uuid = get_net_uuid(lustre, node_name)
560 error("NODE: ", node_name, "not found")
562 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
563 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
564 osc = gen.osc(oscname, osc_uuid, obd_uuid, net_uuid)
567 lov = findByName(lustre, lovname, "lov")
569 error('add_ost:', '"'+lovname+'"', "lov element not found.")
570 lov_add_osc(gen, lov, osc_uuid)
572 node = findByName(lustre, node_name, "node")
573 node_add_profile(gen, node, 'obd', obd_uuid)
574 node_add_profile(gen, node, 'ost', ost_uuid)
576 lustre.appendChild(obd)
577 lustre.appendChild(osc)
578 lustre.appendChild(ost)
581 # this is generally only used by llecho.sh
582 def add_osc(gen, lustre, options, args):
583 """ add the osc to the profile for this node. """
587 if options.has_key('node'):
588 node_name = options['node']
590 error("--osc requires a --node argument")
591 osc_uuid = name2uuid(lustre, osc_name) # either 'osc' or 'lov'
592 node = findByName(lustre, node_name, "node")
593 node_add_profile(gen, node, 'osc', osc_uuid)
596 def add_lov(gen, lustre, options, args):
601 name = new_name(options['lov'])
602 if name != options['lov']:
603 warning("name:", options['lov'], "already used. using:", name)
607 stripe_count = args[2]
609 uuid = new_uuid(name)
611 ret = findByName(lustre, name, "lov")
613 error("LOV: ", name, " already exists.")
615 mds_uuid = name2uuid(lustre, mds_name, 'mds')
616 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
617 lustre.appendChild(lov)
619 # add an lovconfig entry to the mds profile
620 lovconfig_name = new_name('LVCFG_' + name)
621 lovconfig_uuid = new_uuid(lovconfig_name)
622 node = mds2node(lustre, mds_name)
623 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
624 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
625 lustre.appendChild(lovconfig)
629 def add_mtpt(gen, lustre, options, args):
630 """ create mtpt on a node """
634 if options.has_key('node'):
635 node_name = options['node']
637 error("--mtpt requires a --node argument")
643 name = new_name('MNT_'+ node_name)
645 ret = findByName(lustre, name, "mountpoint")
647 error("MOUNTPOINT: ", name, " already exists.")
649 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
650 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
652 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
654 uuid = new_uuid(name)
655 mdc_name = new_name('MDC_'+mds_name)
656 mdc_uuid = new_uuid(mdc_name)
657 lovnode = lookup(lustre,lov_uuid)
660 mdsnode = lookup(lustre,mds_uuid)
661 ref = mdsnode.getElementsByTagName('network_ref')
662 net_uuid = ref[0].getAttribute('uuidref')
664 mdc = gen.mdc(mdc_name,mdc_uuid,mds_uuid,net_uuid)
665 lustre.appendChild(mdc)
668 lovnode.appendChild(gen.ref("mdc",mdc_uuid))
671 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
673 node = findByName(lustre, node_name, "node")
675 error('node:', node_name, "not found.")
676 node_add_profile(gen, node, "mountpoint", uuid)
677 lustre.appendChild(mtpt)
680 ############################################################
681 # Command line processing
683 def parse_cmdline(argv):
684 short_opts = "ho:i:m:"
685 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
686 "route", "router", "merge=", "format", "reformat", "output=",
687 "obdtype=", "obduuid=", "in=", "help", "batch="]
692 opts, args = getopt.getopt(argv, short_opts, long_opts)
698 # Commands to create new devices
714 options['router'] = 1
718 # Options for commands
720 options['obdtype'] = a
722 options['obduuid'] = a
724 options['tcpbuf'] = a
727 if o in ("-h", "--help"):
729 if o in ("-o", "--output"):
730 options['output'] = a
731 if o in ("-m", "--merge"):
734 options['format'] = 1
735 if o == "--reformat":
736 options['reformat'] = 1
739 if o in ("--in" , "-i"):
745 # simple class for profiling
752 self._start = time.time()
753 def stop(self, msg=''):
754 self._stop = time.time()
758 return self._stop - self._start
759 def display(self, msg):
761 str = '%s: %g secs' % (msg, d)
764 ############################################################
767 def do_command(gen, lustre, options, args):
768 if options.has_key('ost'):
769 add_ost(gen, lustre, options, args)
770 elif options.has_key('osc'):
771 add_osc(gen, lustre, options, args)
772 elif options.has_key('mtpt'):
773 add_mtpt(gen, lustre, options, args)
774 elif options.has_key('mds'):
775 add_mds(gen, lustre, options, args)
776 elif options.has_key('net'):
777 add_net(gen, lustre, options, args)
778 elif options.has_key('lov'):
779 add_lov(gen, lustre, options, args)
780 elif options.has_key('route'):
781 add_route(gen, lustre, options, args)
782 elif options.has_key('node'):
783 add_node(gen, lustre, options, args)
785 print "Missing command"
789 options, args = parse_cmdline(sys.argv[1:])
792 if options.has_key('merge'):
793 outFile = options['merge']
794 if os.access(outFile, os.R_OK):
795 doc = xml.dom.minidom.parse(outFile)
797 doc = new_lustre(xml.dom.minidom)
798 elif options.has_key('in'):
799 doc = xml.dom.minidom.parse(options['in'])
801 doc = new_lustre(xml.dom.minidom)
803 if options.has_key('output'):
804 outFile = options['output']
806 lustre = doc.documentElement
808 if lustre.tagName != "lustre":
809 print "Existing config not valid."
814 if options.has_key('batch'):
815 fp = open(options['batch'])
816 batchCommands = fp.readlines()
818 for cmd in batchCommands:
819 options, args = parse_cmdline(string.split(cmd))
820 do_command(gen, lustre, options, args)
822 do_command(gen, lustre, options, args)
827 PrettyPrint(doc, open(outFile,"w"))
829 if __name__ == "__main__":