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 See lustre book for documentation for lmc.
28 import sys, os, getopt, string, exceptions
29 import xml.dom.minidom
30 from xml.dom.ext import PrettyPrint
35 print """usage: lmc --add object [object parameters]"""
39 msg = string.join(map(str,args))
40 raise OptionError("Error: " + msg)
49 msg = string.join(map(str,args))
50 print "Warning: ", msg
53 # manage names and uuids
54 # need to initialize this by walking tree to ensure
55 # no duplicate names or uuids are created.
56 # this are just place holders for now.
57 # consider changing this to be like OBD-dev-host
61 while names.has_key(ret):
62 ret = "%s_%d" % (base, ctr)
68 return "%s_UUID" % (name)
71 ldlm_uuid = 'ldlm_UUID'
73 """Create a new empty lustre document"""
74 # adding ldlm here is a bit of a hack, but one is enough.
76 <ldlm name="%s" uuid="%s"/>
77 </lustre>""" % (ldlm_name, ldlm_uuid)
78 return dom.parseString(str)
84 """initialize auto-name generation tables"""
86 # get all elements that contain a name attribute
87 for n in doc.childNodes:
88 if n.nodeType == n.ELEMENT_NODE:
94 def get_format_flag(options):
95 if options.has_key('format'):
100 ############################################################
101 # Build config objects using DOM
106 def __init__(self, doc):
109 def ref(self, type, uuid):
110 """ generate <[type]_ref uuidref="[uuid]"/> """
111 tag = "%s_ref" % (type)
112 ref = self.doc.createElement(tag)
113 ref.setAttribute("uuidref", uuid)
116 def newService(self, tag, name, uuid):
117 """ create a new service elmement, which requires name and uuid attributes """
118 new = self.doc.createElement(tag)
119 new.setAttribute("uuid", uuid);
120 new.setAttribute("name", name);
123 def addText(self, node, str):
124 txt = self.doc.createTextNode(str)
125 node.appendChild(txt)
127 def addElement(self, node, tag, str=None):
128 """ create a new element and add it as a child to node. If str is passed,
129 a text node is created for the new element"""
130 new = self.doc.createElement(tag)
132 self.addText(new, str)
133 node.appendChild(new)
136 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
137 """create <network> node"""
138 network = self.newService("network", name, uuid)
139 network.setAttribute("type", net);
140 self.addElement(network, "server", hostname)
142 self.addElement(network, "port", "%d" %(port))
144 self.addElement(network, "send_mem", "%d" %(tcpbuf))
145 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
149 def route(self, net_type, gw, lo, hi):
150 """ create one entry for the route table """
151 ref = self.doc.createElement('route')
152 ref.setAttribute("type", net_type)
153 ref.setAttribute("gw", gw)
154 ref.setAttribute("lo", lo)
156 ref.setAttribute("hi", hi)
159 def node(self, name, uuid):
160 """ create a host """
161 node = self.newService("node", name, uuid)
162 self.addElement(node, 'profile')
165 def ldlm(self, name, uuid):
166 """ create a ldlm """
167 ldlm = self.newService("ldlm", name, uuid)
170 def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
171 obd = self.newService("obd", name, uuid)
172 obd.setAttribute('type', obdtype)
174 self.addElement(obd, "fstype", fs)
176 dev = self.addElement(obd, "device", devname)
178 dev.setAttribute("size", "%s" % (dev_size))
179 self.addElement(obd, "autoformat", format)
182 def cobd(self, name, uuid, real_uuid, cache_uuid):
183 cobd = self.newService("cobd", name, uuid)
184 cobd.appendChild(self.ref("real_obd",real_uuid))
185 cobd.appendChild(self.ref("cache_obd",cache_uuid))
188 def osc(self, name, uuid, obd_uuid, net_uuid):
189 osc = self.newService("osc", name, uuid)
190 osc.appendChild(self.ref("ost", net_uuid))
191 osc.appendChild(self.ref("obd", obd_uuid))
194 def ost(self, name, uuid, obd_uuid, net_uuid):
195 ost = self.newService("ost", name, uuid)
196 ost.appendChild(self.ref("network", net_uuid))
197 ost.appendChild(self.ref("obd", obd_uuid))
200 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
201 lov = self.newService("lov", name, uuid)
202 lov.appendChild(self.ref("mds", mds_uuid))
203 devs = self.addElement(lov, "devices" )
204 devs.setAttribute("stripesize", stripe_sz)
205 devs.setAttribute("stripecount", stripe_cnt)
206 devs.setAttribute("pattern", pattern)
209 def lovconfig(self, name, uuid, lov_uuid):
210 lovconfig = self.newService("lovconfig", name, uuid)
211 lovconfig.appendChild(self.ref("lov", lov_uuid))
214 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
215 failover_uuid = "", dev_size=0 ):
216 mds = self.newService("mds", name, uuid)
217 self.addElement(mds, "fstype", fs)
218 dev = self.addElement(mds, "device", devname)
220 dev.setAttribute("size", "%s" % (dev_size))
221 self.addElement(mds, "autoformat", format)
222 mds.appendChild(self.ref("network", net_uuid))
223 mds.appendChild(self.ref("node", node_uuid))
225 mds.appendChild(self.ref("failover", failover_uuid))
228 def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
229 mtpt = self.newService("mountpoint", name, uuid)
230 mtpt.appendChild(self.ref("mds", mds_uuid))
231 mtpt.appendChild(self.ref("osc", osc_uuid))
232 self.addElement(mtpt, "path", path)
235 def echo_client(self, name, uuid, osc_uuid):
236 ec = self.newService("echo_client", name, uuid)
237 ec.appendChild(self.ref("osc", osc_uuid))
240 ############################################################
241 # Utilities to query a DOM tree
242 # Using this functions we can treat use config information
243 # directly as a database.
245 return n.getAttribute('name')
248 return node.getAttribute('uuid')
251 def findByName(lustre, name, tag = ""):
252 for n in lustre.childNodes:
253 if n.nodeType == n.ELEMENT_NODE:
254 if tag and n.nodeName != tag:
256 if getName(n) == name:
259 n = findByName(n, name)
264 def lookup(node, uuid):
265 for n in node.childNodes:
266 if n.nodeType == n.ELEMENT_NODE:
267 if getUUID(n) == uuid:
275 def mds2node(lustre, mds_name):
276 """ Find the node a MDS is configured on """
277 mds = findByName(lustre, mds_name, 'mds')
278 ref = mds.getElementsByTagName('node_ref')
280 error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
281 node_uuid = ref[0].getAttribute('uuidref')
282 node = lookup(lustre, node_uuid)
284 error('mds2node:', "no node found for :", '"'+mds_name+'"')
288 def name2uuid(lustre, name, tag="", fatal=1):
289 ret = findByName(lustre, name, tag)
292 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
298 # XXX: assumes only one network element per node. will fix this
299 # as soon as support for routers is added
300 def get_net_uuid(lustre, node_name):
301 """ get a network uuid for a node_name """
302 node = findByName(lustre, node_name, "node")
304 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
305 net = node.getElementsByTagName('network')
307 return getUUID(net[0])
311 def lov_add_osc(gen, lov, osc_uuid):
312 devs = lov.getElementsByTagName('devices')
314 devs[0].appendChild(gen.ref("osc", osc_uuid))
316 error("No devices element found for LOV:", lov)
319 def node_add_profile(gen, node, ref, uuid):
320 ret = node.getElementsByTagName('profile')
322 error('node has no profile:', node)
323 ret[0].appendChild(gen.ref(ref, uuid))
325 def get_attr(dom_node, attr, default=""):
326 v = dom_node.getAttribute(attr)
331 ############################################################
334 def do_add_node(gen, lustre, options, node_name):
335 uuid = new_uuid(node_name)
336 node = gen.node(node_name, uuid)
337 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
338 if options.has_key('router'):
339 node.setAttribute('router', '1')
340 lustre.appendChild(node)
344 def add_node(gen, lustre, options):
345 """ create a node with a network config """
347 node_name = get_option(options, 'node')
349 ret = findByName(lustre, node_name, "node")
351 print "Node:", node_name, "exists."
353 do_add_node(gen, lustre, options, node_name)
356 def add_net(gen, lustre, options):
357 """ create a node with a network config """
359 node_name = get_option(options, 'node')
360 nid = get_option(options, 'nid')
361 net_type = get_option(options, 'nettype')
363 if net_type == 'tcp':
364 port = get_option_int(options, 'port', DEFAULT_PORT)
365 tcpbuf = get_option_int(options, 'tcpbuf', 0)
366 elif net_type in ('elan', 'gm'):
370 print "Unknown net_type: ", net_type
373 ret = findByName(lustre, node_name, "node")
375 node = do_add_node(gen, lustre, options, node_name)
378 net_name = new_name('NET_'+ node_name +'_'+ net_type)
379 net_uuid = new_uuid(net_name)
380 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
381 node_add_profile(gen, node, "network", net_uuid)
384 def add_route(gen, lustre, options):
385 """ create a node with a network config """
387 node_name = get_option(options, 'node')
388 net_type = get_option(options, 'nettype')
389 gw = get_option(options, 'gw')
390 lo = get_option(options, 'lo')
391 hi = get_option(options, 'hi', '')
393 node = findByName(lustre, node_name, "node")
395 error (node_name, " not found.")
397 netlist = node.getElementsByTagName('network')
399 rlist = net.getElementsByTagName('route_tbl')
403 rtbl = gen.addElement(net, 'route_tbl')
404 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
407 def add_mds(gen, lustre, options):
408 node_name = get_option(options, 'node')
409 mds_orig = get_option(options, 'mds')
410 mds_name = new_name(mds_orig)
411 if mds_name != mds_orig:
412 warning("name:", mds_orig, "already used. using:", mds_name)
413 devname = get_option(options, 'dev')
414 size = get_option(options, 'size', 0)
415 fstype = get_option(options, 'fstype', 'extN')
417 mds_uuid = new_uuid(mds_name)
419 node_uuid = name2uuid(lustre, node_name, 'node')
421 node = findByName(lustre, node_name, "node")
422 node_add_profile(gen, node, "mds", mds_uuid)
423 net_uuid = get_net_uuid(lustre, node_name)
425 error("NODE: ", node_name, "not found")
427 mds = gen.mds(mds_name, mds_uuid, fstype, devname, get_format_flag(options),
428 net_uuid, node_uuid, dev_size=size)
429 lustre.appendChild(mds)
432 def add_ost(gen, lustre, options):
433 node_name = get_option(options, 'node')
434 lovname = get_option(options, 'lov', '')
435 obdtype = get_option(options, 'obdtype', 'obdfilter')
437 if obdtype == 'obdecho':
443 devname = get_option(options, 'dev', '') # can be unset for bluearcs
444 size = get_option(options, 'size', 0)
445 fstype = get_option(options, 'fstype', 'extN')
447 obdname = get_option(options, 'obd', 'OBD_'+ node_name)
448 obdname = new_name(obdname)
449 oscname = new_name('OSC_'+ obdname)
450 ostname = new_name('OST_'+ obdname)
451 if options.has_key('obduuid'):
452 obd_uuid = options['obduuid']
453 obd = lookup(lustre, obd_uuid)
455 error("Duplicate OBD UUID:", obd_uuid)
457 obd_uuid = new_uuid(obdname)
458 ost_uuid = new_uuid(ostname)
459 osc_uuid = new_uuid(oscname)
461 net_uuid = get_net_uuid(lustre, node_name)
463 error("NODE: ", node_name, "not found")
465 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
466 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
467 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
470 lov = findByName(lustre, lovname, "lov")
472 error('add_ost:', '"'+lovname+'"', "lov element not found.")
473 lov_add_osc(gen, lov, osc_uuid)
475 node = findByName(lustre, node_name, "node")
476 node_add_profile(gen, node, 'obd', obd_uuid)
477 node_add_profile(gen, node, 'ost', ost_uuid)
479 lustre.appendChild(obd)
480 lustre.appendChild(osc)
481 lustre.appendChild(ost)
484 def add_cobd(gen, lustre, options):
485 node_name = get_option(options, 'node')
486 name = new_name('COBD_' + node_name)
487 uuid = new_uuid(name)
489 real_name = get_option(options, 'real_obd')
490 cache_name = get_option(options, 'cache_obd')
491 # temp hack until merged with b_recover and OSC is removed
492 real_name = 'OSC_' + real_name
493 cache_name = 'OSC_' + cache_name
495 real_uuid = name2uuid(lustre, real_name, tag='osc')
496 cache_uuid = name2uuid(lustre, cache_name, tag='osc')
498 node = findByName(lustre, node_name, "node")
499 node_add_profile(gen, node, "cobd", uuid)
500 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
501 lustre.appendChild(cobd)
504 def add_echo_client(gen, lustre, options):
505 """ add an echo client to the profile for this node. """
506 node_name = get_option(options, 'node')
507 lov_name = get_option(options, 'obd')
509 node = findByName(lustre, node_name, 'node')
511 echoname = new_name('ECHO_'+ node_name)
512 echo_uuid = new_uuid(echoname)
513 node_add_profile(gen, node, 'echo_client', echo_uuid)
515 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
517 # remove this hack when the osc uuids are removed
518 lov_name = 'OSC_' + lov_name
519 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
521 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
522 lustre.appendChild(echo)
525 def add_lov(gen, lustre, options):
528 lov_orig = get_option(options, 'lov')
529 name = new_name(lov_orig)
531 warning("name:", lov_orig, "already used. using:", name)
533 mds_name = get_option(options, 'mds')
534 stripe_sz = get_option(options, 'stripe_sz')
535 stripe_cnt = get_option(options, 'stripe_cnt', 0)
536 pattern = get_option(options, 'stripe_pattern', 0)
537 uuid = new_uuid(name)
539 ret = findByName(lustre, name, "lov")
541 error("LOV: ", name, " already exists.")
543 mds_uuid = name2uuid(lustre, mds_name, 'mds')
544 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
545 lustre.appendChild(lov)
547 # add an lovconfig entry to the mds profile
548 lovconfig_name = new_name('LVCFG_' + name)
549 lovconfig_uuid = new_uuid(lovconfig_name)
550 node = mds2node(lustre, mds_name)
551 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
552 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
553 lustre.appendChild(lovconfig)
556 def add_mtpt(gen, lustre, options):
557 """ create mtpt on a node """
558 node_name = get_option(options, 'node')
560 path = get_option(options, 'path')
561 mds_name = get_option(options, 'mds')
562 lov_name = get_option(options, 'lov', '')
564 lov_name = get_option(options, 'obd', '')
566 error("--add mtpt requires either --lov lov_name or --obd obd_name")
568 name = new_name('MNT_'+ node_name)
570 ret = findByName(lustre, name, "mountpoint")
572 error("MOUNTPOINT: ", name, " already exists.")
574 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
575 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
577 # remove this hack when OSC is removed
578 lov_name = 'OSC_' + lov_name
579 lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
581 uuid = new_uuid(name)
582 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
583 node = findByName(lustre, node_name, "node")
585 error('node:', node_name, "not found.")
586 node_add_profile(gen, node, "mountpoint", uuid)
587 lustre.appendChild(mtpt)
589 def add_oscref(gen, lustre, options):
590 """ create mtpt on a node """
591 node_name = get_option(options, 'node')
592 osc_name = get_option(options, 'osc')
594 osc_uuid = name2uuid(lustre, osc_name, tag='osc')
595 node = findByName(lustre, node_name, "node")
597 error('node:', node_name, "not found")
598 node_add_profile(gen, node, "osc",osc_uuid)
600 ############################################################
601 # Command line processing
603 class OptionError (exceptions.Exception):
604 def __init__(self, args):
607 def get_option(options, tag, default = None):
608 """Look for tag in options hash and return the value if set. If not
609 set, then if return default it is set, otherwise exception."""
610 if options.has_key(tag):
612 elif default != None:
615 raise OptionError("--add %s requires --%s value" % (options['add'], tag))
616 # this exception should print an error like '--add blah requires --<tag> value'
618 def get_option_int(options, tag, default = None):
619 """Return an integer option. Raise exception if the value is not an int"""
620 val = get_option(options, tag, default)
623 def parse_cmdline(argv):
624 short_opts = "ho:i:m:"
625 long_opts = ["add=", "node=", "nettype=", "nid=", "tcpbuf=", "port=",
626 "echo_client=", "stripe_sz=", "stripe_cnt=", "stripe_pattern=",
627 "mds=", "route", "router", "merge=", "format", "reformat", "output=",
628 "dev=", "size=", "obd=", "obdtype=", "obduuid=", "in=",
629 "path=", "help", "batch=", "lov=", "gw=", "lo=", "hi=",
630 "oscref", "osc=", "real_obd=", "cache_obd=", "fstype="]
635 opts, args = getopt.getopt(argv, short_opts, long_opts)
636 except getopt.error, e:
637 panic(string.join(sys.argv), e)
640 # Commands to create new devices
659 options['nettype'] = a
663 options['tcpbuf'] = a
671 options['router'] = 1
683 options['obdtype'] = a
685 options['fstype'] = a
687 options['obduuid'] = a
690 if o == "--stripe_sz":
691 options['stripe_sz'] = a
692 if o == "--stripe_cnt":
693 options['stripe_cnt'] = a
694 if o == "--stripe_pattern":
695 options['stripe_pattern'] = a
704 if o == "--cache_obd":
705 options['cache_obd'] = a
706 if o == "--real_obd":
707 options['real_obd'] = a
710 if o in ("-h", "--help"):
712 if o in ("-o", "--output"):
713 options['output'] = a
714 if o in ("-m", "--merge"):
717 options['format'] = 1
718 if o == "--reformat":
719 options['reformat'] = 1
722 if o in ("--in" , "-i"):
728 # simple class for profiling
735 self._start = time.time()
736 def stop(self, msg=''):
737 self._stop = time.time()
741 return self._stop - self._start
742 def display(self, msg):
744 str = '%s: %g secs' % (msg, d)
749 ############################################################
753 def add(devtype, gen, lustre, options):
755 add_net(gen, lustre, options)
756 elif devtype =='osc':
757 add_osc(gen, lustre, options)
758 elif devtype == 'mtpt':
759 add_mtpt(gen, lustre, options)
760 elif devtype == 'mds':
761 add_mds(gen, lustre, options)
762 elif devtype == 'ost':
763 add_ost(gen, lustre, options)
764 elif devtype == 'lov':
765 add_lov(gen, lustre, options)
766 elif devtype == 'route':
767 add_route(gen, lustre, options)
768 elif devtype == 'node':
769 add_node(gen, lustre, options)
770 elif devtype == 'echo_client':
771 add_echo_client(gen, lustre, options)
772 elif devtype == 'oscref':
773 add_oscref(gen, lustre, options)
774 elif devtype == 'cobd':
775 add_cobd(gen, lustre, options)
777 error("unknown device type:", devtype)
779 def do_command(gen, lustre, options, args):
780 if options.has_key('add'):
781 add(options['add'], gen, lustre, options)
783 error("Missing command")
786 options, args = parse_cmdline(sys.argv[1:])
789 if options.has_key('merge'):
790 outFile = options['merge']
791 if os.access(outFile, os.R_OK):
792 doc = xml.dom.minidom.parse(outFile)
794 doc = new_lustre(xml.dom.minidom)
795 elif options.has_key('in'):
796 doc = xml.dom.minidom.parse(options['in'])
798 doc = new_lustre(xml.dom.minidom)
800 if options.has_key('output'):
801 outFile = options['output']
803 lustre = doc.documentElement
805 if lustre.tagName != "lustre":
806 print "Existing config not valid."
811 if options.has_key('batch'):
812 fp = open(options['batch'])
813 batchCommands = fp.readlines()
815 for cmd in batchCommands:
816 options, args = parse_cmdline(string.split(cmd))
818 do_command(gen, lustre, options, args)
819 except OptionError, e:
823 do_command(gen, lustre, options, args)
824 except OptionError, e:
825 panic(string.join(sys.argv),e)
830 PrettyPrint(doc, open(outFile,"w"))
832 if __name__ == "__main__":