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]
37 Object creation command summary:
42 --recovery_upcall path
47 --nettype tcp|elan|toe|gm
75 --add mtpt - Mountpoint
79 --ost ost_name OR --lov lov_name
84 msg = string.join(map(str,args))
85 raise OptionError("Error: " + msg)
94 msg = string.join(map(str,args))
95 print "Warning: ", msg
98 # manage names and uuids
99 # need to initialize this by walking tree to ensure
100 # no duplicate names or uuids are created.
101 # this are just place holders for now.
102 # consider changing this to be like OBD-dev-host
106 while names.has_key(ret):
107 ret = "%s_%d" % (base, ctr)
113 return "%s_UUID" % (name)
116 ldlm_uuid = 'ldlm_UUID'
118 """Create a new empty lustre document"""
119 # adding ldlm here is a bit of a hack, but one is enough.
121 <ldlm name="%s" uuid="%s"/>
122 </lustre>""" % (ldlm_name, ldlm_uuid)
123 return dom.parseString(str)
129 """initialize auto-name generation tables"""
131 # get all elements that contain a name attribute
132 for n in doc.childNodes:
133 if n.nodeType == n.ELEMENT_NODE:
135 names[getName(n)] = 1
136 uuids[getUUID(n)] = 1
139 def get_format_flag(options):
140 if options.has_key('format'):
141 if options['format']:
145 ############################################################
146 # Build config objects using DOM
151 def __init__(self, doc):
154 def ref(self, type, uuid):
155 """ generate <[type]_ref uuidref="[uuid]"/> """
156 tag = "%s_ref" % (type)
157 ref = self.doc.createElement(tag)
158 ref.setAttribute("uuidref", uuid)
161 def newService(self, tag, name, uuid):
162 """ create a new service elmement, which requires name and uuid attributes """
163 new = self.doc.createElement(tag)
164 new.setAttribute("uuid", uuid);
165 new.setAttribute("name", name);
168 def addText(self, node, str):
169 txt = self.doc.createTextNode(str)
170 node.appendChild(txt)
172 def addElement(self, node, tag, str=None):
173 """ create a new element and add it as a child to node. If str is passed,
174 a text node is created for the new element"""
175 new = self.doc.createElement(tag)
177 self.addText(new, str)
178 node.appendChild(new)
181 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
182 """create <network> node"""
183 network = self.newService("network", name, uuid)
184 network.setAttribute("nettype", net);
185 self.addElement(network, "nid", hostname)
187 self.addElement(network, "port", "%d" %(port))
189 self.addElement(network, "sendmem", "%d" %(tcpbuf))
190 self.addElement(network, "recvmem", "%d" %(tcpbuf))
194 def route(self, net_type, gw, lo, hi):
195 """ create one entry for the route table """
196 ref = self.doc.createElement('route')
197 ref.setAttribute("type", net_type)
198 ref.setAttribute("gw", gw)
199 ref.setAttribute("lo", lo)
201 ref.setAttribute("hi", hi)
204 def profile(self, name, uuid):
205 """ create a host """
206 profile = self.newService("profile", name, uuid)
209 def node(self, name, uuid, prof_uuid):
210 """ create a host """
211 node = self.newService("node", name, uuid)
212 node.appendChild(self.ref("profile", prof_uuid))
215 def ldlm(self, name, uuid):
216 """ create a ldlm """
217 ldlm = self.newService("ldlm", name, uuid)
220 def osd(self, name, uuid, fs, osdtype, devname, format, ost_uuid, net_uuid, dev_size=0):
221 osd = self.newService("osd", name, uuid)
222 osd.setAttribute('osdtype', osdtype)
223 osd.appendChild(self.ref("target", ost_uuid))
224 osd.appendChild(self.ref("network", net_uuid))
226 self.addElement(osd, "fstype", fs)
228 dev = self.addElement(osd, "devpath", devname)
229 self.addElement(osd, "autoformat", format)
231 self.addElement(osd, "devsize", "%s" % (dev_size))
234 def cobd(self, name, uuid, real_uuid, cache_uuid):
235 cobd = self.newService("cobd", name, uuid)
236 cobd.appendChild(self.ref("realobd",real_uuid))
237 cobd.appendChild(self.ref("cacheobd",cache_uuid))
240 def ost(self, name, uuid, osd_uuid):
241 ost = self.newService("ost", name, uuid)
242 ost.appendChild(self.ref("active", osd_uuid))
245 def oss(self, name, uuid):
246 oss = self.newService("oss", name, uuid)
249 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
250 lov = self.newService("lov", name, uuid)
251 lov.appendChild(self.ref("mds", mds_uuid))
252 lov.setAttribute("stripesize", str(stripe_sz))
253 lov.setAttribute("stripecount", str(stripe_cnt))
254 lov.setAttribute("stripepattern", str(pattern))
257 def lovconfig(self, name, uuid, lov_uuid):
258 lovconfig = self.newService("lovconfig", name, uuid)
259 lovconfig.appendChild(self.ref("lov", lov_uuid))
262 def mds(self, name, uuid, mdd_uuid):
263 mds = self.newService("mds", name, uuid)
264 mds.appendChild(self.ref("active",mdd_uuid))
267 def mdsdev(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
268 mds_uuid, dev_size=0 ):
269 mdd = self.newService("mdsdev", name, uuid)
270 self.addElement(mdd, "fstype", fs)
271 dev = self.addElement(mdd, "devpath", devname)
272 self.addElement(mdd, "autoformat", format)
274 self.addElement(mdd, "devsize", "%s" % (dev_size))
275 mdd.appendChild(self.ref("network", net_uuid))
276 mdd.appendChild(self.ref("target", mds_uuid))
279 def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
280 mtpt = self.newService("mountpoint", name, uuid)
281 mtpt.appendChild(self.ref("mds", mds_uuid))
282 mtpt.appendChild(self.ref("obd", osc_uuid))
283 self.addElement(mtpt, "path", path)
286 def echo_client(self, name, uuid, osc_uuid):
287 ec = self.newService("echoclient", name, uuid)
288 ec.appendChild(self.ref("obd", osc_uuid))
291 ############################################################
292 # Utilities to query a DOM tree
293 # Using this functions we can treat use config information
294 # directly as a database.
296 return n.getAttribute('name')
299 return node.getAttribute('uuid')
302 def findByName(lustre, name, tag = ""):
303 for n in lustre.childNodes:
304 if n.nodeType == n.ELEMENT_NODE:
305 if tag and n.nodeName != tag:
307 if getName(n) == name:
310 n = findByName(n, name)
315 def lookup(node, uuid):
316 for n in node.childNodes:
317 if n.nodeType == n.ELEMENT_NODE:
318 if getUUID(n) == uuid:
326 def name2uuid(lustre, name, tag="", fatal=1):
327 ret = findByName(lustre, name, tag)
330 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
336 # XXX: assumes only one network element per node. will fix this
337 # as soon as support for routers is added
338 def get_net_uuid(lustre, node_name):
339 """ get a network uuid for a node_name """
340 node = findByName(lustre, node_name, "node")
342 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
343 net = node.getElementsByTagName('network')
345 return getUUID(net[0])
349 def lov_add_obd(gen, lov, osc_uuid):
350 lov.appendChild(gen.ref("obd", osc_uuid))
352 def ref_exists(profile, uuid):
353 elist = profile.childNodes
355 if e.nodeType == e.ELEMENT_NODE:
356 ref = e.getAttribute('uuidref')
361 # ensure that uuid is not already in the profile
362 # return true if uuid is added
363 def node_add_profile(gen, node, ref, uuid):
364 refname = "%s_ref" % "profile"
365 ret = node.getElementsByTagName(refname)
367 error('node has no profile ref:', node)
368 prof_uuid = ret[0].getAttribute('uuidref')
369 profile = lookup(node.parentNode, prof_uuid)
371 error("no profile found:", prof_uuid)
372 if ref_exists(profile, uuid):
374 profile.appendChild(gen.ref(ref, uuid))
377 def get_attr(dom_node, attr, default=""):
378 v = dom_node.getAttribute(attr)
383 ############################################################
386 def do_add_node(gen, lustre, options, node_name):
387 uuid = new_uuid(node_name)
388 prof_name = new_name("PROFILE_" + node_name)
389 prof_uuid = new_uuid(prof_name)
390 profile = gen.profile(prof_name, prof_uuid)
391 node = gen.node(node_name, uuid, prof_uuid)
392 lustre.appendChild(node)
393 lustre.appendChild(profile)
395 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
396 if has_option(options, 'router'):
397 node.setAttribute('router', '1')
398 if has_option(options, 'timeout'):
399 node.setAttribute('timeout', get_option(options, 'timeout'))
400 if has_option(options, 'recovery_upcall'):
401 node.setAttribute('recovery_upcall', get_option(options, 'recovery_upcall'))
405 def add_node(gen, lustre, options):
406 """ create a node with a network config """
408 node_name = get_option(options, 'node')
409 ret = findByName(lustre, node_name, "node")
411 print "Node:", node_name, "exists."
413 do_add_node(gen, lustre, options, node_name)
416 def add_net(gen, lustre, options):
417 """ create a node with a network config """
419 node_name = get_option(options, 'node')
420 nid = get_option(options, 'nid')
421 net_type = get_option(options, 'nettype')
423 if net_type in ('tcp', 'toe'):
424 port = get_option_int(options, 'port', DEFAULT_PORT)
425 tcpbuf = get_option_int(options, 'tcpbuf', 0)
426 elif net_type in ('elan', 'gm'):
430 print "Unknown net_type: ", net_type
433 ret = findByName(lustre, node_name, "node")
435 node = do_add_node(gen, lustre, options, node_name)
438 net_name = new_name('NET_'+ node_name +'_'+ net_type)
439 net_uuid = new_uuid(net_name)
440 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
441 node_add_profile(gen, node, "network", net_uuid)
444 def add_route(gen, lustre, options):
445 """ create a node with a network config """
447 node_name = get_option(options, 'node')
448 net_type = get_option(options, 'nettype')
449 gw = get_option(options, 'gw')
450 lo = get_option(options, 'lo')
451 hi = get_option(options, 'hi', '')
453 node = findByName(lustre, node_name, "node")
455 error (node_name, " not found.")
457 netlist = node.getElementsByTagName('network')
459 rlist = net.getElementsByTagName('routetbl')
463 rtbl = gen.addElement(net, 'routetbl')
464 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
467 def add_mds(gen, lustre, options):
468 node_name = get_option(options, 'node')
469 mds_name = get_option(options, 'mds')
470 mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
471 mdd_uuid = new_uuid(mdd_name)
473 mds_uuid = name2uuid(lustre, mds_name, fatal=0)
475 mds_uuid = new_uuid(mds_name)
476 mds = gen.mds(mds_name, mds_uuid, mdd_uuid)
477 lustre.appendChild(mds)
479 devname = get_option(options, 'dev')
480 size = get_option(options, 'size', 0)
481 fstype = get_option(options, 'fstype', 'extN')
483 node_uuid = name2uuid(lustre, node_name, 'node')
485 node = findByName(lustre, node_name, "node")
486 node_add_profile(gen, node, "mdsdev", mdd_uuid)
487 net_uuid = get_net_uuid(lustre, node_name)
489 error("NODE: ", node_name, "not found")
491 mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname, get_format_flag(options),
492 net_uuid, node_uuid, mds_uuid, dev_size=size)
493 lustre.appendChild(mdd)
496 def add_ost(gen, lustre, options):
497 node_name = get_option(options, 'node')
498 lovname = get_option(options, 'lov', '')
499 osdtype = get_option(options, 'osdtype', 'obdfilter', deprecated_tag="obdtype")
501 if osdtype == 'obdecho':
507 devname = get_option(options, 'dev', '') # can be unset for bluearcs
508 size = get_option(options, 'size', 0)
509 fstype = get_option(options, 'fstype', 'extN')
511 ostname = get_option(options, 'ost', '', deprecated_tag='obd')
513 ostname = new_name('OST_'+ node_name)
515 osdname = new_name("OSD_" + ostname)
516 osd_uuid = new_uuid(osdname)
518 ost_uuid = name2uuid(lustre, ostname, fatal=0)
520 ost_uuid = get_option(options, 'ostuuid', '', deprecated_tag = 'obduuid')
522 if lookup(lustre, ost_uuid):
523 error("Duplicate OST UUID:", ost_uuid)
525 ost_uuid = new_uuid(ostname)
527 ost = gen.ost(ostname, ost_uuid, osd_uuid)
528 lustre.appendChild(ost)
530 lov = findByName(lustre, lovname, "lov")
532 error('add_ost:', '"'+lovname+'"', "lov element not found.")
533 lov_add_obd(gen, lov, ost_uuid)
535 net_uuid = get_net_uuid(lustre, node_name)
537 error("NODE: No net network interface for", node_name, "found")
539 osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname, get_format_flag(options), ost_uuid,
542 node = findByName(lustre, node_name, "node")
544 ## if node_add_profile(gen, node, 'oss', oss_uuid):
546 ## oss_uuid = new_uuid(ossname)
547 ## oss = gen.oss(ossname, oss_uuid)
548 ## lustre.appendChild(oss)
550 node_add_profile(gen, node, 'osd', osd_uuid)
551 lustre.appendChild(osd)
554 def add_cobd(gen, lustre, options):
555 node_name = get_option(options, 'node')
556 name = new_name('COBD_' + node_name)
557 uuid = new_uuid(name)
559 real_name = get_option(options, 'real_obd')
560 cache_name = get_option(options, 'cache_obd')
562 real_uuid = name2uuid(lustre, real_name, tag='obd')
563 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
565 node = findByName(lustre, node_name, "node")
566 node_add_profile(gen, node, "cobd", uuid)
567 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
568 lustre.appendChild(cobd)
571 def add_echo_client(gen, lustre, options):
572 """ add an echo client to the profile for this node. """
573 node_name = get_option(options, 'node')
574 lov_name = get_option(options, 'ost')
576 node = findByName(lustre, node_name, 'node')
578 echoname = new_name('ECHO_'+ node_name)
579 echo_uuid = new_uuid(echoname)
580 node_add_profile(gen, node, 'echoclient', echo_uuid)
582 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
584 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
586 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
587 lustre.appendChild(echo)
590 def add_lov(gen, lustre, options):
593 lov_orig = get_option(options, 'lov')
594 name = new_name(lov_orig)
596 warning("name:", lov_orig, "already used. using:", name)
598 mds_name = get_option(options, 'mds')
599 stripe_sz = get_option_int(options, 'stripe_sz')
600 stripe_cnt = get_option_int(options, 'stripe_cnt', 0)
601 pattern = get_option_int(options, 'stripe_pattern', 0)
602 uuid = new_uuid(name)
604 ret = findByName(lustre, name, "lov")
606 error("LOV: ", name, " already exists.")
608 mds_uuid = name2uuid(lustre, mds_name, 'mds')
609 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
610 lustre.appendChild(lov)
612 # add an lovconfig entry to the active mdsdev profile
613 lovconfig_name = new_name('LVCFG_' + name)
614 lovconfig_uuid = new_uuid(lovconfig_name)
615 mds = findByName(lustre, mds_name)
616 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
617 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
618 lustre.appendChild(lovconfig)
621 def add_mtpt(gen, lustre, options):
622 """ create mtpt on a node """
623 node_name = get_option(options, 'node')
625 path = get_option(options, 'path')
626 mds_name = get_option(options, 'mds')
627 lov_name = get_option(options, 'lov', '')
629 lov_name = get_option(options, 'ost', '', deprecated_tag='obd')
631 error("--add mtpt requires either --lov lov_name or --ost ost_name")
633 name = new_name('MNT_'+ node_name)
635 ret = findByName(lustre, name, "mountpoint")
637 error("MOUNTPOINT: ", name, " already exists.")
639 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
640 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
642 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
644 uuid = new_uuid(name)
645 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
646 node = findByName(lustre, node_name, "node")
648 error('node:', node_name, "not found.")
649 node_add_profile(gen, node, "mountpoint", uuid)
650 lustre.appendChild(mtpt)
652 # obsolete, leaving behind for reference
653 def add_oscref(gen, lustre, options):
654 """ create mtpt on a node """
655 node_name = get_option(options, 'node')
656 osc_name = get_option(options, 'osc')
658 osc_uuid = name2uuid(lustre, osc_name, tag='osc')
659 node = findByName(lustre, node_name, "node")
661 error('node:', node_name, "not found")
662 node_add_profile(gen, node, "osc",osc_uuid)
664 ############################################################
665 # Command line processing
667 class OptionError (exceptions.Exception):
668 def __init__(self, args):
671 def has_option(options, tag):
672 """Look for tag in options hash and return the true if set"""
673 if options.has_key(tag):
677 def get_option(options, tag, default = None, deprecated_tag=None):
678 """Look for tag in options hash and return the value if set. If not
679 set, then if return default it is set, otherwise exception."""
680 if options.has_key(tag):
682 elif deprecated_tag and options.has_key(deprecated_tag):
683 warning('--'+deprecated_tag, " is deprecated, please use:", '--'+tag)
684 return options[deprecated_tag]
685 elif default != None:
688 raise OptionError("--add %s requires --%s <value>" % (options['add'], tag))
689 # this exception should print an error like '--add blah requires --<tag> value'
691 def get_option_int(options, tag, default = None):
692 """Return an integer option. Raise exception if the value is not an int"""
693 val = get_option(options, tag, default)
697 raise OptionError("--%s <num> (value must be integer)" % (tag))
700 def parse_cmdline(argv):
701 short_opts = "ho:i:m:"
702 long_opts = ["add=", "node=", "nettype=", "nid=", "tcpbuf=", "port=",
703 "echo_client=", "stripe_sz=", "stripe_cnt=", "stripe_pattern=",
704 "mds=", "route", "router", "merge=", "format", "reformat", "output=",
705 "dev=", "size=", "obd=", "ost=", "obdtype=", "osdtype=", "obduuid=", "in=",
706 "ostuuid=", "path=", "help", "batch=", "lov=", "gw=", "lo=", "hi=",
707 "osc=", "real_obd=", "cache_obd=", "fstype=",
708 "timeout=", "recovery_upcall="]
713 opts, args = getopt.getopt(argv, short_opts, long_opts)
714 except getopt.error, e:
715 panic(string.join(sys.argv), e)
718 # Commands to create new devices
737 options['timeout'] = a
738 if o == "--recovery_upcall":
739 options['recovery_upcall'] = a
741 options['router'] = 1
747 options['nettype'] = a
751 options['tcpbuf'] = a
769 options['obdtype'] = a
771 options['osdtype'] = a
773 options['fstype'] = a
775 options['obduuid'] = a
777 options['ostuuid'] = a
780 if o == "--stripe_sz":
781 options['stripe_sz'] = a
782 if o == "--stripe_cnt":
783 options['stripe_cnt'] = a
784 if o == "--stripe_pattern":
785 options['stripe_pattern'] = a
794 if o == "--cache_obd":
795 options['cache_obd'] = a
796 if o == "--real_obd":
797 options['real_obd'] = a
800 if o in ("-h", "--help"):
802 if o in ("-o", "--output"):
803 options['output'] = a
804 if o in ("-m", "--merge"):
807 options['format'] = 1
808 if o == "--reformat":
809 warning("the lmc --reformat option is not supported. Use lconf --reformat")
810 options['reformat'] = 1
813 if o in ("--in" , "-i"):
819 # simple class for profiling
826 self._start = time.time()
827 def stop(self, msg=''):
828 self._stop = time.time()
832 return self._stop - self._start
833 def display(self, msg):
835 str = '%s: %g secs' % (msg, d)
840 ############################################################
844 def add(devtype, gen, lustre, options):
846 add_net(gen, lustre, options)
847 elif devtype =='osc':
848 add_osc(gen, lustre, options)
849 elif devtype == 'mtpt':
850 add_mtpt(gen, lustre, options)
851 elif devtype == 'mds':
852 add_mds(gen, lustre, options)
853 elif devtype == 'ost':
854 add_ost(gen, lustre, options)
855 elif devtype == 'lov':
856 add_lov(gen, lustre, options)
857 elif devtype == 'route':
858 add_route(gen, lustre, options)
859 elif devtype == 'node':
860 add_node(gen, lustre, options)
861 elif devtype == 'echo_client':
862 add_echo_client(gen, lustre, options)
863 elif devtype == 'cobd':
864 add_cobd(gen, lustre, options)
866 error("unknown device type:", devtype)
868 def do_command(gen, lustre, options, args):
869 if options.has_key('add'):
870 add(options['add'], gen, lustre, options)
872 error("Missing command")
875 options, args = parse_cmdline(sys.argv[1:])
878 if options.has_key('merge'):
879 outFile = options['merge']
880 if os.access(outFile, os.R_OK):
881 doc = xml.dom.minidom.parse(outFile)
883 doc = new_lustre(xml.dom.minidom)
884 elif options.has_key('in'):
885 doc = xml.dom.minidom.parse(options['in'])
887 doc = new_lustre(xml.dom.minidom)
889 if options.has_key('output'):
890 outFile = options['output']
892 lustre = doc.documentElement
894 if lustre.tagName != "lustre":
895 print "Existing config not valid."
900 if options.has_key('batch'):
901 fp = open(options['batch'])
902 batchCommands = fp.readlines()
904 for cmd in batchCommands:
905 options, args = parse_cmdline(string.split(cmd))
907 do_command(gen, lustre, options, args)
908 except OptionError, e:
912 do_command(gen, lustre, options, args)
913 except OptionError, e:
914 panic(string.join(sys.argv),e)
919 PrettyPrint(doc, open(outFile,"w"))
921 if __name__ == "__main__":