4 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License version 2 only,
8 # as published by the Free Software Foundation.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License version 2 for more details (a copy is included
14 # in the LICENSE file that accompanied this code).
16 # You should have received a copy of the GNU General Public License
17 # version 2 along with this program; If not, see
18 # http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21 # Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 # CA 95054 USA or visit www.sun.com if you need additional information or
29 # Copyright 2008 Sun Microsystems, Inc. All rights reserved
30 # Use is subject to license terms.
34 # This file is part of Lustre, http://www.lustre.org/
35 # Lustre is a trademark of Sun Microsystems, Inc.
37 # Author: Robert Read <rread@clusterfs.com>
40 lmc - lustre configuration data manager
42 See the man page, or the Lustre Operations Manual, for documentation on lmc.
46 import sys, os, getopt, string, exceptions, re
47 import xml.dom.minidom
49 def printDoc(doc, stream=sys.stdout):
51 from xml.dom.ext import PrettyPrint
52 PrettyPrint(doc, stream)
54 stream.write(doc.toxml())
58 PYMOD_DIR = ["/usr/lib/lustre/python", "/usr/lib64/lustre/python"]
60 def development_mode():
61 base = os.path.dirname(sys.argv[0])
62 if os.access(base+"/Makefile.am", os.R_OK):
66 if not development_mode():
67 sys.path.extend(PYMOD_DIR)
72 DEFAULT_STRIPE_SZ = 1048576
73 DEFAULT_STRIPE_CNT = 1
74 DEFAULT_STRIPE_PATTERN = 0
78 print """usage: lmc --add object [object parameters]
80 Object creation command summary:
89 --ptldebug debug_level
90 --subsystem subsystem_name
96 --nettype tcp|elan|gm|openib|iib|vib|ra|ptl|lnet
97 --hostaddr ip[/netmask]
107 --fstype ldiskfs|ext3
110 --group_upcall upcall
114 --mkfsoptions options
115 --mountfsoptions options
116 --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune=
133 --fstype ldiskfs|ext3
136 --osdtype obdecho|obdfilter
138 --mkfsoptions options
139 --mountfsoptions options
140 --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune=
142 --add mtpt - Mountpoint
146 --ost ost_name OR --lov lov_name
147 --clientoptions options
153 --gateway_cluster_id nid
154 --target_cluster_id nid
162 PARAM = Lustre.Options.PARAM
163 PARAMLIST = Lustre.Options.PARAMLIST
165 # lmc input/output options
166 ('reference', "Print short reference for commands."),
167 ('verbose,v', "Print system commands as they are run."),
168 ('merge,m', "Append to the specified config file.", PARAM),
169 ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
170 ('input,i', "", PARAM),
171 ('batch', "Used to execute lmc commands in batch mode.", PARAM),
177 ('node', "Add a new node in the cluster configuration.", PARAM),
178 ('timeout', "Set timeout to initiate recovery.", PARAM),
179 ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
180 ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
181 ('group_upcall', "Set location of extended group upcall script.", PARAM),
182 ('portals_upcall', "Set location of portals upcall script.", PARAM),
183 ('ptldebug', "Set the portals debug level", PARAM),
184 ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log", PARAM),
187 ('nettype', "Specify the network type. This can be tcp/elan/gm/openib/iib/vib/ra/ptl/lnet.", PARAM),
188 ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
189 ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
190 ('hostaddr', "Optional argument to specify the host address.", PARAMLIST),
191 ('cluster_id', "Specify the cluster ID", PARAM, "0"),
192 ('nonet', "Skip the remote host networking check"),
195 ('route', "Add a new route for the cluster.", PARAM),
196 ('router', "Optional flag to mark a node as router."),
197 ('gw', "Specify the nid of the gateway for a route.", PARAM),
198 ('gateway_cluster_id', "", PARAM, "0"),
199 ('target_cluster_id', "", PARAM, "0"),
200 ('lo', "For a range route, this is the low value nid.", PARAM),
201 ('hi', "For a range route, this is a hi value nid.", PARAM,""),
203 # servers: mds and ost
204 ('mds', "Specify MDS name.", PARAM),
205 ('ost', "Specify the OST name.", PARAM,""),
206 ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
207 ('failout', "Disable failover support on OST"),
208 ('failover', "Enable failover support on OST"),
209 ('group', "", PARAM),
210 ('dev', "Path of the device on local system.", PARAM,""),
211 ('size', "Specify the size of the device if needed.", PARAM,"0"),
212 ('group_upcall', "Set location of supplementary group upcall.", PARAM,""),
213 ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"),
214 ('inode_size', "Specify new inode size for underlying ext3 file system.", PARAM,"0"),
215 ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
216 ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
217 ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""),
218 ('ostuuid', "Optional argument to specify OST UUID", PARAM,""),
219 ('mdsuuid', "Optional argument to specify MDS UUID", PARAM,""),
220 ('nspath', "Local mount point of server namespace.", PARAM,""),
223 quotaon: enable quota, only u|g|ug is supported now.
224 iunit: the unit for slave to acquire/release inode quota from/to master.
225 Int type (>0), default value in Lustre is 5000 inodes.
226 bunit: the unit for slave to acquire/release block quota from/to master.
227 Mbytes (>0), default value in Lustre is 100(Mbytes).
228 itune: used to tune the threthold. When inode quota usage reach the threthold,
229 slave should acquire/release inode quota from/to master.
230 Int type (100 > btune > 0), default value in Lustre is 50 (percentge).
231 inode threthold = iunit * itune / 100.
232 btune: used to tune the threthold. When block quota usage reach the threthold,
233 slave should acquire/release block quota from/to master.
234 Int type (100 > btune > 0), default value in Lustre is 50 (percentage).
235 block threthold = bunit * btune / 100.""", PARAM,""),
236 # clients: mountpoint and echo
237 ('echo_client', "", PARAM),
238 ('path', "Specify the mountpoint for Lustre.", PARAM),
239 ('filesystem', "Lustre filesystem name", PARAM,""),
240 ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""),
243 ('lov', "Specify LOV name.", PARAM,""),
244 ('stripe_sz', "Specify the stripe size in bytes.", PARAM, DEFAULT_STRIPE_SZ),
245 ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, DEFAULT_STRIPE_CNT),
246 ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
249 ('real_obd', "Specify the real device for the cache obd system.", PARAM),
250 ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
254 msg = string.join(map(str,args))
255 raise OptionError("Error: " + msg)
263 msg = string.join(map(str,args))
264 sys.stderr.write("WARNING: %s\n" % (msg))
267 msg = string.join(map(str,args))
268 sys.stderr.write("INFO: %s\n" % (msg))
271 # manage names and uuids
272 # need to initialize this by walking tree to ensure
273 # no duplicate names or uuids are created.
274 # this are just place holders for now.
275 # consider changing this to be like OBD-dev-host
279 while names.has_key(ret):
280 ret = "%s_%d" % (base, ctr)
287 ret = "%s_UUID" % (name)
288 if len(ret) > UUID_MAX_LENGTH:
289 ret = ret[-UUID_MAX_LENGTH:]
290 while uuids.has_key(ret):
291 ret = "%s_UUID_%d" % (name, ctr)
293 if len(ret) > UUID_MAX_LENGTH:
294 ret = ret[-UUID_MAX_LENGTH:]
300 ldlm_uuid = 'ldlm_UUID'
303 """Create a new empty lustre document"""
304 # adding ldlm here is a bit of a hack, but one is enough.
305 str = """<lustre version="%s">
306 <ldlm name="%s" uuid="%s"/>
307 </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
308 return dom.parseString(str)
315 """initialize auto-name generation tables"""
317 # get all elements that contain a name attribute
318 for n in doc.childNodes:
319 if n.nodeType == n.ELEMENT_NODE:
321 names[getName(n)] = 1
322 uuids[getUUID(n)] = 1
325 def get_format_flag(options):
330 ############################################################
331 # Build config objects using DOM
336 def __init__(self, doc):
339 def ref(self, type, uuid):
340 """ generate <[type]_ref uuidref="[uuid]"/> """
341 tag = "%s_ref" % (type)
342 ref = self.doc.createElement(tag)
343 ref.setAttribute("uuidref", uuid)
346 def newService(self, tag, name, uuid):
347 """ create a new service elmement, which requires name and uuid attributes """
348 new = self.doc.createElement(tag)
349 new.setAttribute("uuid", uuid);
350 new.setAttribute("name", name);
353 def addText(self, node, str):
354 txt = self.doc.createTextNode(str)
355 node.appendChild(txt)
357 def addElement(self, node, tag, str=None):
358 """ create a new element and add it as a child to node. If str is passed,
359 a text node is created for the new element"""
360 new = self.doc.createElement(tag)
362 self.addText(new, str)
363 node.appendChild(new)
366 def recordtime(self, timestr):
367 lustre = self.doc.getElementsByTagName("lustre")
368 lustre[0].setAttribute("mtime", timestr)
370 def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
372 """create <network> node"""
373 network = self.newService("network", name, uuid)
374 network.setAttribute("nettype", net);
375 self.addElement(network, "nid", nid)
376 self.addElement(network, "clusterid", cluster_id)
377 for host in hostaddr:
378 self.addElement(network, "hostaddr", host)
380 self.addElement(network, "port", "%d" %(port))
384 def routetbl(self, name, uuid):
385 """create <routetbl> node"""
386 rtbl = self.newService("routetbl", name, uuid)
389 def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
390 """ create one entry for the route table """
391 ref = self.doc.createElement('route')
392 ref.setAttribute("type", gw_net_type)
393 ref.setAttribute("gw", gw)
394 ref.setAttribute("gwclusterid", gw_cluster_id)
395 ref.setAttribute("tgtclusterid", tgt_cluster_id)
396 ref.setAttribute("lo", lo)
398 ref.setAttribute("hi", hi)
401 def profile(self, name, uuid):
402 """ create a host """
403 profile = self.newService("profile", name, uuid)
406 def node(self, name, uuid, prof_uuid):
407 """ create a host """
408 node = self.newService("node", name, uuid)
409 node.appendChild(self.ref("profile", prof_uuid))
412 def ldlm(self, name, uuid):
413 """ create a ldlm """
414 ldlm = self.newService("ldlm", name, uuid)
417 def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
418 node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="",
419 mkfsoptions="", mountfsoptions="", quota=""):
420 osd = self.newService("osd", name, uuid)
421 osd.setAttribute('osdtype', osdtype)
422 osd.appendChild(self.ref("target", ost_uuid))
423 osd.appendChild(self.ref("node", node_uuid))
425 self.addElement(osd, "fstype", fstype)
427 dev = self.addElement(osd, "devpath", devname)
428 self.addElement(osd, "autoformat", format)
430 self.addElement(osd, "devsize", "%s" % (dev_size))
432 self.addElement(osd, "journalsize", "%s" % (journal_size))
434 self.addElement(osd, "inodesize", "%s" % (inode_size))
436 self.addElement(osd, "mkfsoptions", mkfsoptions)
438 self.addElement(osd, "mountfsoptions", mountfsoptions)
440 self.addElement(osd, "quota", quota)
442 self.addElement(osd, "nspath", nspath)
445 def cobd(self, name, uuid, real_uuid, cache_uuid):
446 cobd = self.newService("cobd", name, uuid)
447 cobd.appendChild(self.ref("realobd",real_uuid))
448 cobd.appendChild(self.ref("cacheobd",cache_uuid))
451 def ost(self, name, uuid, osd_uuid, group=""):
452 ost = self.newService("ost", name, uuid)
453 ost.appendChild(self.ref("active", osd_uuid))
455 self.addElement(ost, "group", group)
458 def oss(self, name, uuid):
459 oss = self.newService("oss", name, uuid)
462 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
463 lov = self.newService("lov", name, uuid)
464 lov.appendChild(self.ref("mds", mds_uuid))
465 lov.setAttribute("stripesize", str(stripe_sz))
466 lov.setAttribute("stripecount", str(stripe_cnt))
467 lov.setAttribute("stripepattern", str(pattern))
470 def lovconfig(self, name, uuid, lov_uuid):
471 lovconfig = self.newService("lovconfig", name, uuid)
472 lovconfig.appendChild(self.ref("lov", lov_uuid))
475 def mds(self, name, uuid, mdd_uuid, group=""):
476 mds = self.newService("mds", name, uuid)
477 mds.appendChild(self.ref("active",mdd_uuid))
479 self.addElement(mds, "group", group)
482 def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
483 mds_uuid, dev_size=0, journal_size=0, inode_size=256,
484 nspath="", mkfsoptions="", mountfsoptions="", quota="", group_upcall=""):
485 mdd = self.newService("mdsdev", name, uuid)
486 self.addElement(mdd, "fstype", fstype)
487 dev = self.addElement(mdd, "devpath", devname)
488 self.addElement(mdd, "autoformat", format)
490 self.addElement(mdd, "devsize", "%s" % (dev_size))
492 self.addElement(mdd, "journalsize", "%s" % (journal_size))
494 self.addElement(mdd, "inodesize", "%s" % (inode_size))
496 self.addElement(mdd, "nspath", nspath)
498 self.addElement(mdd, "mkfsoptions", mkfsoptions)
500 self.addElement(mdd, "mountfsoptions", mountfsoptions)
502 self.addElement(mdd, "quota", quota)
504 self.addElement(mdd, "group_upcall", group_upcall)
506 mdd.appendChild(self.ref("node", node_uuid))
507 mdd.appendChild(self.ref("target", mds_uuid))
510 def mountpoint(self, name, uuid, fs_uuid, path, clientoptions):
511 mtpt = self.newService("mountpoint", name, uuid)
512 mtpt.appendChild(self.ref("filesystem", fs_uuid))
513 self.addElement(mtpt, "path", path)
515 self.addElement(mtpt, "clientoptions", clientoptions)
518 def filesystem(self, name, uuid, mds_uuid, obd_uuid):
519 fs = self.newService("filesystem", name, uuid)
520 fs.appendChild(self.ref("mds", mds_uuid))
521 fs.appendChild(self.ref("obd", obd_uuid))
524 def echo_client(self, name, uuid, osc_uuid):
525 ec = self.newService("echoclient", name, uuid)
526 ec.appendChild(self.ref("obd", osc_uuid))
529 ############################################################
530 # Utilities to query a DOM tree
531 # Using this functions we can treat use config information
532 # directly as a database.
534 return n.getAttribute('name')
537 return node.getAttribute('uuid')
540 def findByName(lustre, name, tag = ""):
541 for n in lustre.childNodes:
542 if n.nodeType == n.ELEMENT_NODE:
543 if tag and n.nodeName != tag:
545 if getName(n) == name:
548 n = findByName(n, name)
553 def lookup(node, uuid):
554 for n in node.childNodes:
555 if n.nodeType == n.ELEMENT_NODE:
556 if getUUID(n) == uuid:
564 def name2uuid(lustre, name, tag="", fatal=1):
565 ret = findByName(lustre, name, tag)
568 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
573 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
574 for n in lustre.childNodes:
575 if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
576 if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
580 # XXX: assumes only one network element per node. will fix this
581 # as soon as support for routers is added
582 def get_net_uuid(lustre, node_name):
583 """ get a network uuid for a node_name """
584 node = findByName(lustre, node_name, "node")
586 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
587 net = node.getElementsByTagName('network')
589 return getUUID(net[0])
593 def lov_add_obd(gen, lov, osc_uuid):
594 lov.appendChild(gen.ref("obd", osc_uuid))
596 def ref_exists(profile, uuid):
597 elist = profile.childNodes
599 if e.nodeType == e.ELEMENT_NODE:
600 ref = e.getAttribute('uuidref')
605 # ensure that uuid is not already in the profile
606 # return true if uuid is added
607 def node_add_profile(gen, node, ref, uuid):
608 refname = "%s_ref" % "profile"
609 ret = node.getElementsByTagName(refname)
611 error('node has no profile ref:', node)
612 prof_uuid = ret[0].getAttribute('uuidref')
613 profile = lookup(node.parentNode, prof_uuid)
615 error("no profile found:", prof_uuid)
616 if ref_exists(profile, uuid):
618 profile.appendChild(gen.ref(ref, uuid))
621 def get_attr(dom_node, attr, default=""):
622 v = dom_node.getAttribute(attr)
627 ############################################################
639 def set_node_options(gen, node, options):
641 node.setAttribute('router', '1')
643 gen.addElement(node, "timeout", get_option(options, 'timeout'))
645 default_upcall = get_option(options, 'upcall')
648 if default_upcall or options.lustre_upcall:
649 if options.lustre_upcall:
650 gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
652 gen.addElement(node, 'lustreUpcall', default_upcall)
653 if options.group_upcall:
654 gen.addElement(node, 'groupUpcall', options.group_upcall)
655 if default_upcall or options.portals_upcall:
656 if options.portals_upcall:
657 gen.addElement(node, 'portalsUpcall', options.portals_upcall)
659 gen.addElement(node, 'portalsUpcall', default_upcall)
661 gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
662 if options.subsystem:
663 gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
666 def do_add_node(gen, lustre, options, node_name):
667 uuid = new_uuid(node_name)
668 prof_name = new_name("PROFILE_" + node_name)
669 prof_uuid = new_uuid(prof_name)
670 profile = gen.profile(prof_name, prof_uuid)
671 node = gen.node(node_name, uuid, prof_uuid)
672 lustre.appendChild(node)
673 lustre.appendChild(profile)
675 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
676 set_node_options(gen, node, options)
680 def add_node(gen, lustre, options):
681 """ create a node with a network config """
683 node_name = get_option(options, 'node')
684 ret = findByName(lustre, node_name, "node")
686 print "Node:", node_name, "exists."
688 do_add_node(gen, lustre, options, node_name)
691 def add_net(gen, lustre, options):
692 """ create a node with a network config """
694 node_name = get_option(options, 'node')
695 nid = get_option(options, 'nid')
696 cluster_id = get_option(options, 'cluster_id')
697 hostaddr = get_option(options, 'hostaddr')
698 net_type = get_option(options, 'nettype')
700 if net_type in ('lnet','tcp','openib','ra'):
701 port = get_option_int(options, 'port')
702 elif net_type in ('elan','gm','iib','vib','lo','ptl'):
705 print "Unknown net_type: ", net_type
708 real_net_type = net_type
709 if net_type == 'lnet' and string.find(nid,'@') > 0:
710 real_net_type = string.split(nid,'@')[1]
715 print "Skipping the remote host networking test."
716 elif (real_net_type == 'tcp') and (nid != '*'):
718 print "Testing network on", node_name
719 target = string.split(nid,'@')[0]
720 if target != '*' and target != '\\*':
721 out = runcmd("ping -c 1 -w 5 %s" %target)
723 print "Could not connect to %s, please check network." % node_name
725 ret = findByName(lustre, node_name, "node")
727 node = do_add_node(gen, lustre, options, node_name)
730 set_node_options(gen, node, options)
732 net_name = new_name('NET_'+ node_name +'_'+ net_type)
733 net_uuid = new_uuid(net_name)
734 node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
736 node_add_profile(gen, node, "network", net_uuid)
739 def add_route(gen, lustre, options):
740 """ create a node with a network config """
742 node_name = get_option(options, 'node')
743 gw_net_type = get_option(options, 'nettype')
744 gw = get_option(options, 'gw')
745 gw_cluster_id = get_option(options, 'gateway_cluster_id')
746 tgt_cluster_id = get_option(options, 'target_cluster_id')
747 lo = get_option(options, 'lo')
748 hi = get_option(options, 'hi')
752 node = findByName(lustre, node_name, "node")
754 error (node_name, " not found.")
756 rlist = node.getElementsByTagName('routetbl')
760 rtbl_name = new_name("RTBL_" + node_name)
761 rtbl_uuid = new_uuid(rtbl_name)
762 rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
763 node.appendChild(rtbl)
764 node_add_profile(gen, node, "routetbl", rtbl_uuid)
765 rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
769 def add_mds(gen, lustre, options):
770 node_name = get_option(options, 'node')
771 mds_name = get_option(options, 'mds')
772 mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
773 mdd_uuid = new_uuid(mdd_name)
775 mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
777 mds_uuid = get_option(options, 'mdsuuid')
779 if lookup(lustre, mds_uuid):
780 error("Duplicate MDS UUID:", mds_uuid)
782 mds_uuid = new_uuid(mds_name)
784 mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
785 lustre.appendChild(mds)
787 mds = lookup(lustre, mds_uuid)
789 mds.setAttribute('failover', "1")
791 mds.setAttribute('failover,',"0")
793 devname = get_option(options, 'dev')
794 size = get_option(options, 'size')
795 fstype = get_option(options, 'fstype')
796 journal_size = get_option(options, 'journal_size')
797 inode_size = get_option(options, 'inode_size')
798 nspath = get_option(options, 'nspath')
799 mkfsoptions = get_option(options, 'mkfsoptions')
800 mountfsoptions = get_option(options, 'mountfsoptions')
801 quota = get_option(options, 'quota')
802 group_upcall = get_option(options, 'group_upcall')
804 node_uuid = name2uuid(lustre, node_name, 'node')
806 node = findByName(lustre, node_name, "node")
807 node_add_profile(gen, node, "mdsdev", mdd_uuid)
808 net_uuid = get_net_uuid(lustre, node_name)
810 error("NODE: ", node_name, "not found")
812 mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
813 get_format_flag(options), node_uuid, mds_uuid,
814 size, journal_size, inode_size, nspath, mkfsoptions,
815 mountfsoptions, quota, group_upcall)
816 lustre.appendChild(mdd)
819 def add_ost(gen, lustre, options):
820 node_name = get_option(options, 'node')
821 lovname = get_option(options, 'lov')
822 osdtype = get_option(options, 'osdtype')
824 node_uuid = name2uuid(lustre, node_name, 'node')
826 if osdtype == 'obdecho':
837 devname = get_option(options, 'dev') # can be unset for bluearcs
838 size = get_option(options, 'size')
839 fstype = get_option(options, 'fstype')
840 journal_size = get_option(options, 'journal_size')
841 inode_size = get_option(options, 'inode_size')
842 mkfsoptions = get_option(options, 'mkfsoptions')
843 mountfsoptions = get_option(options, 'mountfsoptions')
844 quota = get_option(options, 'quota')
846 nspath = get_option(options, 'nspath')
848 ostname = get_option(options, 'ost')
850 ostname = new_name('OST_'+ node_name)
852 osdname = new_name("OSD_" + ostname + "_" + node_name)
853 osd_uuid = new_uuid(osdname)
855 ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
857 ost_uuid = get_option(options, 'ostuuid')
859 if lookup(lustre, ost_uuid):
860 error("Duplicate OST UUID:", ost_uuid)
862 ost_uuid = new_uuid(ostname)
864 ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
865 lustre.appendChild(ost)
867 lov = findByName(lustre, lovname, "lov")
869 error('add_ost:', '"'+lovname+'"', "lov element not found.")
870 lov_add_obd(gen, lov, ost_uuid)
872 ost = lookup(lustre, ost_uuid)
875 ost.setAttribute('failover', "1")
877 ost.setAttribute('failover', "0")
880 osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
881 get_format_flag(options), ost_uuid, node_uuid, size,
882 journal_size, inode_size, nspath, mkfsoptions,
883 mountfsoptions, quota)
885 node = findByName(lustre, node_name, "node")
887 ## if node_add_profile(gen, node, 'oss', oss_uuid):
889 ## oss_uuid = new_uuid(ossname)
890 ## oss = gen.oss(ossname, oss_uuid)
891 ## lustre.appendChild(oss)
893 node_add_profile(gen, node, 'osd', osd_uuid)
894 lustre.appendChild(osd)
897 def add_cobd(gen, lustre, options):
898 node_name = get_option(options, 'node')
899 name = new_name('COBD_' + node_name)
900 uuid = new_uuid(name)
902 real_name = get_option(options, 'real_obd')
903 cache_name = get_option(options, 'cache_obd')
905 real_uuid = name2uuid(lustre, real_name, tag='obd')
906 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
908 node = findByName(lustre, node_name, "node")
909 node_add_profile(gen, node, "cobd", uuid)
910 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
911 lustre.appendChild(cobd)
914 def add_echo_client(gen, lustre, options):
915 """ add an echo client to the profile for this node. """
916 node_name = get_option(options, 'node')
917 lov_name = get_option(options, 'ost')
919 node = findByName(lustre, node_name, 'node')
921 echoname = new_name('ECHO_'+ node_name)
922 echo_uuid = new_uuid(echoname)
923 node_add_profile(gen, node, 'echoclient', echo_uuid)
925 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
927 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
929 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
930 lustre.appendChild(echo)
933 def add_lov(gen, lustre, options):
936 lov_orig = get_option(options, 'lov')
937 name = new_name(lov_orig)
939 warning("name:", lov_orig, "already used. using:", name)
941 mds_name = get_option(options, 'mds')
942 stripe_sz = get_option_int(options, 'stripe_sz')
943 stripe_cnt = get_option_int(options, 'stripe_cnt')
945 info("default stripe count (0) - will use %d stripe(s) per file" \
946 % DEFAULT_STRIPE_CNT)
947 pattern = get_option_int(options, 'stripe_pattern')
948 uuid = new_uuid(name)
950 ret = findByName(lustre, name, "lov")
952 error("LOV: ", name, " already exists.")
954 mds_uuid = name2uuid(lustre, mds_name, 'mds')
955 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
956 lustre.appendChild(lov)
958 # add an lovconfig entry to the active mdsdev profile
959 lovconfig_name = new_name('LVCFG_' + name)
960 lovconfig_uuid = new_uuid(lovconfig_name)
961 mds = findByName(lustre, mds_name, "mds")
962 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
963 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
964 lustre.appendChild(lovconfig)
966 def add_default_lov(gen, lustre, mds_name, lov_name):
967 """ create a default lov """
969 stripe_sz = DEFAULT_STRIPE_SZ
970 stripe_cnt = DEFAULT_STRIPE_CNT
971 pattern = DEFAULT_STRIPE_PATTERN
972 uuid = new_uuid(lov_name)
974 ret = findByName(lustre, lov_name, "lov")
976 error("LOV: ", lov_name, " already exists.")
978 mds_uuid = name2uuid(lustre, mds_name, 'mds')
979 lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
980 lustre.appendChild(lov)
982 # add an lovconfig entry to the active mdsdev profile
983 lovconfig_name = new_name('LVCFG_' + lov_name)
984 lovconfig_uuid = new_uuid(lovconfig_name)
985 mds = findByName(lustre, mds_name)
986 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
987 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
988 lustre.appendChild(lovconfig)
990 def new_filesystem(gen, lustre, mds_uuid, obd_uuid):
991 fs_name = new_name("FS_fsname")
992 fs_uuid = new_uuid(fs_name)
993 mds = lookup(lustre, mds_uuid)
994 mds.appendChild(gen.ref("filesystem", fs_uuid))
995 fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid)
996 lustre.appendChild(fs)
999 def get_fs_uuid(gen, lustre, mds_name, obd_name):
1000 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
1001 obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
1002 fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
1004 fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid)
1007 def add_mtpt(gen, lustre, options):
1008 """ create mtpt on a node """
1009 node_name = get_option(options, 'node')
1011 path = get_option(options, 'path')
1012 clientoptions = get_option(options, "clientoptions")
1013 fs_name = get_option(options, 'filesystem')
1015 lov_name = get_option(options, 'lov')
1016 ost_name = get_option(options, 'ost')
1017 mds_name = get_option(options, 'mds')
1020 error("--add mtpt requires --lov lov_name or --ost ost_name")
1022 warning("use default value for lov, due no --lov lov_name provided")
1023 lov_name = new_name("lov_default")
1024 add_default_lov(gen, lustre, mds_name, lov_name)
1025 ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1027 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1028 lov = findByName(lustre, lov_name, "lov")
1029 lov_add_obd(gen, lov, ost_uuid)
1032 fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name)
1034 fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1036 name = new_name('MNT_'+ node_name)
1038 ret = findByName(lustre, name, "mountpoint")
1040 # this can't happen, because new_name creates unique names
1041 error("MOUNTPOINT: ", name, " already exists.")
1043 uuid = new_uuid(name)
1044 mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions)
1045 node = findByName(lustre, node_name, "node")
1047 error('node:', node_name, "not found.")
1048 node_add_profile(gen, node, "mountpoint", uuid)
1049 lustre.appendChild(mtpt)
1051 ############################################################
1052 # Command line processing
1054 class OptionError (exceptions.Exception):
1055 def __init__(self, args):
1058 def get_option(options, tag):
1059 """Look for tag in options hash and return the value if set. If not
1060 set, then if return default it is set, otherwise exception."""
1061 if options.__getattr__(tag) != None:
1062 return options.__getattr__(tag)
1064 raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1066 def get_option_int(options, tag):
1067 """Return an integer option. Raise exception if the value is not an int"""
1068 val = get_option(options, tag)
1072 raise OptionError("--%s <num> (value must be integer)" % (tag))
1075 # simple class for profiling
1082 self._start = time.time()
1083 def stop(self, msg=''):
1084 self._stop = time.time()
1088 return self._stop - self._start
1089 def display(self, msg):
1091 str = '%s: %g secs' % (msg, d)
1094 #################################################################
1095 # function cmdlinesplit used to split cmd line from batch file
1097 def cmdlinesplit(cmdline):
1099 double_quote = re.compile(r'"(([^"\\]|\\.)*)"')
1100 single_quote = re.compile(r"'(.*?)'")
1101 escaped = re.compile(r'\\(.)')
1102 esc_quote = re.compile(r'\\([\\"])')
1103 outside = re.compile(r"""([^\s\\'"]+)""")
1107 while i < len(cmdline):
1110 match = double_quote.match(cmdline, i)
1112 print "Unmatched double quote:", cmdline
1115 if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1116 else: arg = arg + esc_quote.sub(r'\1', match.group(1))
1119 match = single_quote.match(cmdline, i)
1121 print "Unmatched single quote:", cmdline
1124 if arg is None: arg = match.group(1)
1125 else: arg = arg + match.group(1)
1128 match = escaped.match(cmdline, i)
1130 print "Unmatched backslash", cmdline
1133 if arg is None: arg = match.group(1)
1134 else: arg = arg + match.group(1)
1136 elif c in string.whitespace:
1138 arg_list.append(str(arg))
1140 while i < len(cmdline) and cmdline[i] in string.whitespace:
1143 match = outside.match(cmdline, i)
1146 if arg is None: arg = match.group()
1147 else: arg = arg + match.group()
1149 if arg != None: arg_list.append(str(arg))
1153 ############################################################
1157 def add(devtype, gen, lustre, options):
1158 if devtype == 'net':
1159 add_net(gen, lustre, options)
1160 elif devtype == 'mtpt':
1161 add_mtpt(gen, lustre, options)
1162 elif devtype == 'mds':
1163 add_mds(gen, lustre, options)
1164 elif devtype == 'ost':
1165 add_ost(gen, lustre, options)
1166 elif devtype == 'lov':
1167 add_lov(gen, lustre, options)
1168 elif devtype == 'route':
1169 add_route(gen, lustre, options)
1170 elif devtype == 'node':
1171 add_node(gen, lustre, options)
1172 elif devtype == 'echo_client':
1173 add_echo_client(gen, lustre, options)
1174 elif devtype == 'cobd':
1175 add_cobd(gen, lustre, options)
1177 error("unknown device type:", devtype)
1179 def do_command(gen, lustre, options, args):
1181 add(options.add, gen, lustre, options)
1183 error("Missing command")
1186 cl = Lustre.Options("lmc", "", lmc_options)
1188 options, args = cl.parse(sys.argv[1:])
1189 except Lustre.OptionError, e:
1193 panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1195 if options.reference:
1202 outFile = options.merge
1203 if os.access(outFile, os.R_OK):
1204 doc = xml.dom.minidom.parse(outFile)
1206 doc = new_lustre(xml.dom.minidom)
1208 doc = xml.dom.minidom.parse(options.input)
1210 doc = new_lustre(xml.dom.minidom)
1213 outFile = options.output
1215 lustre = doc.documentElement
1217 if lustre.tagName != "lustre":
1218 print "Existing config not valid."
1221 gen = GenConfig(doc)
1224 fp = open(options.batch)
1225 batchCommands = fp.readlines()
1227 for cmd in batchCommands:
1229 options, args = cl.parse(cmdlinesplit(cmd))
1230 if options.merge or options.input or options.output:
1231 print "The batchfile should not contain --merge, --input or --output."
1233 do_command(gen, lustre, options, args)
1234 except OptionError, e:
1236 except Lustre.OptionError, e:
1240 do_command(gen, lustre, options, args)
1241 except OptionError, e:
1242 panic(string.join(sys.argv),e)
1243 except Lustre.OptionError, e:
1247 timestr = string.split(str(time.time()), '.')
1248 gen.recordtime(timestr[0])
1253 printDoc(doc, open(outFile,"w"))
1255 if __name__ == "__main__":