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 = get_option(options, 'osduuid', '', deprecated_tag = 'obduuid')
517 if osd_uuid and lookup(lustre, osd_uuid):
518 error("Duplicate OBD UUID:", osd_uuid)
520 osd_uuid = new_uuid(osdname)
522 ost_uuid = name2uuid(lustre, ostname, fatal=0)
524 ost_uuid = new_uuid(ostname)
525 ost = gen.ost(ostname, ost_uuid, osd_uuid)
526 lustre.appendChild(ost)
528 lov = findByName(lustre, lovname, "lov")
530 error('add_ost:', '"'+lovname+'"', "lov element not found.")
531 lov_add_obd(gen, lov, ost_uuid)
533 net_uuid = get_net_uuid(lustre, node_name)
535 error("NODE: No net network interface for", node_name, "found")
537 osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname, get_format_flag(options), ost_uuid,
540 node = findByName(lustre, node_name, "node")
542 ## if node_add_profile(gen, node, 'oss', oss_uuid):
544 ## oss_uuid = new_uuid(ossname)
545 ## oss = gen.oss(ossname, oss_uuid)
546 ## lustre.appendChild(oss)
548 node_add_profile(gen, node, 'osd', osd_uuid)
549 lustre.appendChild(osd)
552 def add_cobd(gen, lustre, options):
553 node_name = get_option(options, 'node')
554 name = new_name('COBD_' + node_name)
555 uuid = new_uuid(name)
557 real_name = get_option(options, 'real_obd')
558 cache_name = get_option(options, 'cache_obd')
560 real_uuid = name2uuid(lustre, real_name, tag='obd')
561 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
563 node = findByName(lustre, node_name, "node")
564 node_add_profile(gen, node, "cobd", uuid)
565 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
566 lustre.appendChild(cobd)
569 def add_echo_client(gen, lustre, options):
570 """ add an echo client to the profile for this node. """
571 node_name = get_option(options, 'node')
572 lov_name = get_option(options, 'ost')
574 node = findByName(lustre, node_name, 'node')
576 echoname = new_name('ECHO_'+ node_name)
577 echo_uuid = new_uuid(echoname)
578 node_add_profile(gen, node, 'echoclient', echo_uuid)
580 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
582 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
584 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
585 lustre.appendChild(echo)
588 def add_lov(gen, lustre, options):
591 lov_orig = get_option(options, 'lov')
592 name = new_name(lov_orig)
594 warning("name:", lov_orig, "already used. using:", name)
596 mds_name = get_option(options, 'mds')
597 stripe_sz = get_option_int(options, 'stripe_sz')
598 stripe_cnt = get_option_int(options, 'stripe_cnt', 0)
599 pattern = get_option_int(options, 'stripe_pattern', 0)
600 uuid = new_uuid(name)
602 ret = findByName(lustre, name, "lov")
604 error("LOV: ", name, " already exists.")
606 mds_uuid = name2uuid(lustre, mds_name, 'mds')
607 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
608 lustre.appendChild(lov)
610 # add an lovconfig entry to the active mdsdev profile
611 lovconfig_name = new_name('LVCFG_' + name)
612 lovconfig_uuid = new_uuid(lovconfig_name)
613 mds = findByName(lustre, mds_name)
614 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
615 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
616 lustre.appendChild(lovconfig)
619 def add_mtpt(gen, lustre, options):
620 """ create mtpt on a node """
621 node_name = get_option(options, 'node')
623 path = get_option(options, 'path')
624 mds_name = get_option(options, 'mds')
625 lov_name = get_option(options, 'lov', '')
627 lov_name = get_option(options, 'ost', '', deprecated_tag='obd')
629 error("--add mtpt requires either --lov lov_name or --ost ost_name")
631 name = new_name('MNT_'+ node_name)
633 ret = findByName(lustre, name, "mountpoint")
635 error("MOUNTPOINT: ", name, " already exists.")
637 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
638 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
640 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
642 uuid = new_uuid(name)
643 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
644 node = findByName(lustre, node_name, "node")
646 error('node:', node_name, "not found.")
647 node_add_profile(gen, node, "mountpoint", uuid)
648 lustre.appendChild(mtpt)
650 # obsolete, leaving behind for reference
651 def add_oscref(gen, lustre, options):
652 """ create mtpt on a node """
653 node_name = get_option(options, 'node')
654 osc_name = get_option(options, 'osc')
656 osc_uuid = name2uuid(lustre, osc_name, tag='osc')
657 node = findByName(lustre, node_name, "node")
659 error('node:', node_name, "not found")
660 node_add_profile(gen, node, "osc",osc_uuid)
662 ############################################################
663 # Command line processing
665 class OptionError (exceptions.Exception):
666 def __init__(self, args):
669 def has_option(options, tag):
670 """Look for tag in options hash and return the true if set"""
671 if options.has_key(tag):
675 def get_option(options, tag, default = None, deprecated_tag=None):
676 """Look for tag in options hash and return the value if set. If not
677 set, then if return default it is set, otherwise exception."""
678 if options.has_key(tag):
680 elif deprecated_tag and options.has_key(deprecated_tag):
681 warning('--'+deprecated_tag, " is deprecated, please use:", '--'+tag)
682 return options[deprecated_tag]
683 elif default != None:
686 raise OptionError("--add %s requires --%s <value>" % (options['add'], tag))
687 # this exception should print an error like '--add blah requires --<tag> value'
689 def get_option_int(options, tag, default = None):
690 """Return an integer option. Raise exception if the value is not an int"""
691 val = get_option(options, tag, default)
695 raise OptionError("--%s <num> (value must be integer)" % (tag))
698 def parse_cmdline(argv):
699 short_opts = "ho:i:m:"
700 long_opts = ["add=", "node=", "nettype=", "nid=", "tcpbuf=", "port=",
701 "echo_client=", "stripe_sz=", "stripe_cnt=", "stripe_pattern=",
702 "mds=", "route", "router", "merge=", "format", "reformat", "output=",
703 "dev=", "size=", "obd=", "ost=", "obdtype=", "osdtype=", "obduuid=", "in=",
704 "osduuid=", "path=", "help", "batch=", "lov=", "gw=", "lo=", "hi=",
705 "osc=", "real_obd=", "cache_obd=", "fstype=",
706 "timeout=", "recovery_upcall="]
711 opts, args = getopt.getopt(argv, short_opts, long_opts)
712 except getopt.error, e:
713 panic(string.join(sys.argv), e)
716 # Commands to create new devices
735 options['timeout'] = a
736 if o == "--recovery_upcall":
737 options['recovery_upcall'] = a
739 options['router'] = 1
745 options['nettype'] = a
749 options['tcpbuf'] = a
767 options['obdtype'] = a
769 options['osdtype'] = a
771 options['fstype'] = a
773 options['obduuid'] = a
775 options['osduuid'] = a
778 if o == "--stripe_sz":
779 options['stripe_sz'] = a
780 if o == "--stripe_cnt":
781 options['stripe_cnt'] = a
782 if o == "--stripe_pattern":
783 options['stripe_pattern'] = a
792 if o == "--cache_obd":
793 options['cache_obd'] = a
794 if o == "--real_obd":
795 options['real_obd'] = a
798 if o in ("-h", "--help"):
800 if o in ("-o", "--output"):
801 options['output'] = a
802 if o in ("-m", "--merge"):
805 options['format'] = 1
806 if o == "--reformat":
807 warning("the lmc --reformat option is not supported. Use lconf --reformat")
808 options['reformat'] = 1
811 if o in ("--in" , "-i"):
817 # simple class for profiling
824 self._start = time.time()
825 def stop(self, msg=''):
826 self._stop = time.time()
830 return self._stop - self._start
831 def display(self, msg):
833 str = '%s: %g secs' % (msg, d)
838 ############################################################
842 def add(devtype, gen, lustre, options):
844 add_net(gen, lustre, options)
845 elif devtype =='osc':
846 add_osc(gen, lustre, options)
847 elif devtype == 'mtpt':
848 add_mtpt(gen, lustre, options)
849 elif devtype == 'mds':
850 add_mds(gen, lustre, options)
851 elif devtype == 'ost':
852 add_ost(gen, lustre, options)
853 elif devtype == 'lov':
854 add_lov(gen, lustre, options)
855 elif devtype == 'route':
856 add_route(gen, lustre, options)
857 elif devtype == 'node':
858 add_node(gen, lustre, options)
859 elif devtype == 'echo_client':
860 add_echo_client(gen, lustre, options)
861 elif devtype == 'cobd':
862 add_cobd(gen, lustre, options)
864 error("unknown device type:", devtype)
866 def do_command(gen, lustre, options, args):
867 if options.has_key('add'):
868 add(options['add'], gen, lustre, options)
870 error("Missing command")
873 options, args = parse_cmdline(sys.argv[1:])
876 if options.has_key('merge'):
877 outFile = options['merge']
878 if os.access(outFile, os.R_OK):
879 doc = xml.dom.minidom.parse(outFile)
881 doc = new_lustre(xml.dom.minidom)
882 elif options.has_key('in'):
883 doc = xml.dom.minidom.parse(options['in'])
885 doc = new_lustre(xml.dom.minidom)
887 if options.has_key('output'):
888 outFile = options['output']
890 lustre = doc.documentElement
892 if lustre.tagName != "lustre":
893 print "Existing config not valid."
898 if options.has_key('batch'):
899 fp = open(options['batch'])
900 batchCommands = fp.readlines()
902 for cmd in batchCommands:
903 options, args = parse_cmdline(string.split(cmd))
905 do_command(gen, lustre, options, args)
906 except OptionError, e:
910 do_command(gen, lustre, options, args)
911 except OptionError, e:
912 panic(string.join(sys.argv),e)
917 PrettyPrint(doc, open(outFile,"w"))
919 if __name__ == "__main__":