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
73 --add mtpt - Mountpoint
77 --obd obd_name OR --lov lovname
82 msg = string.join(map(str,args))
83 raise OptionError("Error: " + msg)
92 msg = string.join(map(str,args))
93 print "Warning: ", msg
96 # manage names and uuids
97 # need to initialize this by walking tree to ensure
98 # no duplicate names or uuids are created.
99 # this are just place holders for now.
100 # consider changing this to be like OBD-dev-host
104 while names.has_key(ret):
105 ret = "%s_%d" % (base, ctr)
111 return "%s_UUID" % (name)
114 ldlm_uuid = 'ldlm_UUID'
116 """Create a new empty lustre document"""
117 # adding ldlm here is a bit of a hack, but one is enough.
119 <ldlm name="%s" uuid="%s"/>
120 </lustre>""" % (ldlm_name, ldlm_uuid)
121 return dom.parseString(str)
127 """initialize auto-name generation tables"""
129 # get all elements that contain a name attribute
130 for n in doc.childNodes:
131 if n.nodeType == n.ELEMENT_NODE:
133 names[getName(n)] = 1
134 uuids[getUUID(n)] = 1
137 def get_format_flag(options):
138 if options.has_key('format'):
139 if options['format']:
143 ############################################################
144 # Build config objects using DOM
149 def __init__(self, doc):
152 def ref(self, type, uuid):
153 """ generate <[type]_ref uuidref="[uuid]"/> """
154 tag = "%s_ref" % (type)
155 ref = self.doc.createElement(tag)
156 ref.setAttribute("uuidref", uuid)
159 def newService(self, tag, name, uuid):
160 """ create a new service elmement, which requires name and uuid attributes """
161 new = self.doc.createElement(tag)
162 new.setAttribute("uuid", uuid);
163 new.setAttribute("name", name);
166 def addText(self, node, str):
167 txt = self.doc.createTextNode(str)
168 node.appendChild(txt)
170 def addElement(self, node, tag, str=None):
171 """ create a new element and add it as a child to node. If str is passed,
172 a text node is created for the new element"""
173 new = self.doc.createElement(tag)
175 self.addText(new, str)
176 node.appendChild(new)
179 def network(self, name, uuid, hostname, net, port=0, tcpbuf=0):
180 """create <network> node"""
181 network = self.newService("network", name, uuid)
182 network.setAttribute("type", net);
183 self.addElement(network, "server", hostname)
185 self.addElement(network, "port", "%d" %(port))
187 self.addElement(network, "send_mem", "%d" %(tcpbuf))
188 self.addElement(network, "recv_mem", "%d" %(tcpbuf))
192 def route(self, net_type, gw, lo, hi):
193 """ create one entry for the route table """
194 ref = self.doc.createElement('route')
195 ref.setAttribute("type", net_type)
196 ref.setAttribute("gw", gw)
197 ref.setAttribute("lo", lo)
199 ref.setAttribute("hi", hi)
202 def node(self, name, uuid):
203 """ create a host """
204 node = self.newService("node", name, uuid)
205 self.addElement(node, 'profile')
208 def ldlm(self, name, uuid):
209 """ create a ldlm """
210 ldlm = self.newService("ldlm", name, uuid)
213 def obd(self, name, uuid, fs, obdtype, devname, format, ost_uuid, dev_size=0):
214 obd = self.newService("obd", name, uuid)
215 obd.setAttribute('type', obdtype)
216 self.addElement(obd, 'active_target', ost_uuid)
218 self.addElement(obd, "fstype", fs)
220 dev = self.addElement(obd, "device", devname)
222 dev.setAttribute("size", "%s" % (dev_size))
223 self.addElement(obd, "autoformat", format)
226 # def osc(self, name, uuid, obd_uuid, net_uuid):
227 # osc = self.newService("osc", name, uuid)
228 # osc.appendChild(self.ref("ost", net_uuid))
229 # osc.appendChild(self.ref("obd", obd_uuid))
232 def cobd(self, name, uuid, real_uuid, cache_uuid):
233 cobd = self.newService("cobd", name, uuid)
234 cobd.appendChild(self.ref("real_obd",real_uuid))
235 cobd.appendChild(self.ref("cache_obd",cache_uuid))
238 def ost(self, name, uuid, obd_uuid, net_uuid):
239 ost = self.newService("ost", name, uuid)
240 ost.appendChild(self.ref("network", net_uuid))
241 ost.appendChild(self.ref("obd", obd_uuid))
244 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
245 lov = self.newService("lov", name, uuid)
246 lov.appendChild(self.ref("mds", mds_uuid))
247 devs = self.addElement(lov, "devices" )
248 devs.setAttribute("stripesize", stripe_sz)
249 devs.setAttribute("stripecount", stripe_cnt)
250 devs.setAttribute("pattern", pattern)
253 def lovconfig(self, name, uuid, lov_uuid):
254 lovconfig = self.newService("lovconfig", name, uuid)
255 lovconfig.appendChild(self.ref("lov", lov_uuid))
258 def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
259 failover_uuid = "", dev_size=0 ):
260 mds = self.newService("mds", name, uuid)
261 self.addElement(mds, "fstype", fs)
262 dev = self.addElement(mds, "device", devname)
264 dev.setAttribute("size", "%s" % (dev_size))
265 self.addElement(mds, "autoformat", format)
266 mds.appendChild(self.ref("network", net_uuid))
267 mds.appendChild(self.ref("node", node_uuid))
269 mds.appendChild(self.ref("failover", failover_uuid))
272 def mountpoint(self, name, uuid, mds_uuid, osc_uuid, path):
273 mtpt = self.newService("mountpoint", name, uuid)
274 mtpt.appendChild(self.ref("mds", mds_uuid))
275 mtpt.appendChild(self.ref("obd", osc_uuid))
276 self.addElement(mtpt, "path", path)
279 def echo_client(self, name, uuid, osc_uuid):
280 ec = self.newService("echo_client", name, uuid)
281 ec.appendChild(self.ref("obd", osc_uuid))
284 ############################################################
285 # Utilities to query a DOM tree
286 # Using this functions we can treat use config information
287 # directly as a database.
289 return n.getAttribute('name')
292 return node.getAttribute('uuid')
295 def findByName(lustre, name, tag = ""):
296 for n in lustre.childNodes:
297 if n.nodeType == n.ELEMENT_NODE:
298 if tag and n.nodeName != tag:
300 if getName(n) == name:
303 n = findByName(n, name)
308 def lookup(node, uuid):
309 for n in node.childNodes:
310 if n.nodeType == n.ELEMENT_NODE:
311 if getUUID(n) == uuid:
319 def mds2node(lustre, mds_name):
320 """ Find the node a MDS is configured on """
321 mds = findByName(lustre, mds_name, 'mds')
322 ref = mds.getElementsByTagName('node_ref')
324 error("mds2node:", "no node_ref found for", '"'+mds_name+'"')
325 node_uuid = ref[0].getAttribute('uuidref')
326 node = lookup(lustre, node_uuid)
328 error('mds2node:', "no node found for :", '"'+mds_name+'"')
332 def name2uuid(lustre, name, tag="", fatal=1):
333 ret = findByName(lustre, name, tag)
336 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
342 # XXX: assumes only one network element per node. will fix this
343 # as soon as support for routers is added
344 def get_net_uuid(lustre, node_name):
345 """ get a network uuid for a node_name """
346 node = findByName(lustre, node_name, "node")
348 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
349 net = node.getElementsByTagName('network')
351 return getUUID(net[0])
355 def lov_add_obd(gen, lov, osc_uuid):
356 devs = lov.getElementsByTagName('devices')
358 devs[0].appendChild(gen.ref("obd", osc_uuid))
360 error("No devices element found for LOV:", lov)
363 def node_add_profile(gen, node, ref, uuid):
364 ret = node.getElementsByTagName('profile')
366 error('node has no profile:', node)
367 ret[0].appendChild(gen.ref(ref, uuid))
369 def get_attr(dom_node, attr, default=""):
370 v = dom_node.getAttribute(attr)
375 ############################################################
378 def do_add_node(gen, lustre, options, node_name):
379 uuid = new_uuid(node_name)
380 node = gen.node(node_name, uuid)
381 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
382 if has_option(options, 'router'):
383 node.setAttribute('router', '1')
384 if has_option(options, 'timeout'):
385 node.setAttribute('timeout', get_option(options, 'timeout'))
386 if has_option(options, 'recovery_upcall'):
387 node.setAttribute('recovery_upcall', get_option(options, 'recovery_upcall'))
388 lustre.appendChild(node)
392 def add_node(gen, lustre, options):
393 """ create a node with a network config """
395 node_name = get_option(options, 'node')
397 ret = findByName(lustre, node_name, "node")
399 print "Node:", node_name, "exists."
401 do_add_node(gen, lustre, options, node_name)
404 def add_net(gen, lustre, options):
405 """ create a node with a network config """
407 node_name = get_option(options, 'node')
408 nid = get_option(options, 'nid')
409 net_type = get_option(options, 'nettype')
411 if net_type == 'tcp':
412 port = get_option_int(options, 'port', DEFAULT_PORT)
413 tcpbuf = get_option_int(options, 'tcpbuf', 0)
414 elif net_type in ('elan', 'gm'):
418 print "Unknown net_type: ", net_type
421 ret = findByName(lustre, node_name, "node")
423 node = do_add_node(gen, lustre, options, node_name)
426 net_name = new_name('NET_'+ node_name +'_'+ net_type)
427 net_uuid = new_uuid(net_name)
428 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf))
429 node_add_profile(gen, node, "network", net_uuid)
432 def add_route(gen, lustre, options):
433 """ create a node with a network config """
435 node_name = get_option(options, 'node')
436 net_type = get_option(options, 'nettype')
437 gw = get_option(options, 'gw')
438 lo = get_option(options, 'lo')
439 hi = get_option(options, 'hi', '')
441 node = findByName(lustre, node_name, "node")
443 error (node_name, " not found.")
445 netlist = node.getElementsByTagName('network')
447 rlist = net.getElementsByTagName('route_tbl')
451 rtbl = gen.addElement(net, 'route_tbl')
452 rtbl.appendChild(gen.route(net_type, gw, lo, hi))
455 def add_mds(gen, lustre, options):
456 node_name = get_option(options, 'node')
457 mds_orig = get_option(options, 'mds')
458 mds_name = new_name(mds_orig)
459 if mds_name != mds_orig:
460 warning("name:", mds_orig, "already used. using:", mds_name)
461 devname = get_option(options, 'dev')
462 size = get_option(options, 'size', 0)
463 fstype = get_option(options, 'fstype', 'extN')
465 mds_uuid = new_uuid(mds_name)
467 node_uuid = name2uuid(lustre, node_name, 'node')
469 node = findByName(lustre, node_name, "node")
470 node_add_profile(gen, node, "mds", mds_uuid)
471 net_uuid = get_net_uuid(lustre, node_name)
473 error("NODE: ", node_name, "not found")
475 mds = gen.mds(mds_name, mds_uuid, fstype, devname, get_format_flag(options),
476 net_uuid, node_uuid, dev_size=size)
477 lustre.appendChild(mds)
480 def add_ost(gen, lustre, options):
481 node_name = get_option(options, 'node')
482 lovname = get_option(options, 'lov', '')
483 obdtype = get_option(options, 'obdtype', 'obdfilter')
485 if obdtype == 'obdecho':
491 devname = get_option(options, 'dev', '') # can be unset for bluearcs
492 size = get_option(options, 'size', 0)
493 fstype = get_option(options, 'fstype', 'extN')
495 obdname = get_option(options, 'obd', 'OBD_'+ node_name)
496 obdname = new_name(obdname)
497 ostname = new_name('OST_'+ obdname)
498 if options.has_key('obduuid'):
499 obd_uuid = options['obduuid']
500 obd = lookup(lustre, obd_uuid)
502 error("Duplicate OBD UUID:", obd_uuid)
504 obd_uuid = new_uuid(obdname)
505 ost_uuid = new_uuid(ostname)
507 net_uuid = get_net_uuid(lustre, node_name)
509 error("NODE: ", node_name, "not found")
511 obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), ost_uuid,
513 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
516 lov = findByName(lustre, lovname, "lov")
518 error('add_ost:', '"'+lovname+'"', "lov element not found.")
519 lov_add_obd(gen, lov, obd_uuid)
521 node = findByName(lustre, node_name, "node")
522 node_add_profile(gen, node, 'obd', obd_uuid)
523 node_add_profile(gen, node, 'ost', ost_uuid)
525 lustre.appendChild(obd)
526 lustre.appendChild(ost)
529 def add_cobd(gen, lustre, options):
530 node_name = get_option(options, 'node')
531 name = new_name('COBD_' + node_name)
532 uuid = new_uuid(name)
534 real_name = get_option(options, 'real_obd')
535 cache_name = get_option(options, 'cache_obd')
537 real_uuid = name2uuid(lustre, real_name, tag='obd')
538 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
540 node = findByName(lustre, node_name, "node")
541 node_add_profile(gen, node, "cobd", uuid)
542 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
543 lustre.appendChild(cobd)
546 def add_echo_client(gen, lustre, options):
547 """ add an echo client to the profile for this node. """
548 node_name = get_option(options, 'node')
549 lov_name = get_option(options, 'obd')
551 node = findByName(lustre, node_name, 'node')
553 echoname = new_name('ECHO_'+ node_name)
554 echo_uuid = new_uuid(echoname)
555 node_add_profile(gen, node, 'echo_client', echo_uuid)
557 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
559 lov_uuid = name2uuid(lustre, lov_name, tag='obd', fatal=1)
561 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
562 lustre.appendChild(echo)
565 def add_lov(gen, lustre, options):
568 lov_orig = get_option(options, 'lov')
569 name = new_name(lov_orig)
571 warning("name:", lov_orig, "already used. using:", name)
573 mds_name = get_option(options, 'mds')
574 stripe_sz = get_option(options, 'stripe_sz')
575 stripe_cnt = get_option(options, 'stripe_cnt', 0)
576 pattern = get_option(options, 'stripe_pattern', 0)
577 uuid = new_uuid(name)
579 ret = findByName(lustre, name, "lov")
581 error("LOV: ", name, " already exists.")
583 mds_uuid = name2uuid(lustre, mds_name, 'mds')
584 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
585 lustre.appendChild(lov)
587 # add an lovconfig entry to the mds profile
588 lovconfig_name = new_name('LVCFG_' + name)
589 lovconfig_uuid = new_uuid(lovconfig_name)
590 node = mds2node(lustre, mds_name)
591 node_add_profile(gen, node, "lovconfig", lovconfig_uuid)
592 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
593 lustre.appendChild(lovconfig)
596 def add_mtpt(gen, lustre, options):
597 """ create mtpt on a node """
598 node_name = get_option(options, 'node')
600 path = get_option(options, 'path')
601 mds_name = get_option(options, 'mds')
602 lov_name = get_option(options, 'lov', '')
604 lov_name = get_option(options, 'obd', '')
606 error("--add mtpt requires either --lov lov_name or --obd obd_name")
608 name = new_name('MNT_'+ node_name)
610 ret = findByName(lustre, name, "mountpoint")
612 error("MOUNTPOINT: ", name, " already exists.")
614 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
615 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
617 lov_uuid = name2uuid(lustre, lov_name, tag='obd', fatal=1)
619 uuid = new_uuid(name)
620 mtpt = gen.mountpoint(name, uuid, mds_uuid, lov_uuid, path)
621 node = findByName(lustre, node_name, "node")
623 error('node:', node_name, "not found.")
624 node_add_profile(gen, node, "mountpoint", uuid)
625 lustre.appendChild(mtpt)
627 def add_oscref(gen, lustre, options):
628 """ create mtpt on a node """
629 node_name = get_option(options, 'node')
630 osc_name = get_option(options, 'osc')
632 osc_uuid = name2uuid(lustre, osc_name, tag='osc')
633 node = findByName(lustre, node_name, "node")
635 error('node:', node_name, "not found")
636 node_add_profile(gen, node, "osc",osc_uuid)
638 ############################################################
639 # Command line processing
641 class OptionError (exceptions.Exception):
642 def __init__(self, args):
645 def has_option(options, tag):
646 """Look for tag in options hash and return the true if set"""
647 if options.has_key(tag):
651 def get_option(options, tag, default = None):
652 """Look for tag in options hash and return the value if set. If not
653 set, then if return default it is set, otherwise exception."""
654 if options.has_key(tag):
656 elif default != None:
659 raise OptionError("--add %s requires --%s value" % (options['add'], tag))
660 # this exception should print an error like '--add blah requires --<tag> value'
662 def get_option_int(options, tag, default = None):
663 """Return an integer option. Raise exception if the value is not an int"""
664 val = get_option(options, tag, default)
667 def parse_cmdline(argv):
668 short_opts = "ho:i:m:"
669 long_opts = ["add=", "node=", "nettype=", "nid=", "tcpbuf=", "port=",
670 "echo_client=", "stripe_sz=", "stripe_cnt=", "stripe_pattern=",
671 "mds=", "route", "router", "merge=", "format", "reformat", "output=",
672 "dev=", "size=", "obd=", "obdtype=", "obduuid=", "in=",
673 "path=", "help", "batch=", "lov=", "gw=", "lo=", "hi=",
674 "oscref", "osc=", "real_obd=", "cache_obd=", "fstype=",
675 "timeout=", "recovery_upcall="]
680 opts, args = getopt.getopt(argv, short_opts, long_opts)
681 except getopt.error, e:
682 panic(string.join(sys.argv), e)
685 # Commands to create new devices
702 options['timeout'] = a
703 if o == "--recovery_upcall":
704 options['recovery_upcall'] = a
706 options['router'] = 1
712 options['nettype'] = a
716 options['tcpbuf'] = a
734 options['obdtype'] = a
736 options['fstype'] = a
738 options['obduuid'] = a
741 if o == "--stripe_sz":
742 options['stripe_sz'] = a
743 if o == "--stripe_cnt":
744 options['stripe_cnt'] = a
745 if o == "--stripe_pattern":
746 options['stripe_pattern'] = a
755 if o == "--cache_obd":
756 options['cache_obd'] = a
757 if o == "--real_obd":
758 options['real_obd'] = a
761 if o in ("-h", "--help"):
763 if o in ("-o", "--output"):
764 options['output'] = a
765 if o in ("-m", "--merge"):
768 options['format'] = 1
769 if o == "--reformat":
770 options['reformat'] = 1
773 if o in ("--in" , "-i"):
779 # simple class for profiling
786 self._start = time.time()
787 def stop(self, msg=''):
788 self._stop = time.time()
792 return self._stop - self._start
793 def display(self, msg):
795 str = '%s: %g secs' % (msg, d)
800 ############################################################
804 def add(devtype, gen, lustre, options):
806 add_net(gen, lustre, options)
807 elif devtype =='osc':
808 add_osc(gen, lustre, options)
809 elif devtype == 'mtpt':
810 add_mtpt(gen, lustre, options)
811 elif devtype == 'mds':
812 add_mds(gen, lustre, options)
813 elif devtype == 'ost':
814 add_ost(gen, lustre, options)
815 elif devtype == 'lov':
816 add_lov(gen, lustre, options)
817 elif devtype == 'route':
818 add_route(gen, lustre, options)
819 elif devtype == 'node':
820 add_node(gen, lustre, options)
821 elif devtype == 'echo_client':
822 add_echo_client(gen, lustre, options)
823 elif devtype == 'oscref':
824 add_oscref(gen, lustre, options)
825 elif devtype == 'cobd':
826 add_cobd(gen, lustre, options)
828 error("unknown device type:", devtype)
830 def do_command(gen, lustre, options, args):
831 if options.has_key('add'):
832 add(options['add'], gen, lustre, options)
834 error("Missing command")
837 options, args = parse_cmdline(sys.argv[1:])
840 if options.has_key('merge'):
841 outFile = options['merge']
842 if os.access(outFile, os.R_OK):
843 doc = xml.dom.minidom.parse(outFile)
845 doc = new_lustre(xml.dom.minidom)
846 elif options.has_key('in'):
847 doc = xml.dom.minidom.parse(options['in'])
849 doc = new_lustre(xml.dom.minidom)
851 if options.has_key('output'):
852 outFile = options['output']
854 lustre = doc.documentElement
856 if lustre.tagName != "lustre":
857 print "Existing config not valid."
862 if options.has_key('batch'):
863 fp = open(options['batch'])
864 batchCommands = fp.readlines()
866 for cmd in batchCommands:
867 options, args = parse_cmdline(string.split(cmd))
869 do_command(gen, lustre, options, args)
870 except OptionError, e:
874 do_command(gen, lustre, options, args)
875 except OptionError, e:
876 panic(string.join(sys.argv),e)
881 PrettyPrint(doc, open(outFile,"w"))
883 if __name__ == "__main__":