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 [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 NB: The autoformat option has been disabled until a safe
104 method is implemented to determine if a block device has a
106 --reformat Reformat partitions (this should be an lconf arg,
108 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
109 This is only useful for the --ost command.
110 The device parameters are ignored for the obdecho type.
115 msg = string.join(map(str,args))
120 # manage names and uuids
121 # need to initialize this by walking tree to ensure
122 # no duplicate names or uuids are created.
123 # this are just place holders for now.
124 # consider changing this to be like OBD-dev-host
128 while names.has_key(ret):
129 ret = "%s_%d" % (base, ctr)
135 return "%s_UUID" % (name)
138 ldlm_uuid = 'ldlm_UUID'
140 """Create a new empty lustre document"""
141 # adding ldlm here is a bit of a hack, but one is enough.
143 <ldlm name="%s" uuid="%s"/>
144 </lustre>""" % (ldlm_name, ldlm_uuid)
145 return dom.parseString(str)
151 """initialize auto-name generation tables"""
153 # get all elements that contain a name attribute
154 for n in doc.childNodes:
155 if n.nodeType == n.ELEMENT_NODE:
157 names[getName(n)] = 1
158 uuids[getUUID(n)] = 1
161 def get_format_flag(options):
162 if options.has_key('format'):
163 if options['format']:
167 ############################################################
168 # Build config objects using DOM
173 def __init__(self, doc):
176 def ref(self, type, uuid):
177 """ generate <[type]_ref uuidref="[uuid]"/> """
178 tag = "%s_ref" % (type)
179 ref = self.doc.createElement(tag)
180 ref.setAttribute("uuidref", uuid)
183 def newService(self, tag, name, uuid):
184 """ create a new service elmement, which requires name and uuid attributes """
185 new = self.doc.createElement(tag)
186 new.setAttribute("name", name);
187 new.setAttribute("uuid", uuid);
190 def addText(self, node, str):
191 txt = self.doc.createTextNode(str)
192 node.appendChild(txt)
194 def addElement(self, node, tag, str=None):
195 """ create a new element and add it as a child to node. If str is passed,
196 a text node is created for the new element"""
197 new = self.doc.createElement(tag)
199 self.addText(new, str)
200 node.appendChild(new)
203 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
204 """create <network> node"""
205 network = self.newService("network", name, uuid)
206 network.setAttribute("type", net);
207 self.addElement(network, "server", hostname)
209 self.addElement(network, "port", "%d" %(port))
211 self.addElement(network, "send_mem", "%d" %(tcpbuf))
212 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
216 def route(self, net_type, gw, lo, hi):
217 """ create one entry for the route table """
218 ref = self.doc.createElement('route')
219 ref.setAttribute("type", net_type)
220 ref.setAttribute("gw", gw)
221 ref.setAttribute("lo", lo)
223 ref.setAttribute("hi", hi)
226 def node(self, name, uuid):
227 """ create a host """
228 node = self.newService("node", name, uuid)
229 self.addElement(node, 'profile')
232 def ldlm(self, name, uuid):
233 """ create a ldlm """
234 ldlm = self.newService("ldlm", name, uuid)
237 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
238 obd = self.newService("obd", name, uuid)
239 obd.setAttribute('type', obdtype)
241 self.addElement(obd, "fstype", fs)
243 dev = self.addElement(obd, "device", devname)
245 dev.setAttribute("size", "%s" % (dev_size))
246 self.addElement(obd, "autoformat", format)
249 def osc(self, name, uuid, obd_uuid, net_uuid):
250 osc = self.newService("osc", name, uuid)
251 osc.appendChild(self.ref("ost", net_uuid))
252 osc.appendChild(self.ref("obd", obd_uuid))
255 def ost(self, name, uuid, obd_uuid, net_uuid):
256 ost = self.newService("ost", name, uuid)
257 ost.appendChild(self.ref("network", net_uuid))
258 ost.appendChild(self.ref("obd", obd_uuid))
261 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
262 lov = self.newService("lov", name, uuid)
263 lov.appendChild(self.ref("mds", mds_uuid))
264 devs = self.addElement(lov, "devices" )
265 devs.setAttribute("stripesize", stripe_sz)
266 devs.setAttribute("stripeoffset", stripe_off)
267 devs.setAttribute("pattern", pattern)
270 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
271 failover_uuid = "", dev_size=0 ):
272 mds = self.newService("mds", name, uuid)
273 self.addElement(mds, "fstype", fs)
274 dev = self.addElement(mds, "device", devname)
276 dev.setAttribute("size", "%s" % (dev_size))
277 self.addElement(mds, "autoformat", format)
278 mds.appendChild(self.ref("network", net_uuid))
279 mds.appendChild(self.ref("node", node_uuid))
281 mds.appendChild(self.ref("failover", failover_uuid))
284 def mdc(self, name, uuid, mds_uuid):
285 mdc = self.newService("mdc", name, uuid)
286 mdc.appendChild(self.ref("mds", mds_uuid))
289 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
290 mtpt = self.newService("mountpoint", name, uuid)
291 mtpt.appendChild(self.ref("mdc", mdc_uuid))
292 mtpt.appendChild(self.ref("osc", osc_uuid))
293 self.addElement(mtpt, "path", path)
296 ############################################################
297 # Utilities to query a DOM tree
298 # Using this functions we can treat use config information
299 # directly as a database.
301 return n.getAttribute('name')
304 return node.getAttribute('uuid')
307 def findByName(lustre, name, tag = ""):
308 for n in lustre.childNodes:
309 if n.nodeType == n.ELEMENT_NODE:
310 if tag and n.nodeName != tag:
312 if getName(n) == name:
315 n = findByName(n, name)
320 def lookup(node, uuid):
321 for n in node.childNodes:
322 if n.nodeType == n.ELEMENT_NODE:
323 if getUUID(n) == uuid:
331 def mds2node(lustre, mds_name):
332 """ Find the node a MDS is configured on """
333 mds = findByName(lustre, mds_name, 'mds')
334 ref = mds.getElementsByTagName('node_ref')
336 error("no node found for:", mds_name)
337 node_uuid = ref[0].getAttribute('uuidref')
338 node = lookup(lustre, node_uuid)
340 error("no node found for :", mds_name)
344 def name2uuid(lustre, name, tag="", fatal=1):
345 ret = findByName(lustre, name, tag)
348 error('name2uuid:', name, "not found.")
354 # XXX: assumes only one network element per node. will fix this
355 # as soon as support for routers is added
356 def get_net_uuid(lustre, node_name):
357 """ get a network uuid for a node_name """
358 node = findByName(lustre, node_name, "node")
360 error ("node not found:", node_name)
361 net = node.getElementsByTagName('network')
363 return getUUID(net[0])
367 def lov_add_osc(gen, lov, osc_uuid):
368 devs = lov.getElementsByTagName('devices')
370 devs[0].appendChild(gen.ref("osc", osc_uuid))
372 error("No devices element found for LOV:", lov)
375 def node_add_profile(gen, node, ref, uuid):
376 ret = node.getElementsByTagName('profile')
378 error('node has no profile:', node)
379 ret[0].appendChild(gen.ref(ref, uuid))
381 def get_attr(dom_node, attr, default=""):
382 v = dom_node.getAttribute(attr)
387 ############################################################
390 def do_add_node(gen, lustre, options, node_name):
391 uuid = new_uuid(node_name)
392 node = gen.node(node_name, uuid)
393 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
394 if options.has_key('router'):
395 node.setAttribute('router', '1')
396 lustre.appendChild(node)
400 def add_node(gen, lustre, options, args):
401 """ create a node with a network config """
405 node_name = options['node']
407 ret = findByName(lustre, node_name, "node")
409 print "Node:", node_name, "exists."
411 do_add_node(gen, lustre, options, node_name)
414 def add_net(gen, lustre, options, args):
415 """ create a node with a network config """
419 node_name = options['node']
425 if net_type == 'tcp':
430 if options.has_key('tcpbuf'):
431 tcpbuf = int(options['tcpbuf'])
432 elif net_type in ('elan', 'gm'):
435 print "Unknown net_type: ", net_type
438 ret = findByName(lustre, node_name, "node")
440 node = do_add_node(gen, lustre, options, node_name)
443 net_name = new_name('NET_'+ node_name +'_'+ net_type)
444 net_uuid = new_uuid(net_name)
445 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
446 node_add_profile(gen, node, "network", net_uuid)
449 def add_route(gen, lustre, options, args):
450 """ create a node with a network config """
454 node_name = options['node']
463 node = findByName(lustre, node_name, "node")
465 error (node_name, " not found.")
467 netlist = node.getElementsByTagName('network')
469 rlist = net.getElementsByTagName('route_tbl')
473 rtbl = gen.addElement(net, 'route_tbl')
474 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
477 def add_mds(gen, lustre, options, args):
481 if options.has_key('node'):
482 node_name = options['node']
484 error("--mds requires a --node argument")
486 mds_name = new_name(options['mds'])
493 mdc_name = 'MDC_' + mds_name
494 mds_uuid = new_uuid(mds_name)
495 mdc_uuid = new_uuid(mdc_name)
497 node_uuid = name2uuid(lustre, node_name)
499 node = findByName(lustre, node_name, "node")
500 node_add_profile(gen, node, "mds", mds_uuid)
501 net_uuid = get_net_uuid(lustre, node_name)
503 error("NODE: ", node_name, "not found")
506 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
507 net_uuid, node_uuid, dev_size=size)
508 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
509 lustre.appendChild(mds)
510 lustre.appendChild(mdc)
513 def add_mdc(gen, lustre, options, args):
514 """ create mtpt on a node """
518 if options.has_key('node'):
519 node_name = options['node']
521 error("--mdc requires a --node argument")
524 mdc_uuid = name2uuid(lustre, mdc_name)
526 node = findByName(lustre, node_name, "node")
528 error('node:', node_name, "not found.")
529 node_add_profile(gen, node, "mdc", mdc_uuid)
532 def add_ost(gen, lustre, options, args):
534 obdtype = 'obdfilter'
539 if options.has_key('node'):
540 node_name = options['node']
542 error("--ost requires a --node argument")
544 if options.has_key('lov'):
545 lovname = options['lov']
547 if options.has_key('obdtype'):
548 obdtype = options['obdtype']
550 if obdtype == 'obdecho':
559 obdname = new_name('OBD_'+ node_name)
560 oscname = new_name('OSC_'+ node_name)
561 ostname = new_name('OST_'+ node_name)
562 obd_uuid = new_uuid(obdname)
563 ost_uuid = new_uuid(ostname)
564 osc_uuid = new_uuid(oscname)
566 net_uuid = get_net_uuid(lustre, node_name)
568 error("NODE: ", node_name, "not found")
570 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
571 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
572 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
575 lov = findByName(lustre, lovname, "lov")
577 error("LOV:", lovname, "not found.")
578 lov_add_osc(gen, lov, osc_uuid)
580 node = findByName(lustre, node_name, "node")
581 node_add_profile(gen, node, 'obd', obd_uuid)
582 node_add_profile(gen, node, 'ost', ost_uuid)
584 lustre.appendChild(obd)
585 lustre.appendChild(osc)
586 lustre.appendChild(ost)
589 # this is generally only used by llecho.sh
590 def add_osc(gen, lustre, options, args):
591 """ add the osc to the profile for this node. """
595 if options.has_key('node'):
596 node_name = options['node']
598 error("--osc requires a --node argument")
599 osc_uuid = name2uuid(lustre, osc_name)
600 node = findByName(lustre, node_name, "node")
601 node_add_profile(gen, node, 'osc', osc_uuid)
604 def add_lov(gen, lustre, options, args):
609 name = options['lov']
614 uuid = new_uuid(name)
616 ret = findByName(lustre, name, "lov")
618 error("LOV: ", name, " already exists.")
620 mds_uuid = name2uuid(lustre, mds_name)
622 node = mds2node(lustre, mds_name)
623 node_add_profile(gen, node, "lov", uuid)
624 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
625 lustre.appendChild(lov)
628 def add_mtpt(gen, lustre, options, args):
629 """ create mtpt on a node """
633 if options.has_key('node'):
634 node_name = options['node']
636 error("--mtpt requires a --node argument")
641 mdc_name = 'MDC_' + mds_name
643 name = new_name('MNT_'+ node_name)
645 ret = findByName(lustre, name, "mountpoint")
647 error("MOUNTPOINT: ", name, " already exists.")
649 mdc_uuid = name2uuid(lustre, mdc_name)
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 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
656 node = findByName(lustre, node_name, "node")
658 error('node:', node_name, "not found.")
659 node_add_profile(gen, node, "mountpoint", uuid)
660 node_add_profile(gen, node, "mdc", mdc_uuid)
661 lustre.appendChild(mtpt)
664 ############################################################
665 # Command line processing
667 def parse_cmdline(argv):
668 short_opts = "ho:i:m:"
669 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=",
670 "mdc", "route", "router", "merge=", "format", "reformat", "output=",
671 "obdtype=", "in=", "help"]
676 opts, args = getopt.getopt(argv, short_opts, long_opts)
682 if o in ("-h", "--help"):
684 if o in ("-o", "--output"):
685 options['output'] = a
703 options['router'] = 1
706 if o in ("-m", "--merge"):
709 options['obdtype'] = a
711 options['tcpbuf'] = a
713 options['format'] = 1
714 if o == "--reformat":
715 options['reformat'] = 1
716 if o in ("--in" , "-i"):
722 # simple class for profiling
729 self._start = time.time()
730 def stop(self, msg=''):
731 self._stop = time.time()
735 return self._stop - self._start
736 def display(self, msg):
738 str = '%s: %g secs' % (msg, d)
741 ############################################################
745 options, args = parse_cmdline(sys.argv[1:])
748 if options.has_key('merge'):
749 outFile = options['merge']
750 if os.access(outFile, os.R_OK):
751 doc = xml.dom.minidom.parse(outFile)
753 doc = new_lustre(xml.dom.minidom)
754 elif options.has_key('in'):
755 doc = xml.dom.minidom.parse(options['in'])
757 doc = new_lustre(xml.dom.minidom)
759 if options.has_key('output'):
760 outFile = options['output']
762 lustre = doc.documentElement
764 if lustre.tagName != "lustre":
765 print "Existing config not valid."
769 if options.has_key('ost'):
770 add_ost(gen, lustre, options, args)
771 elif options.has_key('osc'):
772 add_osc(gen, lustre, options, args)
773 elif options.has_key('mtpt'):
774 add_mtpt(gen, lustre, options, args)
775 elif options.has_key('mds'):
776 add_mds(gen, lustre, options, args)
777 elif options.has_key('mdc'):
778 add_mdc(gen, lustre, options, args)
779 elif options.has_key('net'):
780 add_net(gen, lustre, options, args)
781 elif options.has_key('lov'):
782 add_lov(gen, lustre, options, args)
783 elif options.has_key('route'):
784 add_route(gen, lustre, options, args)
785 elif options.has_key('node'):
786 add_node(gen, lustre, options, args)
788 print "Missing command"
794 PrettyPrint(doc, open(outFile,"w"))
796 if __name__ == "__main__":