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, toe, 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("ost", 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 mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
291 mtpt = self.newService("mountpoint", name, uuid)
292 mtpt.appendChild(self.ref("mds", mds_uuid))
293 mtpt.appendChild(self.ref("osc", osc_uuid))
294 self.addElement(mtpt, "path", path)
297 def echo_client(self, name, uuid, osc_uuid):
298 ec = self.newService("echo_client", name, uuid)
299 ec.appendChild(self.ref("osc", osc_uuid))
302 ############################################################
303 # Utilities to query a DOM tree
304 # Using this functions we can treat use config information
305 # directly as a database.
307 return n.getAttribute('name')
310 return node.getAttribute('uuid')
313 def findByName(lustre, name, tag = ""):
314 for n in lustre.childNodes:
315 if n.nodeType == n.ELEMENT_NODE:
316 if tag and n.nodeName != tag:
318 if getName(n) == name:
321 n = findByName(n, name)
326 def lookup(node, uuid):
327 for n in node.childNodes:
328 if n.nodeType == n.ELEMENT_NODE:
329 if getUUID(n) == uuid:
337 def mds2node(lustre, mds_name):
338 """ Find the node a MDS is configured on """
339 mds = findByName(lustre, mds_name, 'mds')
340 ref = mds.getElementsByTagName('node_ref')
342 error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
343 node_uuid = ref[0].getAttribute('uuidref')
344 node = lookup(lustre, node_uuid)
346 error('mds2node:', "no node found for :", '"'+mds_name+'"')
350 def name2uuid(lustre, name, tag="", fatal=1):
351 ret = findByName(lustre, name, tag)
354 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
360 # XXX: assumes only one network element per node. will fix this
361 # as soon as support for routers is added
362 def get_net_uuid(lustre, node_name):
363 """ get a network uuid for a node_name """
364 node = findByName(lustre, node_name, "node")
366 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
367 net = node.getElementsByTagName('network')
369 return getUUID(net[0])
373 def lov_add_osc(gen, lov, osc_uuid):
374 devs = lov.getElementsByTagName('devices')
376 devs[0].appendChild(gen.ref("osc", osc_uuid))
378 error("No devices element found for LOV:", lov)
381 def node_add_profile(gen, node, ref, uuid):
382 ret = node.getElementsByTagName('profile')
384 error('node has no profile:', node)
385 ret[0].appendChild(gen.ref(ref, uuid))
387 def get_attr(dom_node, attr, default=""):
388 v = dom_node.getAttribute(attr)
393 ############################################################
396 def do_add_node(gen, lustre, options, node_name):
397 uuid = new_uuid(node_name)
398 node = gen.node(node_name, uuid)
399 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
400 if options.has_key('router'):
401 node.setAttribute('router', '1')
402 lustre.appendChild(node)
406 def add_node(gen, lustre, options, args):
407 """ create a node with a network config """
411 node_name = options['node']
413 ret = findByName(lustre, node_name, "node")
415 print "Node:", node_name, "exists."
417 do_add_node(gen, lustre, options, node_name)
420 def add_net(gen, lustre, options, args):
421 """ create a node with a network config """
425 node_name = options['node']
431 if net_type in ('tcp', 'toe'):
436 if options.has_key('tcpbuf'):
437 tcpbuf = int(options['tcpbuf'])
438 elif net_type in ('elan', 'gm'):
441 print "Unknown net_type: ", net_type
444 ret = findByName(lustre, node_name, "node")
446 node = do_add_node(gen, lustre, options, node_name)
449 net_name = new_name('NET_'+ node_name +'_'+ net_type)
450 net_uuid = new_uuid(net_name)
451 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
452 node_add_profile(gen, node, "network", net_uuid)
455 def add_route(gen, lustre, options, args):
456 """ create a node with a network config """
460 node_name = options['node']
469 node = findByName(lustre, node_name, "node")
471 error (node_name, " not found.")
473 netlist = node.getElementsByTagName('network')
475 rlist = net.getElementsByTagName('route_tbl')
479 rtbl = gen.addElement(net, 'route_tbl')
480 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
483 def add_mds(gen, lustre, options, args):
489 if options.has_key('node'):
490 node_name = options['node']
492 error("--mds requires a --node argument")
494 if options.has_key('fstype'):
495 fstype = options['fstype']
497 mds_name = new_name(options['mds'])
498 if mds_name != options['mds']:
499 warning("name:", options['mds'], "already used. using:", mds_name)
506 mds_uuid = new_uuid(mds_name)
508 node_uuid = name2uuid(lustre, node_name, 'node')
510 node = findByName(lustre, node_name, "node")
511 node_add_profile(gen, node, "mds", mds_uuid)
512 net_uuid = get_net_uuid(lustre, node_name)
514 error("NODE: ", node_name, "not found")
516 mds = gen.mds(mds_name, mds_uuid, fstype, devname, get_format_flag(options),
517 net_uuid, node_uuid, dev_size=size)
518 lustre.appendChild(mds)
521 def add_ost(gen, lustre, options, args):
523 obdtype = 'obdfilter'
528 if options.has_key('node'):
529 node_name = options['node']
531 error("--ost requires a --node argument")
533 if options.has_key('lov'):
534 lovname = options['lov']
536 if options.has_key('obdtype'):
537 obdtype = options['obdtype']
538 if options.has_key('fstype'):
539 fstype = options['fstype']
540 if obdtype == 'obdecho':
549 obdname = new_name('OBD_'+ node_name)
550 oscname = new_name('OSC_'+ node_name)
551 ostname = new_name('OST_'+ node_name)
552 if options.has_key('obduuid'):
553 obd_uuid = options['obduuid']
554 obd = lookup(lustre, obd_uuid)
556 error("Duplicate OBD UUID:", obd_uuid)
558 obd_uuid = new_uuid(obdname)
559 ost_uuid = new_uuid(ostname)
560 osc_uuid = new_uuid(oscname)
562 net_uuid = get_net_uuid(lustre, node_name)
564 error("NODE: ", node_name, "not found")
566 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
567 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
568 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
571 lov = findByName(lustre, lovname, "lov")
573 error('add_ost:', '"'+lovname+'"', "lov element not found.")
574 lov_add_osc(gen, lov, osc_uuid)
576 node = findByName(lustre, node_name, "node")
577 node_add_profile(gen, node, 'obd', obd_uuid)
578 node_add_profile(gen, node, 'ost', ost_uuid)
580 lustre.appendChild(obd)
581 lustre.appendChild(osc)
582 lustre.appendChild(ost)
585 # this is generally only used by llecho.sh
586 def add_osc(gen, lustre, options, args):
587 """ add the osc to the profile for this node. """
591 if options.has_key('node'):
592 node_name = options['node']
594 error("--osc requires a --node argument")
595 osc_uuid = name2uuid(lustre, osc_name) # either 'osc' or 'lov'
596 node = findByName(lustre, node_name, "node")
597 node_add_profile(gen, node, 'osc', osc_uuid)
601 def add_echo_client(gen, lustre, options, args):
602 """ add an echo client to the profile for this node. """
606 if options.has_key('node'):
607 node_name = options['node']
609 error("--echo_client requires a --node argument")
610 node = findByName(lustre, node_name, "node")
612 echoname = new_name('ECHO_'+ node_name)
613 echo_uuid = new_uuid(echoname)
614 node_add_profile(gen, node, 'echo_client', echo_uuid)
616 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
618 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
620 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
621 lustre.appendChild(echo)
624 def add_lov(gen, lustre, options, args):
629 name = new_name(options['lov'])
630 if name != options['lov']:
631 warning("name:", options['lov'], "already used. using:", name)
635 stripe_count = args[2]
637 uuid = new_uuid(name)
639 ret = findByName(lustre, name, "lov")
641 error("LOV: ", name, " already exists.")
643 mds_uuid = name2uuid(lustre, mds_name, 'mds')
644 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_count, pattern)
645 lustre.appendChild(lov)
647 # add an lovconfig entry to the mds profile
648 lovconfig_name = new_name('LVCFG_' + name)
649 lovconfig_uuid = new_uuid(lovconfig_name)
650 node = mds2node(lustre, mds_name)
651 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
652 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
653 lustre.appendChild(lovconfig)
657 def add_mtpt(gen, lustre, options, args):
658 """ create mtpt on a node """
662 if options.has_key('node'):
663 node_name = options['node']
665 error("--mtpt requires a --node argument")
671 name = new_name('MNT_'+ node_name)
673 ret = findByName(lustre, name, "mountpoint")
675 error("MOUNTPOINT: ", name, " already exists.")
677 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
678 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
680 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
682 uuid = new_uuid(name)
683 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
684 node = findByName(lustre, node_name, "node")
686 error('node:', node_name, "not found.")
687 node_add_profile(gen, node, "mountpoint", uuid)
688 lustre.appendChild(mtpt)
691 ############################################################
692 # Command line processing
694 def parse_cmdline(argv):
695 short_opts = "ho:i:m:"
696 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net",
697 "echo_client", "tcpbuf=",
698 "route", "router", "merge=", "format", "reformat", "output=",
699 "obdtype=", "fstype=", "obduuid=", "in=", "help", "batch="]
704 opts, args = getopt.getopt(argv, short_opts, long_opts)
710 # Commands to create new devices
715 if o == "--echo_client":
716 options['echo_client'] = 1
728 options['router'] = 1
732 # Options for commands
734 options['obdtype'] = a
736 options['fstype'] = a
738 options['obduuid'] = a
740 options['tcpbuf'] = a
743 if o in ("-h", "--help"):
745 if o in ("-o", "--output"):
746 options['output'] = a
747 if o in ("-m", "--merge"):
750 options['format'] = 1
751 if o == "--reformat":
752 options['reformat'] = 1
755 if o in ("--in" , "-i"):
761 # simple class for profiling
768 self._start = time.time()
769 def stop(self, msg=''):
770 self._stop = time.time()
774 return self._stop - self._start
775 def display(self, msg):
777 str = '%s: %g secs' % (msg, d)
780 ############################################################
783 def do_command(gen, lustre, options, args):
784 if options.has_key('ost'):
785 add_ost(gen, lustre, options, args)
786 elif options.has_key('osc'):
787 add_osc(gen, lustre, options, args)
788 elif options.has_key('echo_client'):
789 add_echo_client(gen, lustre, options, args)
790 elif options.has_key('mtpt'):
791 add_mtpt(gen, lustre, options, args)
792 elif options.has_key('mds'):
793 add_mds(gen, lustre, options, args)
794 elif options.has_key('net'):
795 add_net(gen, lustre, options, args)
796 elif options.has_key('lov'):
797 add_lov(gen, lustre, options, args)
798 elif options.has_key('route'):
799 add_route(gen, lustre, options, args)
800 elif options.has_key('node'):
801 add_node(gen, lustre, options, args)
803 print "Missing command"
807 options, args = parse_cmdline(sys.argv[1:])
810 if options.has_key('merge'):
811 outFile = options['merge']
812 if os.access(outFile, os.R_OK):
813 doc = xml.dom.minidom.parse(outFile)
815 doc = new_lustre(xml.dom.minidom)
816 elif options.has_key('in'):
817 doc = xml.dom.minidom.parse(options['in'])
819 doc = new_lustre(xml.dom.minidom)
821 if options.has_key('output'):
822 outFile = options['output']
824 lustre = doc.documentElement
826 if lustre.tagName != "lustre":
827 print "Existing config not valid."
832 if options.has_key('batch'):
833 fp = open(options['batch'])
834 batchCommands = fp.readlines()
836 for cmd in batchCommands:
837 options, args = parse_cmdline(string.split(cmd))
838 do_command(gen, lustre, options, args)
840 do_command(gen, lustre, options, args)
845 PrettyPrint(doc, open(outFile,"w"))
847 if __name__ == "__main__":