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 configuration data manager
24 See the man page, or the Lustre Operations Manual, for documentation on lmc.
28 import sys, os, getopt, string, exceptions, re
29 import xml.dom.minidom
31 def printDoc(doc, stream=sys.stdout):
33 from xml.dom.ext import PrettyPrint
34 PrettyPrint(doc, stream)
36 stream.write(doc.toxml())
40 PYMOD_DIR = ["/usr/lib/lustre/python", "/usr/lib64/lustre/python"]
42 def development_mode():
43 base = os.path.dirname(sys.argv[0])
44 if os.access(base+"/Makefile.am", os.R_OK):
48 if not development_mode():
49 sys.path.extend(PYMOD_DIR)
54 DEFAULT_STRIPE_SZ = 1048576
55 DEFAULT_STRIPE_CNT = 1
56 DEFAULT_STRIPE_PATTERN = 0
60 print """usage: lmc --add object [object parameters]
62 Object creation command summary:
71 --ptldebug debug_level
72 --subsystem subsystem_name
78 --nettype tcp|elan|gm|openib|iib|vib|ra|ptl|lnet
79 --hostaddr ip[/netmask]
97 --mountfsoptions options
98 --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune=
115 --fstype ldiskfs|ext3
118 --osdtype obdecho|obdfilter
120 --mkfsoptions options
121 --mountfsoptions options
122 --quota quotaon=u|g|ug,iunit=,bunit=,itune=,btune=
124 --add mtpt - Mountpoint
128 --ost ost_name OR --lov lov_name
129 --clientoptions options
135 --gateway_cluster_id nid
136 --target_cluster_id nid
144 PARAM = Lustre.Options.PARAM
145 PARAMLIST = Lustre.Options.PARAMLIST
147 # lmc input/output options
148 ('reference', "Print short reference for commands."),
149 ('verbose,v', "Print system commands as they are run."),
150 ('merge,m', "Append to the specified config file.", PARAM),
151 ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
152 ('input,i', "", PARAM),
153 ('batch', "Used to execute lmc commands in batch mode.", PARAM),
159 ('node', "Add a new node in the cluster configuration.", PARAM),
160 ('timeout', "Set timeout to initiate recovery.", PARAM),
161 ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
162 ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
163 ('group_upcall', "Set location of extended group upcall script.", PARAM),
164 ('portals_upcall', "Set location of portals upcall script.", PARAM),
165 ('ptldebug', "Set the portals debug level", PARAM),
166 ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log", PARAM),
169 ('nettype', "Specify the network type. This can be tcp/elan/gm/openib/iib/vib/ra/ptl/lnet.", PARAM),
170 ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
171 ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
172 ('hostaddr', "Optional argument to specify the host address.", PARAMLIST),
173 ('cluster_id', "Specify the cluster ID", PARAM, "0"),
174 ('nonet', "Skip the remote host networking check"),
177 ('route', "Add a new route for the cluster.", PARAM),
178 ('router', "Optional flag to mark a node as router."),
179 ('gw', "Specify the nid of the gateway for a route.", PARAM),
180 ('gateway_cluster_id', "", PARAM, "0"),
181 ('target_cluster_id', "", PARAM, "0"),
182 ('lo', "For a range route, this is the low value nid.", PARAM),
183 ('hi', "For a range route, this is a hi value nid.", PARAM,""),
185 # servers: mds and ost
186 ('mds', "Specify MDS name.", PARAM),
187 ('ost', "Specify the OST name.", PARAM,""),
188 ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
189 ('failout', "Disable failover support on OST"),
190 ('failover', "Enable failover support on OST"),
191 ('group', "", PARAM),
192 ('dev', "Path of the device on local system.", PARAM,""),
193 ('size', "Specify the size of the device if needed.", PARAM,"0"),
194 ('group_upcall', "Set location of supplementary group upcall.", PARAM,""),
195 ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"),
196 ('inode_size', "Specify new inode size for underlying ext3 file system.", PARAM,"0"),
197 ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
198 ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
199 ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""),
200 ('ostuuid', "Optional argument to specify OST UUID", PARAM,""),
201 ('mdsuuid', "Optional argument to specify MDS UUID", PARAM,""),
202 ('nspath', "Local mount point of server namespace.", PARAM,""),
204 ('quota', "quotaon:enable quota, only u|g|ug is supported now. \
205 iunit: the unit for slave to acquire/release inode quota from/to masteri.\
206 Int type (>0), default value in Lustre is 5000 inodes.\
207 bunit: the unit for slave to acquire/release block quota from/to master.\
208 Mbytes (>0), default value in Lustre is 100(Mbytes).\
209 itune: used to tune the threthold. When inode quota usage reach the threthold,\
210 slave should acquire/release inode quota from/to master.\
211 Int type (100 > btune > 0), default value in Lustre is 50 (percentge).\
212 inode threthold = iunit * itune / 100.\
213 btune: used to tune the threthold. When block quota usage reach the threthold,\
214 slave should acquire/release block quota from/to master.\
215 Int type (100 > btune > 0), default value in Lustre is 50 (percentage).\
216 block threthold = bunit * btune / 100.", PARAM,""),
217 # clients: mountpoint and echo
218 ('echo_client', "", PARAM),
219 ('path', "Specify the mountpoint for Lustre.", PARAM),
220 ('filesystem', "Lustre filesystem name", PARAM,""),
221 ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""),
224 ('lov', "Specify LOV name.", PARAM,""),
225 ('stripe_sz', "Specify the stripe size in bytes.", PARAM, DEFAULT_STRIPE_SZ),
226 ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, DEFAULT_STRIPE_CNT),
227 ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
230 ('real_obd', "Specify the real device for the cache obd system.", PARAM),
231 ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
235 msg = string.join(map(str,args))
236 raise OptionError("Error: " + msg)
244 msg = string.join(map(str,args))
245 sys.stderr.write("WARNING: %s\n" % (msg))
248 msg = string.join(map(str,args))
249 sys.stderr.write("INFO: %s\n" % (msg))
252 # manage names and uuids
253 # need to initialize this by walking tree to ensure
254 # no duplicate names or uuids are created.
255 # this are just place holders for now.
256 # consider changing this to be like OBD-dev-host
260 while names.has_key(ret):
261 ret = "%s_%d" % (base, ctr)
268 ret = "%s_UUID" % (name)
269 if len(ret) > UUID_MAX_LENGTH:
270 ret = ret[-UUID_MAX_LENGTH:]
271 while uuids.has_key(ret):
272 ret = "%s_UUID_%d" % (name, ctr)
274 if len(ret) > UUID_MAX_LENGTH:
275 ret = ret[-UUID_MAX_LENGTH:]
281 ldlm_uuid = 'ldlm_UUID'
284 """Create a new empty lustre document"""
285 # adding ldlm here is a bit of a hack, but one is enough.
286 str = """<lustre version="%s">
287 <ldlm name="%s" uuid="%s"/>
288 </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
289 return dom.parseString(str)
296 """initialize auto-name generation tables"""
298 # get all elements that contain a name attribute
299 for n in doc.childNodes:
300 if n.nodeType == n.ELEMENT_NODE:
302 names[getName(n)] = 1
303 uuids[getUUID(n)] = 1
306 def get_format_flag(options):
311 ############################################################
312 # Build config objects using DOM
317 def __init__(self, doc):
320 def ref(self, type, uuid):
321 """ generate <[type]_ref uuidref="[uuid]"/> """
322 tag = "%s_ref" % (type)
323 ref = self.doc.createElement(tag)
324 ref.setAttribute("uuidref", uuid)
327 def newService(self, tag, name, uuid):
328 """ create a new service elmement, which requires name and uuid attributes """
329 new = self.doc.createElement(tag)
330 new.setAttribute("uuid", uuid);
331 new.setAttribute("name", name);
334 def addText(self, node, str):
335 txt = self.doc.createTextNode(str)
336 node.appendChild(txt)
338 def addElement(self, node, tag, str=None):
339 """ create a new element and add it as a child to node. If str is passed,
340 a text node is created for the new element"""
341 new = self.doc.createElement(tag)
343 self.addText(new, str)
344 node.appendChild(new)
347 def recordtime(self, timestr):
348 lustre = self.doc.getElementsByTagName("lustre")
349 lustre[0].setAttribute("mtime", timestr)
351 def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
353 """create <network> node"""
354 network = self.newService("network", name, uuid)
355 network.setAttribute("nettype", net);
356 self.addElement(network, "nid", nid)
357 self.addElement(network, "clusterid", cluster_id)
358 for host in hostaddr:
359 self.addElement(network, "hostaddr", host)
361 self.addElement(network, "port", "%d" %(port))
365 def routetbl(self, name, uuid):
366 """create <routetbl> node"""
367 rtbl = self.newService("routetbl", name, uuid)
370 def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
371 """ create one entry for the route table """
372 ref = self.doc.createElement('route')
373 ref.setAttribute("type", gw_net_type)
374 ref.setAttribute("gw", gw)
375 ref.setAttribute("gwclusterid", gw_cluster_id)
376 ref.setAttribute("tgtclusterid", tgt_cluster_id)
377 ref.setAttribute("lo", lo)
379 ref.setAttribute("hi", hi)
382 def profile(self, name, uuid):
383 """ create a host """
384 profile = self.newService("profile", name, uuid)
387 def node(self, name, uuid, prof_uuid):
388 """ create a host """
389 node = self.newService("node", name, uuid)
390 node.appendChild(self.ref("profile", prof_uuid))
393 def ldlm(self, name, uuid):
394 """ create a ldlm """
395 ldlm = self.newService("ldlm", name, uuid)
398 def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
399 node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="",
400 mkfsoptions="", mountfsoptions="", quota=""):
401 osd = self.newService("osd", name, uuid)
402 osd.setAttribute('osdtype', osdtype)
403 osd.appendChild(self.ref("target", ost_uuid))
404 osd.appendChild(self.ref("node", node_uuid))
406 self.addElement(osd, "fstype", fstype)
408 dev = self.addElement(osd, "devpath", devname)
409 self.addElement(osd, "autoformat", format)
411 self.addElement(osd, "devsize", "%s" % (dev_size))
413 self.addElement(osd, "journalsize", "%s" % (journal_size))
415 self.addElement(osd, "inodesize", "%s" % (inode_size))
417 self.addElement(osd, "mkfsoptions", mkfsoptions)
419 self.addElement(osd, "mountfsoptions", mountfsoptions)
421 self.addElement(osd, "quota", quota)
423 self.addElement(osd, "nspath", nspath)
426 def cobd(self, name, uuid, real_uuid, cache_uuid):
427 cobd = self.newService("cobd", name, uuid)
428 cobd.appendChild(self.ref("realobd",real_uuid))
429 cobd.appendChild(self.ref("cacheobd",cache_uuid))
432 def ost(self, name, uuid, osd_uuid, group=""):
433 ost = self.newService("ost", name, uuid)
434 ost.appendChild(self.ref("active", osd_uuid))
436 self.addElement(ost, "group", group)
439 def oss(self, name, uuid):
440 oss = self.newService("oss", name, uuid)
443 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
444 lov = self.newService("lov", name, uuid)
445 lov.appendChild(self.ref("mds", mds_uuid))
446 lov.setAttribute("stripesize", str(stripe_sz))
447 lov.setAttribute("stripecount", str(stripe_cnt))
448 lov.setAttribute("stripepattern", str(pattern))
451 def lovconfig(self, name, uuid, lov_uuid):
452 lovconfig = self.newService("lovconfig", name, uuid)
453 lovconfig.appendChild(self.ref("lov", lov_uuid))
456 def mds(self, name, uuid, mdd_uuid, group=""):
457 mds = self.newService("mds", name, uuid)
458 mds.appendChild(self.ref("active",mdd_uuid))
460 self.addElement(mds, "group", group)
463 def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
464 mds_uuid, dev_size=0, journal_size=0, inode_size=256,
465 nspath="", mkfsoptions="", mountfsoptions="", quota="", group_upcall=""):
466 mdd = self.newService("mdsdev", name, uuid)
467 self.addElement(mdd, "fstype", fstype)
468 dev = self.addElement(mdd, "devpath", devname)
469 self.addElement(mdd, "autoformat", format)
471 self.addElement(mdd, "devsize", "%s" % (dev_size))
473 self.addElement(mdd, "journalsize", "%s" % (journal_size))
475 self.addElement(mdd, "inodesize", "%s" % (inode_size))
477 self.addElement(mdd, "nspath", nspath)
479 self.addElement(mdd, "mkfsoptions", mkfsoptions)
481 self.addElement(mdd, "mountfsoptions", mountfsoptions)
483 self.addElement(mdd, "quota", quota)
485 self.addElement(mdd, "group_upcall", group_upcall)
487 mdd.appendChild(self.ref("node", node_uuid))
488 mdd.appendChild(self.ref("target", mds_uuid))
491 def mountpoint(self, name, uuid, fs_uuid, path, clientoptions):
492 mtpt = self.newService("mountpoint", name, uuid)
493 mtpt.appendChild(self.ref("filesystem", fs_uuid))
494 self.addElement(mtpt, "path", path)
496 self.addElement(mtpt, "clientoptions", clientoptions)
499 def filesystem(self, name, uuid, mds_uuid, obd_uuid):
500 fs = self.newService("filesystem", name, uuid)
501 fs.appendChild(self.ref("mds", mds_uuid))
502 fs.appendChild(self.ref("obd", obd_uuid))
505 def echo_client(self, name, uuid, osc_uuid):
506 ec = self.newService("echoclient", name, uuid)
507 ec.appendChild(self.ref("obd", osc_uuid))
510 ############################################################
511 # Utilities to query a DOM tree
512 # Using this functions we can treat use config information
513 # directly as a database.
515 return n.getAttribute('name')
518 return node.getAttribute('uuid')
521 def findByName(lustre, name, tag = ""):
522 for n in lustre.childNodes:
523 if n.nodeType == n.ELEMENT_NODE:
524 if tag and n.nodeName != tag:
526 if getName(n) == name:
529 n = findByName(n, name)
534 def lookup(node, uuid):
535 for n in node.childNodes:
536 if n.nodeType == n.ELEMENT_NODE:
537 if getUUID(n) == uuid:
545 def name2uuid(lustre, name, tag="", fatal=1):
546 ret = findByName(lustre, name, tag)
549 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
554 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
555 for n in lustre.childNodes:
556 if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
557 if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
561 # XXX: assumes only one network element per node. will fix this
562 # as soon as support for routers is added
563 def get_net_uuid(lustre, node_name):
564 """ get a network uuid for a node_name """
565 node = findByName(lustre, node_name, "node")
567 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
568 net = node.getElementsByTagName('network')
570 return getUUID(net[0])
574 def lov_add_obd(gen, lov, osc_uuid):
575 lov.appendChild(gen.ref("obd", osc_uuid))
577 def ref_exists(profile, uuid):
578 elist = profile.childNodes
580 if e.nodeType == e.ELEMENT_NODE:
581 ref = e.getAttribute('uuidref')
586 # ensure that uuid is not already in the profile
587 # return true if uuid is added
588 def node_add_profile(gen, node, ref, uuid):
589 refname = "%s_ref" % "profile"
590 ret = node.getElementsByTagName(refname)
592 error('node has no profile ref:', node)
593 prof_uuid = ret[0].getAttribute('uuidref')
594 profile = lookup(node.parentNode, prof_uuid)
596 error("no profile found:", prof_uuid)
597 if ref_exists(profile, uuid):
599 profile.appendChild(gen.ref(ref, uuid))
602 def get_attr(dom_node, attr, default=""):
603 v = dom_node.getAttribute(attr)
608 ############################################################
620 def set_node_options(gen, node, options):
622 node.setAttribute('router', '1')
624 gen.addElement(node, "timeout", get_option(options, 'timeout'))
626 default_upcall = get_option(options, 'upcall')
629 if default_upcall or options.lustre_upcall:
630 if options.lustre_upcall:
631 gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
633 gen.addElement(node, 'lustreUpcall', default_upcall)
634 if options.group_upcall:
635 gen.addElement(node, 'groupUpcall', options.group_upcall)
636 if default_upcall or options.portals_upcall:
637 if options.portals_upcall:
638 gen.addElement(node, 'portalsUpcall', options.portals_upcall)
640 gen.addElement(node, 'portalsUpcall', default_upcall)
642 gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
643 if options.subsystem:
644 gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
647 def do_add_node(gen, lustre, options, node_name):
648 uuid = new_uuid(node_name)
649 prof_name = new_name("PROFILE_" + node_name)
650 prof_uuid = new_uuid(prof_name)
651 profile = gen.profile(prof_name, prof_uuid)
652 node = gen.node(node_name, uuid, prof_uuid)
653 lustre.appendChild(node)
654 lustre.appendChild(profile)
656 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
657 set_node_options(gen, node, options)
661 def add_node(gen, lustre, options):
662 """ create a node with a network config """
664 node_name = get_option(options, 'node')
665 ret = findByName(lustre, node_name, "node")
667 print "Node:", node_name, "exists."
669 do_add_node(gen, lustre, options, node_name)
672 def add_net(gen, lustre, options):
673 """ create a node with a network config """
675 node_name = get_option(options, 'node')
676 nid = get_option(options, 'nid')
677 cluster_id = get_option(options, 'cluster_id')
678 hostaddr = get_option(options, 'hostaddr')
679 net_type = get_option(options, 'nettype')
681 if net_type in ('lnet','tcp','openib','ra'):
682 port = get_option_int(options, 'port')
683 elif net_type in ('elan','gm','iib','vib','lo','ptl'):
686 print "Unknown net_type: ", net_type
689 real_net_type = net_type
690 if net_type == 'lnet' and string.find(nid,'@') > 0:
691 real_net_type = string.split(nid,'@')[1]
696 print "Skip the remote host networking test."
697 elif (node_name != 'client') and (real_net_type == 'tcp'):
699 print "Testing network on", node_name
700 target = string.split(nid,'@')[0]
701 out = runcmd("ping -c 1 -w 10 %s" %target)
703 print "Could not connect to", node_name,", Please check network."
705 ret = findByName(lustre, node_name, "node")
707 node = do_add_node(gen, lustre, options, node_name)
710 set_node_options(gen, node, options)
712 net_name = new_name('NET_'+ node_name +'_'+ net_type)
713 net_uuid = new_uuid(net_name)
714 node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
716 node_add_profile(gen, node, "network", net_uuid)
719 def add_route(gen, lustre, options):
720 """ create a node with a network config """
722 node_name = get_option(options, 'node')
723 gw_net_type = get_option(options, 'nettype')
724 gw = get_option(options, 'gw')
725 gw_cluster_id = get_option(options, 'gateway_cluster_id')
726 tgt_cluster_id = get_option(options, 'target_cluster_id')
727 lo = get_option(options, 'lo')
728 hi = get_option(options, 'hi')
732 node = findByName(lustre, node_name, "node")
734 error (node_name, " not found.")
736 rlist = node.getElementsByTagName('routetbl')
740 rtbl_name = new_name("RTBL_" + node_name)
741 rtbl_uuid = new_uuid(rtbl_name)
742 rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
743 node.appendChild(rtbl)
744 node_add_profile(gen, node, "routetbl", rtbl_uuid)
745 rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
749 def add_mds(gen, lustre, options):
750 node_name = get_option(options, 'node')
751 mds_name = get_option(options, 'mds')
752 mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
753 mdd_uuid = new_uuid(mdd_name)
755 mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
757 mds_uuid = get_option(options, 'mdsuuid')
759 if lookup(lustre, mds_uuid):
760 error("Duplicate MDS UUID:", mds_uuid)
762 mds_uuid = new_uuid(mds_name)
764 mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
765 lustre.appendChild(mds)
767 mds = lookup(lustre, mds_uuid)
769 mds.setAttribute('failover', "1")
771 mds.setAttribute('failover,',"0")
773 devname = get_option(options, 'dev')
774 size = get_option(options, 'size')
775 fstype = get_option(options, 'fstype')
776 journal_size = get_option(options, 'journal_size')
777 inode_size = get_option(options, 'inode_size')
778 nspath = get_option(options, 'nspath')
779 mkfsoptions = get_option(options, 'mkfsoptions')
780 mountfsoptions = get_option(options, 'mountfsoptions')
781 quota = get_option(options, 'quota')
782 group_upcall = get_option(options, 'group_upcall')
784 node_uuid = name2uuid(lustre, node_name, 'node')
786 node = findByName(lustre, node_name, "node")
787 node_add_profile(gen, node, "mdsdev", mdd_uuid)
788 net_uuid = get_net_uuid(lustre, node_name)
790 error("NODE: ", node_name, "not found")
792 mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
793 get_format_flag(options), node_uuid, mds_uuid,
794 size, journal_size, inode_size, nspath, mkfsoptions,
795 mountfsoptions, quota, group_upcall)
796 lustre.appendChild(mdd)
799 def add_ost(gen, lustre, options):
800 node_name = get_option(options, 'node')
801 lovname = get_option(options, 'lov')
802 osdtype = get_option(options, 'osdtype')
804 node_uuid = name2uuid(lustre, node_name, 'node')
806 if osdtype == 'obdecho':
817 devname = get_option(options, 'dev') # can be unset for bluearcs
818 size = get_option(options, 'size')
819 fstype = get_option(options, 'fstype')
820 journal_size = get_option(options, 'journal_size')
821 inode_size = get_option(options, 'inode_size')
822 mkfsoptions = get_option(options, 'mkfsoptions')
823 mountfsoptions = get_option(options, 'mountfsoptions')
824 quota = get_option(options, 'quota')
826 nspath = get_option(options, 'nspath')
828 ostname = get_option(options, 'ost')
830 ostname = new_name('OST_'+ node_name)
832 osdname = new_name("OSD_" + ostname + "_" + node_name)
833 osd_uuid = new_uuid(osdname)
835 ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
837 ost_uuid = get_option(options, 'ostuuid')
839 if lookup(lustre, ost_uuid):
840 error("Duplicate OST UUID:", ost_uuid)
842 ost_uuid = new_uuid(ostname)
844 ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
845 lustre.appendChild(ost)
847 lov = findByName(lustre, lovname, "lov")
849 error('add_ost:', '"'+lovname+'"', "lov element not found.")
850 lov_add_obd(gen, lov, ost_uuid)
852 ost = lookup(lustre, ost_uuid)
855 ost.setAttribute('failover', "1")
857 ost.setAttribute('failover', "0")
860 osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
861 get_format_flag(options), ost_uuid, node_uuid, size,
862 journal_size, inode_size, nspath, mkfsoptions,
863 mountfsoptions, quota)
865 node = findByName(lustre, node_name, "node")
867 ## if node_add_profile(gen, node, 'oss', oss_uuid):
869 ## oss_uuid = new_uuid(ossname)
870 ## oss = gen.oss(ossname, oss_uuid)
871 ## lustre.appendChild(oss)
873 node_add_profile(gen, node, 'osd', osd_uuid)
874 lustre.appendChild(osd)
877 def add_cobd(gen, lustre, options):
878 node_name = get_option(options, 'node')
879 name = new_name('COBD_' + node_name)
880 uuid = new_uuid(name)
882 real_name = get_option(options, 'real_obd')
883 cache_name = get_option(options, 'cache_obd')
885 real_uuid = name2uuid(lustre, real_name, tag='obd')
886 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
888 node = findByName(lustre, node_name, "node")
889 node_add_profile(gen, node, "cobd", uuid)
890 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
891 lustre.appendChild(cobd)
894 def add_echo_client(gen, lustre, options):
895 """ add an echo client to the profile for this node. """
896 node_name = get_option(options, 'node')
897 lov_name = get_option(options, 'ost')
899 node = findByName(lustre, node_name, 'node')
901 echoname = new_name('ECHO_'+ node_name)
902 echo_uuid = new_uuid(echoname)
903 node_add_profile(gen, node, 'echoclient', echo_uuid)
905 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
907 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
909 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
910 lustre.appendChild(echo)
913 def add_lov(gen, lustre, options):
916 lov_orig = get_option(options, 'lov')
917 name = new_name(lov_orig)
919 warning("name:", lov_orig, "already used. using:", name)
921 mds_name = get_option(options, 'mds')
922 stripe_sz = get_option_int(options, 'stripe_sz')
923 stripe_cnt = get_option_int(options, 'stripe_cnt')
925 info("default stripe count (0) - will use %d stripe(s) per file" \
926 % DEFAULT_STRIPE_CNT)
927 pattern = get_option_int(options, 'stripe_pattern')
928 uuid = new_uuid(name)
930 ret = findByName(lustre, name, "lov")
932 error("LOV: ", name, " already exists.")
934 mds_uuid = name2uuid(lustre, mds_name, 'mds')
935 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
936 lustre.appendChild(lov)
938 # add an lovconfig entry to the active mdsdev profile
939 lovconfig_name = new_name('LVCFG_' + name)
940 lovconfig_uuid = new_uuid(lovconfig_name)
941 mds = findByName(lustre, mds_name, "mds")
942 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
943 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
944 lustre.appendChild(lovconfig)
946 def add_default_lov(gen, lustre, mds_name, lov_name):
947 """ create a default lov """
949 stripe_sz = DEFAULT_STRIPE_SZ
950 stripe_cnt = DEFAULT_STRIPE_CNT
951 pattern = DEFAULT_STRIPE_PATTERN
952 uuid = new_uuid(lov_name)
954 ret = findByName(lustre, lov_name, "lov")
956 error("LOV: ", lov_name, " already exists.")
958 mds_uuid = name2uuid(lustre, mds_name, 'mds')
959 lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
960 lustre.appendChild(lov)
962 # add an lovconfig entry to the active mdsdev profile
963 lovconfig_name = new_name('LVCFG_' + lov_name)
964 lovconfig_uuid = new_uuid(lovconfig_name)
965 mds = findByName(lustre, mds_name)
966 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
967 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
968 lustre.appendChild(lovconfig)
970 def new_filesystem(gen, lustre, mds_uuid, obd_uuid):
971 fs_name = new_name("FS_fsname")
972 fs_uuid = new_uuid(fs_name)
973 mds = lookup(lustre, mds_uuid)
974 mds.appendChild(gen.ref("filesystem", fs_uuid))
975 fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid)
976 lustre.appendChild(fs)
979 def get_fs_uuid(gen, lustre, mds_name, obd_name):
980 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
981 obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
982 fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
984 fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid)
987 def add_mtpt(gen, lustre, options):
988 """ create mtpt on a node """
989 node_name = get_option(options, 'node')
991 path = get_option(options, 'path')
992 clientoptions = get_option(options, "clientoptions")
993 fs_name = get_option(options, 'filesystem')
995 lov_name = get_option(options, 'lov')
996 ost_name = get_option(options, 'ost')
997 mds_name = get_option(options, 'mds')
1000 error("--add mtpt requires --lov lov_name or --ost ost_name")
1002 warning("use default value for lov, due no --lov lov_name provided")
1003 lov_name = new_name("lov_default")
1004 add_default_lov(gen, lustre, mds_name, lov_name)
1005 ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1007 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1008 lov = findByName(lustre, lov_name, "lov")
1009 lov_add_obd(gen, lov, ost_uuid)
1012 fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name)
1014 fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1016 name = new_name('MNT_'+ node_name)
1018 ret = findByName(lustre, name, "mountpoint")
1020 # this can't happen, because new_name creates unique names
1021 error("MOUNTPOINT: ", name, " already exists.")
1023 uuid = new_uuid(name)
1024 mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions)
1025 node = findByName(lustre, node_name, "node")
1027 error('node:', node_name, "not found.")
1028 node_add_profile(gen, node, "mountpoint", uuid)
1029 lustre.appendChild(mtpt)
1031 ############################################################
1032 # Command line processing
1034 class OptionError (exceptions.Exception):
1035 def __init__(self, args):
1038 def get_option(options, tag):
1039 """Look for tag in options hash and return the value if set. If not
1040 set, then if return default it is set, otherwise exception."""
1041 if options.__getattr__(tag) != None:
1042 return options.__getattr__(tag)
1044 raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1046 def get_option_int(options, tag):
1047 """Return an integer option. Raise exception if the value is not an int"""
1048 val = get_option(options, tag)
1052 raise OptionError("--%s <num> (value must be integer)" % (tag))
1055 # simple class for profiling
1062 self._start = time.time()
1063 def stop(self, msg=''):
1064 self._stop = time.time()
1068 return self._stop - self._start
1069 def display(self, msg):
1071 str = '%s: %g secs' % (msg, d)
1074 #################################################################
1075 # function cmdlinesplit used to split cmd line from batch file
1077 def cmdlinesplit(cmdline):
1079 double_quote = re.compile(r'"(([^"\\]|\\.)*)"')
1080 single_quote = re.compile(r"'(.*?)'")
1081 escaped = re.compile(r'\\(.)')
1082 esc_quote = re.compile(r'\\([\\"])')
1083 outside = re.compile(r"""([^\s\\'"]+)""")
1087 while i < len(cmdline):
1090 match = double_quote.match(cmdline, i)
1092 print "Unmatched double quote:", cmdline
1095 if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1096 else: arg = arg + esc_quote.sub(r'\1', match.group(1))
1099 match = single_quote.match(cmdline, i)
1101 print "Unmatched single quote:", cmdline
1104 if arg is None: arg = match.group(1)
1105 else: arg = arg + match.group(1)
1108 match = escaped.match(cmdline, i)
1110 print "Unmatched backslash", cmdline
1113 if arg is None: arg = match.group(1)
1114 else: arg = arg + match.group(1)
1116 elif c in string.whitespace:
1118 arg_list.append(str(arg))
1120 while i < len(cmdline) and cmdline[i] in string.whitespace:
1123 match = outside.match(cmdline, i)
1126 if arg is None: arg = match.group()
1127 else: arg = arg + match.group()
1129 if arg != None: arg_list.append(str(arg))
1133 ############################################################
1137 def add(devtype, gen, lustre, options):
1138 if devtype == 'net':
1139 add_net(gen, lustre, options)
1140 elif devtype == 'mtpt':
1141 add_mtpt(gen, lustre, options)
1142 elif devtype == 'mds':
1143 add_mds(gen, lustre, options)
1144 elif devtype == 'ost':
1145 add_ost(gen, lustre, options)
1146 elif devtype == 'lov':
1147 add_lov(gen, lustre, options)
1148 elif devtype == 'route':
1149 add_route(gen, lustre, options)
1150 elif devtype == 'node':
1151 add_node(gen, lustre, options)
1152 elif devtype == 'echo_client':
1153 add_echo_client(gen, lustre, options)
1154 elif devtype == 'cobd':
1155 add_cobd(gen, lustre, options)
1157 error("unknown device type:", devtype)
1159 def do_command(gen, lustre, options, args):
1161 add(options.add, gen, lustre, options)
1163 error("Missing command")
1166 cl = Lustre.Options("lmc", "", lmc_options)
1168 options, args = cl.parse(sys.argv[1:])
1169 except Lustre.OptionError, e:
1173 panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1175 if options.reference:
1182 outFile = options.merge
1183 if os.access(outFile, os.R_OK):
1184 doc = xml.dom.minidom.parse(outFile)
1186 doc = new_lustre(xml.dom.minidom)
1188 doc = xml.dom.minidom.parse(options.input)
1190 doc = new_lustre(xml.dom.minidom)
1193 outFile = options.output
1195 lustre = doc.documentElement
1197 if lustre.tagName != "lustre":
1198 print "Existing config not valid."
1201 gen = GenConfig(doc)
1204 fp = open(options.batch)
1205 batchCommands = fp.readlines()
1207 for cmd in batchCommands:
1209 options, args = cl.parse(cmdlinesplit(cmd))
1210 if options.merge or options.input or options.output:
1211 print "The batchfile should not contain --merge, --input or --output."
1213 do_command(gen, lustre, options, args)
1214 except OptionError, e:
1216 except Lustre.OptionError, e:
1220 do_command(gen, lustre, options, args)
1221 except OptionError, e:
1222 panic(string.join(sys.argv),e)
1223 except Lustre.OptionError, e:
1227 timestr = string.split(str(time.time()), '.')
1228 gen.recordtime(timestr[0])
1233 printDoc(doc, open(outFile,"w"))
1235 if __name__ == "__main__":