3 # Copyright (C) 2002 Cluster File Systems, Inc.
4 # Author: Robert Read <rread@clusterfs.com>
6 # This file is part of Lustre, http://www.lustre.org.
8 # Lustre is free software; you can redistribute it and/or
9 # modify it under the terms of version 2 of the GNU General Public
10 # License as published by the Free Software Foundation.
12 # Lustre is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Lustre; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 lmc - lustre configurtion data manager
25 Basic plan for lmc usage:
27 ./lmc --output config.xml --node server --net server1 tcp
28 ./lmc --merge config.xml --node client --net client1 tcp
32 ./lmc --merge config.xml --node server --mds mds1 /tmp/mds1 50000
35 ./lmc --merge config.xml --lov lov1 mds1 65536 0 0
36 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost1 100000
37 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost2 100000
39 # create client config
40 ./lmc --merge config.xml --node client --mtpt /mnt/lustre mds1 lov1
44 import sys, getopt, string
45 import xml.dom.minidom
46 from xml.dom.ext import PrettyPrint
49 DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use?
52 print """usage: lmc [--node --ost | --mtpt | --lov] args
55 Node_name by itself it will create a new node. When used with other
56 commands it specifies the node to modify
58 --net hostname nettype [port, recv_buf, send_buf]
59 Nettype is either tcp, elan, or gm.
60 Requires a node argument
63 Create a MDS using the device
66 --lov lov_name [mdc_name stripe_sz stripe_off pattern]
67 Creates a logical volume
68 When used with other commands, it specifics the lov to modify
71 Configures a MDC for a node.
75 Creates an OBD/OST/OSC configuration triplet for a new device.
76 When used on "host", the device will be initialized and the OST
77 will be enabled. On client nodes, the OSC will be avaiable.
79 If --lov lov_name is used, this device is added to lov.
81 --mtpt /mnt/point mds_name lov_name|osc_name
82 Creates a client mount point.
86 --merge="xml file" Add the new objects to an existing file
87 --format Format the partitions if unformated
88 --reformat Reformat partitions (this should be an lconf arg,
90 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
91 This is only useful for the --ost command.
92 The device parameters are ignored for the obdecho type.
97 msg = string.join(map(str,args))
102 # manage names and uuids
103 # need to initialize this by walking tree to ensure
104 # no duplicate names or uuids are created.
105 # this are just place holders for now.
106 # consider changing this to be like OBD-dev-host
110 while names.has_key(ret):
111 ret = "%s_%d" % (base, ctr)
117 return "%s_UUID" % (name)
120 ldlm_uuid = 'ldlm_UUID'
122 """Create a new empty lustre document"""
123 # adding ldlm here is a bit of a hack, but one is enough.
124 str = """<lustre> <ldlm name="%s" uuid="%s"/> </lustre>""" % (ldlm_name, ldlm_uuid)
125 return dom.parseString(str)
131 """initialize auto-name generation tables"""
133 # get all elements that contain a name attribute
134 for n in doc.childNodes:
135 if n.nodeType == n.ELEMENT_NODE:
137 names[getName(n)] = 1
138 uuids[getUUID(n)] = 1
141 def get_format_flag(options):
142 if options.has_key('format'):
143 if options['format']:
150 def __init__(self, doc):
153 def ref(self, type, uuid):
154 """ generate <[type]_ref uuidref="[uuid]"/> """
155 tag = "%s_ref" % (type)
156 ref = self.doc.createElement(tag)
157 ref.setAttribute("uuidref", uuid)
160 def newService(self, tag, name, uuid):
161 """ create a new service elmement, which requires name and uuid attributes """
162 new = self.doc.createElement(tag)
163 new.setAttribute("name", name);
164 new.setAttribute("uuid", uuid);
167 def addText(self, node, str):
168 txt = self.doc.createTextNode(str)
169 node.appendChild(txt)
171 def addElement(self, node, tag, str=None):
172 """ create a new element and add it as a child to node. If str is passed,
173 a text node is created for the new element"""
174 new = self.doc.createElement(tag)
176 self.addText(new, str)
177 node.appendChild(new)
180 def network(self, name, uuid, hostname, net, port=0):
181 """create <network> node"""
182 network = self.newService("network", name, uuid)
183 network.setAttribute("type", net);
184 self.addElement(network, "server", hostname)
186 self.addElement(network, "port", "%d" %(port))
189 def node(self, name, uuid):
190 """ create a host """
191 node = self.newService("node", name, uuid)
192 self.addElement(node, 'profile')
195 def ldlm(self, name, uuid):
196 """ create a ldlm """
197 ldlm = self.newService("ldlm", name, uuid)
200 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
201 obd = self.newService("obd", name, uuid)
202 obd.setAttribute('type', obdtype)
204 self.addElement(obd, "fstype", fs)
206 dev = self.addElement(obd, "device", devname)
208 dev.setAttribute("size", "%s" % (dev_size))
209 self.addElement(obd, "autoformat", format)
212 def osc(self, name, uuid, obd_uuid, net_uuid):
213 osc = self.newService("osc", name, uuid)
214 osc.appendChild(self.ref("ost", net_uuid))
215 osc.appendChild(self.ref("obd", obd_uuid))
218 def ost(self, name, uuid, obd_uuid, net_uuid):
219 ost = self.newService("ost", name, uuid)
220 ost.appendChild(self.ref("network", net_uuid))
221 ost.appendChild(self.ref("obd", obd_uuid))
224 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
225 lov = self.newService("lov", name, uuid)
226 lov.appendChild(self.ref("mds", mds_uuid))
227 devs = self.addElement(lov, "devices" )
228 devs.setAttribute("stripesize", stripe_sz)
229 devs.setAttribute("stripeoffset", stripe_off)
230 devs.setAttribute("pattern", pattern)
233 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
234 failover_uuid = "", dev_size=0 ):
235 mds = self.newService("mds", name, uuid)
236 self.addElement(mds, "fstype", fs)
237 dev = self.addElement(mds, "device", devname)
239 dev.setAttribute("size", "%s" % (dev_size))
240 self.addElement(mds, "autoformat", format)
241 mds.appendChild(self.ref("network", net_uuid))
242 mds.appendChild(self.ref("node", node_uuid))
244 mds.appendChild(self.ref("failover", failover_uuid))
247 def mdc(self, name, uuid, mds_uuid):
248 mdc = self.newService("mdc", name, uuid)
249 mdc.appendChild(self.ref("mds", mds_uuid))
252 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
253 mtpt = self.newService("mountpoint", name, uuid)
254 mtpt.appendChild(self.ref("mdc", mdc_uuid))
255 mtpt.appendChild(self.ref("osc", osc_uuid))
256 self.addElement(mtpt, "path", path)
260 return n.getAttribute('name')
263 return node.getAttribute('uuid')
266 def findByName(lustre, name, tag = ""):
267 for n in lustre.childNodes:
268 if n.nodeType == n.ELEMENT_NODE:
269 if tag and n.nodeName != tag:
271 if getName(n) == name:
274 n = findByName(n, name)
278 def lookup(node, uuid):
279 for n in node.childNodes:
280 if n.nodeType == n.ELEMENT_NODE:
281 if getUUID(n) == uuid:
289 def mds2node(lustre, mds_name):
290 """ Find the node a MDS is configured on """
291 mds = findByName(lustre, mds_name, 'mds')
292 ref = mds.getElementsByTagName('node_ref')
294 error("no node found for:", mds_name)
295 node_uuid = ref[0].getAttribute('uuidref')
296 node = lookup(lustre, node_uuid)
298 error("no node found for :", mds_name)
301 def name2uuid(lustre, name, tag="", fatal=1):
302 ret = findByName(lustre, name, tag)
305 error('name2uuid:', name, "not found.")
310 # XXX: assumes only one network element per node. will fix this
311 # as soon as support for routers is added
312 def get_net_uuid(lustre, node_name):
313 """ get a network uuid for a node_name """
314 node = findByName(lustre, node_name, "node")
316 error ("node not found:", node_name)
317 net = node.getElementsByTagName('network')
319 return getUUID(net[0])
322 def lov_add_osc(gen, lov, osc_uuid):
323 devs = lov.getElementsByTagName('devices')
325 devs[0].appendChild(gen.ref("osc", osc_uuid))
327 error("No devices element found for LOV:", lov)
329 def node_add_profile(gen, node, ref, uuid):
330 ret = node.getElementsByTagName('profile')
332 error('node has no profile:', node)
333 ret[0].appendChild(gen.ref(ref, uuid))
336 # Create a new obd, osc, and ost. Add them to the DOM.
338 def add_ost(gen, lustre, options, args):
340 obdtype = 'obdfilter'
345 if options.has_key('node'):
346 node_name = options['node']
348 error("--ost requires a --node argument")
350 if options.has_key('lov'):
351 lovname = options['lov']
353 if options.has_key('obdtype'):
354 obdtype = options['obdtype']
356 if obdtype == 'obdecho':
365 obdname = new_name('OBD_'+ node_name)
366 oscname = new_name('OSC_'+ node_name)
367 ostname = new_name('OST_'+ node_name)
368 obd_uuid = get_uuid(obdname)
369 ost_uuid = get_uuid(ostname)
370 osc_uuid = get_uuid(oscname)
372 net_uuid = get_net_uuid(lustre, node_name)
374 error("NODE: ", node_name, "not found")
376 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
377 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
378 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
381 lov = findByName(lustre, lovname, "lov")
383 error("LOV:", lovname, "not found.")
384 lov_add_osc(gen, lov, osc_uuid)
386 node = findByName(lustre, node_name, "node")
387 node_add_profile(gen, node, 'obd', obd_uuid)
388 node_add_profile(gen, node, 'ost', ost_uuid)
390 lustre.appendChild(obd)
391 lustre.appendChild(osc)
392 lustre.appendChild(ost)
394 # this is generally only used by llecho.sh
395 def add_osc(gen, lustre, options, args):
396 """ add the osc to the profile for this node. """
400 if options.has_key('node'):
401 node_name = options['node']
403 error("--osc requires a --node argument")
404 osc_uuid = name2uuid(lustre, osc_name)
405 node = findByName(lustre, node_name, "node")
406 node_add_profile(gen, node, 'osc', osc_uuid)
408 def add_net(gen, lustre, options, args):
409 """ create a node with a network config """
413 node_name = options['node']
417 if net_type == 'tcp':
422 # add send, recv buffer size here
423 elif net_type in ('elan', 'gm'):
426 print "Unknown net_type: ", net_type
429 ret = findByName(lustre, node_name, "node")
431 node = do_add_node(gen, lustre, node_name)
434 net_name = new_name('NET_'+ node_name +'_'+ net_type)
435 net_uuid = get_uuid(net_name)
436 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port))
437 node_add_profile(gen, node, "network", net_uuid)
439 def do_add_node(gen, lustre, node_name):
440 uuid = get_uuid(node_name)
441 node = gen.node(node_name, uuid)
442 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
443 lustre.appendChild(node)
446 def add_node(gen, lustre, options, args):
447 """ create a node with a network config """
451 node_name = options['node']
453 ret = findByName(lustre, node_name, "node")
455 print "Node:", node_name, "exists."
457 do_add_node(gen, lustre, node_name)
460 def add_lov(gen, lustre, options, args):
465 name = options['lov']
470 uuid = get_uuid(name)
472 ret = findByName(lustre, name, "lov")
474 error("LOV: ", name, " already exists.")
476 mds_uuid = name2uuid(lustre, mds_name)
478 node = mds2node(lustre, mds_name)
479 node_add_profile(gen, node, "lov", uuid)
480 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
481 lustre.appendChild(lov)
483 def add_mtpt(gen, lustre, options, args):
484 """ create mtpt on a node """
488 if options.has_key('node'):
489 node_name = options['node']
491 error("--mtpt requires a --node argument")
496 mdc_name = 'MDC_' + mds_name
498 name = new_name('MNT_'+ node_name)
500 ret = findByName(lustre, name, "mountpoint")
502 error("MOUNTPOINT: ", name, " already exists.")
504 mdc_uuid = name2uuid(lustre, mdc_name)
505 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
507 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
509 uuid = get_uuid(name)
510 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
511 node = findByName(lustre, node_name, "node")
513 error('node:', node_name, "not found.")
514 node_add_profile(gen, node, "mountpoint", uuid)
515 node_add_profile(gen, node, "mdc", mdc_uuid)
516 lustre.appendChild(mtpt)
518 def add_mdc(gen, lustre, options, args):
519 """ create mtpt on a node """
523 if options.has_key('node'):
524 node_name = options['node']
526 error("--mdc requires a --node argument")
529 mdc_uuid = name2uuid(lustre, mdc_name)
531 node = findByName(lustre, node_name, "node")
533 error('node:', node_name, "not found.")
534 node_add_profile(gen, node, "mdc", mdc_uuid)
536 def add_mds(gen, lustre, options, args):
540 if options.has_key('node'):
541 node_name = options['node']
543 error("--mds requires a --node argument")
545 mds_name = new_name(options['mds'])
552 mdc_name = 'MDC_' + mds_name
553 mds_uuid = get_uuid(mds_name)
554 mdc_uuid = get_uuid(mdc_name)
556 node_uuid = name2uuid(lustre, node_name)
558 node = findByName(lustre, node_name, "node")
559 node_add_profile(gen, node, "mds", mds_uuid)
560 net_uuid = get_net_uuid(lustre, node_name)
562 error("NODE: ", node_name, "not found")
565 mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
566 net_uuid, node_uuid, dev_size=size)
567 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
568 lustre.appendChild(mds)
569 lustre.appendChild(mdc)
573 # Command line processing
576 def parse_cmdline(argv):
577 short_opts = "ho:i:m:"
578 long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net",
579 "mdc", "merge=", "format", "reformat", "output=",
580 "obdtype=", "in=", "help"]
585 opts, args = getopt.getopt(argv, short_opts, long_opts)
591 if o in ("-h", "--help"):
593 if o in ("-o", "--output"):
594 options['output'] = a
611 if o in ("-m", "--merge"):
614 options['obdtype'] = a
616 options['format'] = 1
617 if o == "--reformat":
618 options['reformat'] = 1
619 if o in ("--in" , "-i"):
631 self._start = time.time()
632 def stop(self, msg=''):
633 self._stop = time.time()
637 return self._stop - self._start
638 def display(self, msg):
640 str = '%s: %g secs' % (msg, d)
645 options, args = parse_cmdline(sys.argv[1:])
648 if options.has_key('merge'):
649 outFile = options['merge']
650 doc = xml.dom.minidom.parse(outFile)
651 elif options.has_key('in'):
652 doc = xml.dom.minidom.parse(options['in'])
654 doc = new_lustre(xml.dom.minidom)
656 if options.has_key('output'):
657 outFile = options['output']
659 lustre = doc.documentElement
661 if lustre.tagName != "lustre":
662 print "Existing config not valid."
666 if options.has_key('ost'):
667 add_ost(gen, lustre, options, args)
668 elif options.has_key('osc'):
669 add_osc(gen, lustre, options, args)
670 elif options.has_key('mtpt'):
671 add_mtpt(gen, lustre, options, args)
672 elif options.has_key('mds'):
673 add_mds(gen, lustre, options, args)
674 elif options.has_key('mdc'):
675 add_mdc(gen, lustre, options, args)
676 elif options.has_key('net'):
677 add_net(gen, lustre, options, args)
678 elif options.has_key('lov'):
679 add_lov(gen, lustre, options, args)
680 elif options.has_key('node'):
681 add_node(gen, lustre, options, args)
683 print "Missing command"
689 PrettyPrint(doc, open(outFile,"w"))
691 if __name__ == "__main__":