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, 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):
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))
210 def route(self, lo, hi):
211 """ create one entry for the route table """
212 ref = self.doc.createElement('route')
213 ref.setAttribute("lo", lo)
215 ref.setAttribute("hi", hi)
218 def node(self, name, uuid):
219 """ create a host """
220 node = self.newService("node", name, uuid)
221 self.addElement(node, 'profile')
224 def ldlm(self, name, uuid):
225 """ create a ldlm """
226 ldlm = self.newService("ldlm", name, uuid)
229 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
230 obd = self.newService("obd", name, uuid)
231 obd.setAttribute('type', obdtype)
233 self.addElement(obd, "fstype", fs)
235 dev = self.addElement(obd, "device", devname)
237 dev.setAttribute("size", "%s" % (dev_size))
238 self.addElement(obd, "autoformat", format)
241 def osc(self, name, uuid, obd_uuid, net_uuid):
242 osc = self.newService("osc", name, uuid)
243 osc.appendChild(self.ref("ost", net_uuid))
244 osc.appendChild(self.ref("obd", obd_uuid))
247 def ost(self, name, uuid, obd_uuid, net_uuid):
248 ost = self.newService("ost", name, uuid)
249 ost.appendChild(self.ref("network", net_uuid))
250 ost.appendChild(self.ref("obd", obd_uuid))
253 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
254 lov = self.newService("lov", name, uuid)
255 lov.appendChild(self.ref("mds", mds_uuid))
256 devs = self.addElement(lov, "devices" )
257 devs.setAttribute("stripesize", stripe_sz)
258 devs.setAttribute("stripeoffset", stripe_off)
259 devs.setAttribute("pattern", pattern)
262 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
263 failover_uuid = "", dev_size=0 ):
264 mds = self.newService("mds", name, uuid)
265 self.addElement(mds, "fstype", fs)
266 dev = self.addElement(mds, "device", devname)
268 dev.setAttribute("size", "%s" % (dev_size))
269 self.addElement(mds, "autoformat", format)
270 mds.appendChild(self.ref("network", net_uuid))
271 mds.appendChild(self.ref("node", node_uuid))
273 mds.appendChild(self.ref("failover", failover_uuid))
276 def mdc(self, name, uuid, mds_uuid):
277 mdc = self.newService("mdc", name, uuid)
278 mdc.appendChild(self.ref("mds", mds_uuid))
281 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
282 mtpt = self.newService("mountpoint", name, uuid)
283 mtpt.appendChild(self.ref("mdc", mdc_uuid))
284 mtpt.appendChild(self.ref("osc", osc_uuid))
285 self.addElement(mtpt, "path", path)
288 ############################################################
289 # Utilities to query a DOM tree
290 # Using this functions we can treat use config information
291 # directly as a database.
293 return n.getAttribute('name')
296 return node.getAttribute('uuid')
299 def findByName(lustre, name, tag = ""):
300 for n in lustre.childNodes:
301 if n.nodeType == n.ELEMENT_NODE:
302 if tag and n.nodeName != tag:
304 if getName(n) == name:
307 n = findByName(n, name)
312 def lookup(node, uuid):
313 for n in node.childNodes:
314 if n.nodeType == n.ELEMENT_NODE:
315 if getUUID(n) == uuid:
323 def mds2node(lustre, mds_name):
324 """ Find the node a MDS is configured on """
325 mds = findByName(lustre, mds_name, 'mds')
326 ref = mds.getElementsByTagName('node_ref')
328 error("no node found for:", mds_name)
329 node_uuid = ref[0].getAttribute('uuidref')
330 node = lookup(lustre, node_uuid)
332 error("no node found for :", mds_name)
336 def name2uuid(lustre, name, tag="", fatal=1):
337 ret = findByName(lustre, name, tag)
340 error('name2uuid:', name, "not found.")
346 # XXX: assumes only one network element per node. will fix this
347 # as soon as support for routers is added
348 def get_net_uuid(lustre, node_name):
349 """ get a network uuid for a node_name """
350 node = findByName(lustre, node_name, "node")
352 error ("node not found:", node_name)
353 net = node.getElementsByTagName('network')
355 return getUUID(net[0])
359 def lov_add_osc(gen, lov, osc_uuid):
360 devs = lov.getElementsByTagName('devices')
362 devs[0].appendChild(gen.ref("osc", osc_uuid))
364 error("No devices element found for LOV:", lov)
367 def node_add_profile(gen, node, ref, uuid):
368 ret = node.getElementsByTagName('profile')
370 error('node has no profile:', node)
371 ret[0].appendChild(gen.ref(ref, uuid))
373 def get_attr(dom_node, attr, default=""):
374 v = dom_node.getAttribute(attr)
379 ############################################################
382 def do_add_node(gen, lustre, options, node_name):
383 uuid = new_uuid(node_name)
384 node = gen.node(node_name, uuid)
385 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
386 if options.has_key('router'):
387 node.setAttribute('router', '1')
388 node_add_profile(gen, node, "ptlrouter", 'PTLROUTER_UUID')
389 lustre.appendChild(node)
393 def add_node(gen, lustre, options, args):
394 """ create a node with a network config """
398 node_name = options['node']
400 ret = findByName(lustre, node_name, "node")
402 print "Node:", node_name, "exists."
404 do_add_node(gen, lustre, options, node_name)
407 def add_net(gen, lustre, options, args):
408 """ create a node with a network config """
412 node_name = options['node']
416 if net_type == 'tcp':
421 # add send, recv buffer size here
422 elif net_type in ('elan', 'gm'):
425 print "Unknown net_type: ", net_type
428 ret = findByName(lustre, node_name, "node")
430 node = do_add_node(gen, lustre, options, node_name)
433 net_name = new_name('NET_'+ node_name +'_'+ net_type)
434 net_uuid = new_uuid(net_name)
435 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port))
436 node_add_profile(gen, node, "network", net_uuid)
439 def add_route(gen, lustre, options, args):
440 """ create a node with a network config """
444 node_name = options['node']
453 node = findByName(lustre, node_name, "node")
455 error (node_name, " not found.")
457 netlist = node.getElementsByTagName('network')
459 if get_attr(net, 'type') == net_type:
460 rlist = net.getElementsByTagName('route_tbl')
464 rtbl = gen.addElement(net, 'route_tbl')
465 rtbl.appendChild(gen.route(lo, hi))
468 def add_mds(gen, lustre, options, args):
472 if options.has_key('node'):
473 node_name = options['node']
475 error("--mds requires a --node argument")
477 mds_name = new_name(options['mds'])
484 mdc_name = 'MDC_' + mds_name
485 mds_uuid = new_uuid(mds_name)
486 mdc_uuid = new_uuid(mdc_name)
488 node_uuid = name2uuid(lustre, node_name)
490 node = findByName(lustre, node_name, "node")
491 node_add_profile(gen, node, "mds", mds_uuid)
492 net_uuid = get_net_uuid(lustre, node_name)
494 error("NODE: ", node_name, "not found")
497 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
498 net_uuid, node_uuid, dev_size=size)
499 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
500 lustre.appendChild(mds)
501 lustre.appendChild(mdc)
504 def add_mdc(gen, lustre, options, args):
505 """ create mtpt on a node """
509 if options.has_key('node'):
510 node_name = options['node']
512 error("--mdc requires a --node argument")
515 mdc_uuid = name2uuid(lustre, mdc_name)
517 node = findByName(lustre, node_name, "node")
519 error('node:', node_name, "not found.")
520 node_add_profile(gen, node, "mdc", mdc_uuid)
523 def add_ost(gen, lustre, options, args):
525 obdtype = 'obdfilter'
530 if options.has_key('node'):
531 node_name = options['node']
533 error("--ost requires a --node argument")
535 if options.has_key('lov'):
536 lovname = options['lov']
538 if options.has_key('obdtype'):
539 obdtype = options['obdtype']
541 if obdtype == 'obdecho':
550 obdname = new_name('OBD_'+ node_name)
551 oscname = new_name('OSC_'+ node_name)
552 ostname = new_name('OST_'+ node_name)
553 obd_uuid = new_uuid(obdname)
554 ost_uuid = new_uuid(ostname)
555 osc_uuid = new_uuid(oscname)
557 net_uuid = get_net_uuid(lustre, node_name)
559 error("NODE: ", node_name, "not found")
561 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
562 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
563 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
566 lov = findByName(lustre, lovname, "lov")
568 error("LOV:", lovname, "not found.")
569 lov_add_osc(gen, lov, osc_uuid)
571 node = findByName(lustre, node_name, "node")
572 node_add_profile(gen, node, 'obd', obd_uuid)
573 node_add_profile(gen, node, 'ost', ost_uuid)
575 lustre.appendChild(obd)
576 lustre.appendChild(osc)
577 lustre.appendChild(ost)
580 # this is generally only used by llecho.sh
581 def add_osc(gen, lustre, options, args):
582 """ add the osc to the profile for this node. """
586 if options.has_key('node'):
587 node_name = options['node']
589 error("--osc requires a --node argument")
590 osc_uuid = name2uuid(lustre, osc_name)
591 node = findByName(lustre, node_name, "node")
592 node_add_profile(gen, node, 'osc', osc_uuid)
595 def add_lov(gen, lustre, options, args):
600 name = options['lov']
605 uuid = new_uuid(name)
607 ret = findByName(lustre, name, "lov")
609 error("LOV: ", name, " already exists.")
611 mds_uuid = name2uuid(lustre, mds_name)
613 node = mds2node(lustre, mds_name)
614 node_add_profile(gen, node, "lov", uuid)
615 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
616 lustre.appendChild(lov)
619 def add_mtpt(gen, lustre, options, args):
620 """ create mtpt on a node """
624 if options.has_key('node'):
625 node_name = options['node']
627 error("--mtpt requires a --node argument")
632 mdc_name = 'MDC_' + mds_name
634 name = new_name('MNT_'+ node_name)
636 ret = findByName(lustre, name, "mountpoint")
638 error("MOUNTPOINT: ", name, " already exists.")
640 mdc_uuid = name2uuid(lustre, mdc_name)
641 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
643 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
645 uuid = new_uuid(name)
646 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
647 node = findByName(lustre, node_name, "node")
649 error('node:', node_name, "not found.")
650 node_add_profile(gen, node, "mountpoint", uuid)
651 node_add_profile(gen, node, "mdc", mdc_uuid)
652 lustre.appendChild(mtpt)
655 ############################################################
656 # Command line processing
658 def parse_cmdline(argv):
659 short_opts = "ho:i:m:"
660 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net",
661 "mdc", "route", "router", "merge=", "format", "reformat", "output=",
662 "obdtype=", "in=", "help"]
667 opts, args = getopt.getopt(argv, short_opts, long_opts)
673 if o in ("-h", "--help"):
675 if o in ("-o", "--output"):
676 options['output'] = a
694 options['router'] = 1
697 if o in ("-m", "--merge"):
700 options['obdtype'] = a
702 options['format'] = 1
703 if o == "--reformat":
704 options['reformat'] = 1
705 if o in ("--in" , "-i"):
711 # simple class for profiling
718 self._start = time.time()
719 def stop(self, msg=''):
720 self._stop = time.time()
724 return self._stop - self._start
725 def display(self, msg):
727 str = '%s: %g secs' % (msg, d)
730 ############################################################
734 options, args = parse_cmdline(sys.argv[1:])
737 if options.has_key('merge'):
738 outFile = options['merge']
739 doc = xml.dom.minidom.parse(outFile)
740 elif options.has_key('in'):
741 doc = xml.dom.minidom.parse(options['in'])
743 doc = new_lustre(xml.dom.minidom)
745 if options.has_key('output'):
746 outFile = options['output']
748 lustre = doc.documentElement
750 if lustre.tagName != "lustre":
751 print "Existing config not valid."
755 if options.has_key('ost'):
756 add_ost(gen, lustre, options, args)
757 elif options.has_key('osc'):
758 add_osc(gen, lustre, options, args)
759 elif options.has_key('mtpt'):
760 add_mtpt(gen, lustre, options, args)
761 elif options.has_key('mds'):
762 add_mds(gen, lustre, options, args)
763 elif options.has_key('mdc'):
764 add_mdc(gen, lustre, options, args)
765 elif options.has_key('net'):
766 add_net(gen, lustre, options, args)
767 elif options.has_key('lov'):
768 add_lov(gen, lustre, options, args)
769 elif options.has_key('route'):
770 add_route(gen, lustre, options, args)
771 elif options.has_key('node'):
772 add_node(gen, lustre, options, args)
774 print "Missing command"
780 PrettyPrint(doc, open(outFile,"w"))
782 if __name__ == "__main__":