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,""),
205 quotaon: enable quota, only u|g|ug is supported now.
206 iunit: the unit for slave to acquire/release inode quota from/to master.
207 Int type (>0), default value in Lustre is 5000 inodes.
208 bunit: the unit for slave to acquire/release block quota from/to master.
209 Mbytes (>0), default value in Lustre is 100(Mbytes).
210 itune: used to tune the threthold. When inode quota usage reach the threthold,
211 slave should acquire/release inode quota from/to master.
212 Int type (100 > btune > 0), default value in Lustre is 50 (percentge).
213 inode threthold = iunit * itune / 100.
214 btune: used to tune the threthold. When block quota usage reach the threthold,
215 slave should acquire/release block quota from/to master.
216 Int type (100 > btune > 0), default value in Lustre is 50 (percentage).
217 block threthold = bunit * btune / 100.""", PARAM,""),
218 # clients: mountpoint and echo
219 ('echo_client', "", PARAM),
220 ('path', "Specify the mountpoint for Lustre.", PARAM),
221 ('filesystem', "Lustre filesystem name", PARAM,""),
222 ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""),
225 ('lov', "Specify LOV name.", PARAM,""),
226 ('stripe_sz', "Specify the stripe size in bytes.", PARAM, DEFAULT_STRIPE_SZ),
227 ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, DEFAULT_STRIPE_CNT),
228 ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
231 ('real_obd', "Specify the real device for the cache obd system.", PARAM),
232 ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
236 msg = string.join(map(str,args))
237 raise OptionError("Error: " + msg)
245 msg = string.join(map(str,args))
246 sys.stderr.write("WARNING: %s\n" % (msg))
249 msg = string.join(map(str,args))
250 sys.stderr.write("INFO: %s\n" % (msg))
253 # manage names and uuids
254 # need to initialize this by walking tree to ensure
255 # no duplicate names or uuids are created.
256 # this are just place holders for now.
257 # consider changing this to be like OBD-dev-host
261 while names.has_key(ret):
262 ret = "%s_%d" % (base, ctr)
269 ret = "%s_UUID" % (name)
270 if len(ret) > UUID_MAX_LENGTH:
271 ret = ret[-UUID_MAX_LENGTH:]
272 while uuids.has_key(ret):
273 ret = "%s_UUID_%d" % (name, ctr)
275 if len(ret) > UUID_MAX_LENGTH:
276 ret = ret[-UUID_MAX_LENGTH:]
282 ldlm_uuid = 'ldlm_UUID'
285 """Create a new empty lustre document"""
286 # adding ldlm here is a bit of a hack, but one is enough.
287 str = """<lustre version="%s">
288 <ldlm name="%s" uuid="%s"/>
289 </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
290 return dom.parseString(str)
297 """initialize auto-name generation tables"""
299 # get all elements that contain a name attribute
300 for n in doc.childNodes:
301 if n.nodeType == n.ELEMENT_NODE:
303 names[getName(n)] = 1
304 uuids[getUUID(n)] = 1
307 def get_format_flag(options):
312 ############################################################
313 # Build config objects using DOM
318 def __init__(self, doc):
321 def ref(self, type, uuid):
322 """ generate <[type]_ref uuidref="[uuid]"/> """
323 tag = "%s_ref" % (type)
324 ref = self.doc.createElement(tag)
325 ref.setAttribute("uuidref", uuid)
328 def newService(self, tag, name, uuid):
329 """ create a new service elmement, which requires name and uuid attributes """
330 new = self.doc.createElement(tag)
331 new.setAttribute("uuid", uuid);
332 new.setAttribute("name", name);
335 def addText(self, node, str):
336 txt = self.doc.createTextNode(str)
337 node.appendChild(txt)
339 def addElement(self, node, tag, str=None):
340 """ create a new element and add it as a child to node. If str is passed,
341 a text node is created for the new element"""
342 new = self.doc.createElement(tag)
344 self.addText(new, str)
345 node.appendChild(new)
348 def recordtime(self, timestr):
349 lustre = self.doc.getElementsByTagName("lustre")
350 lustre[0].setAttribute("mtime", timestr)
352 def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
354 """create <network> node"""
355 network = self.newService("network", name, uuid)
356 network.setAttribute("nettype", net);
357 self.addElement(network, "nid", nid)
358 self.addElement(network, "clusterid", cluster_id)
359 for host in hostaddr:
360 self.addElement(network, "hostaddr", host)
362 self.addElement(network, "port", "%d" %(port))
366 def routetbl(self, name, uuid):
367 """create <routetbl> node"""
368 rtbl = self.newService("routetbl", name, uuid)
371 def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
372 """ create one entry for the route table """
373 ref = self.doc.createElement('route')
374 ref.setAttribute("type", gw_net_type)
375 ref.setAttribute("gw", gw)
376 ref.setAttribute("gwclusterid", gw_cluster_id)
377 ref.setAttribute("tgtclusterid", tgt_cluster_id)
378 ref.setAttribute("lo", lo)
380 ref.setAttribute("hi", hi)
383 def profile(self, name, uuid):
384 """ create a host """
385 profile = self.newService("profile", name, uuid)
388 def node(self, name, uuid, prof_uuid):
389 """ create a host """
390 node = self.newService("node", name, uuid)
391 node.appendChild(self.ref("profile", prof_uuid))
394 def ldlm(self, name, uuid):
395 """ create a ldlm """
396 ldlm = self.newService("ldlm", name, uuid)
399 def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
400 node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="",
401 mkfsoptions="", mountfsoptions="", quota=""):
402 osd = self.newService("osd", name, uuid)
403 osd.setAttribute('osdtype', osdtype)
404 osd.appendChild(self.ref("target", ost_uuid))
405 osd.appendChild(self.ref("node", node_uuid))
407 self.addElement(osd, "fstype", fstype)
409 dev = self.addElement(osd, "devpath", devname)
410 self.addElement(osd, "autoformat", format)
412 self.addElement(osd, "devsize", "%s" % (dev_size))
414 self.addElement(osd, "journalsize", "%s" % (journal_size))
416 self.addElement(osd, "inodesize", "%s" % (inode_size))
418 self.addElement(osd, "mkfsoptions", mkfsoptions)
420 self.addElement(osd, "mountfsoptions", mountfsoptions)
422 self.addElement(osd, "quota", quota)
424 self.addElement(osd, "nspath", nspath)
427 def cobd(self, name, uuid, real_uuid, cache_uuid):
428 cobd = self.newService("cobd", name, uuid)
429 cobd.appendChild(self.ref("realobd",real_uuid))
430 cobd.appendChild(self.ref("cacheobd",cache_uuid))
433 def ost(self, name, uuid, osd_uuid, group=""):
434 ost = self.newService("ost", name, uuid)
435 ost.appendChild(self.ref("active", osd_uuid))
437 self.addElement(ost, "group", group)
440 def oss(self, name, uuid):
441 oss = self.newService("oss", name, uuid)
444 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
445 lov = self.newService("lov", name, uuid)
446 lov.appendChild(self.ref("mds", mds_uuid))
447 lov.setAttribute("stripesize", str(stripe_sz))
448 lov.setAttribute("stripecount", str(stripe_cnt))
449 lov.setAttribute("stripepattern", str(pattern))
452 def lovconfig(self, name, uuid, lov_uuid):
453 lovconfig = self.newService("lovconfig", name, uuid)
454 lovconfig.appendChild(self.ref("lov", lov_uuid))
457 def mds(self, name, uuid, mdd_uuid, group=""):
458 mds = self.newService("mds", name, uuid)
459 mds.appendChild(self.ref("active",mdd_uuid))
461 self.addElement(mds, "group", group)
464 def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
465 mds_uuid, dev_size=0, journal_size=0, inode_size=256,
466 nspath="", mkfsoptions="", mountfsoptions="", quota="", group_upcall=""):
467 mdd = self.newService("mdsdev", name, uuid)
468 self.addElement(mdd, "fstype", fstype)
469 dev = self.addElement(mdd, "devpath", devname)
470 self.addElement(mdd, "autoformat", format)
472 self.addElement(mdd, "devsize", "%s" % (dev_size))
474 self.addElement(mdd, "journalsize", "%s" % (journal_size))
476 self.addElement(mdd, "inodesize", "%s" % (inode_size))
478 self.addElement(mdd, "nspath", nspath)
480 self.addElement(mdd, "mkfsoptions", mkfsoptions)
482 self.addElement(mdd, "mountfsoptions", mountfsoptions)
484 self.addElement(mdd, "quota", quota)
486 self.addElement(mdd, "group_upcall", group_upcall)
488 mdd.appendChild(self.ref("node", node_uuid))
489 mdd.appendChild(self.ref("target", mds_uuid))
492 def mountpoint(self, name, uuid, fs_uuid, path, clientoptions):
493 mtpt = self.newService("mountpoint", name, uuid)
494 mtpt.appendChild(self.ref("filesystem", fs_uuid))
495 self.addElement(mtpt, "path", path)
497 self.addElement(mtpt, "clientoptions", clientoptions)
500 def filesystem(self, name, uuid, mds_uuid, obd_uuid):
501 fs = self.newService("filesystem", name, uuid)
502 fs.appendChild(self.ref("mds", mds_uuid))
503 fs.appendChild(self.ref("obd", obd_uuid))
506 def echo_client(self, name, uuid, osc_uuid):
507 ec = self.newService("echoclient", name, uuid)
508 ec.appendChild(self.ref("obd", osc_uuid))
511 ############################################################
512 # Utilities to query a DOM tree
513 # Using this functions we can treat use config information
514 # directly as a database.
516 return n.getAttribute('name')
519 return node.getAttribute('uuid')
522 def findByName(lustre, name, tag = ""):
523 for n in lustre.childNodes:
524 if n.nodeType == n.ELEMENT_NODE:
525 if tag and n.nodeName != tag:
527 if getName(n) == name:
530 n = findByName(n, name)
535 def lookup(node, uuid):
536 for n in node.childNodes:
537 if n.nodeType == n.ELEMENT_NODE:
538 if getUUID(n) == uuid:
546 def name2uuid(lustre, name, tag="", fatal=1):
547 ret = findByName(lustre, name, tag)
550 error('name2uuid:', '"'+name+'"', tag, 'element not found.')
555 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
556 for n in lustre.childNodes:
557 if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
558 if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
562 # XXX: assumes only one network element per node. will fix this
563 # as soon as support for routers is added
564 def get_net_uuid(lustre, node_name):
565 """ get a network uuid for a node_name """
566 node = findByName(lustre, node_name, "node")
568 error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
569 net = node.getElementsByTagName('network')
571 return getUUID(net[0])
575 def lov_add_obd(gen, lov, osc_uuid):
576 lov.appendChild(gen.ref("obd", osc_uuid))
578 def ref_exists(profile, uuid):
579 elist = profile.childNodes
581 if e.nodeType == e.ELEMENT_NODE:
582 ref = e.getAttribute('uuidref')
587 # ensure that uuid is not already in the profile
588 # return true if uuid is added
589 def node_add_profile(gen, node, ref, uuid):
590 refname = "%s_ref" % "profile"
591 ret = node.getElementsByTagName(refname)
593 error('node has no profile ref:', node)
594 prof_uuid = ret[0].getAttribute('uuidref')
595 profile = lookup(node.parentNode, prof_uuid)
597 error("no profile found:", prof_uuid)
598 if ref_exists(profile, uuid):
600 profile.appendChild(gen.ref(ref, uuid))
603 def get_attr(dom_node, attr, default=""):
604 v = dom_node.getAttribute(attr)
609 ############################################################
621 def set_node_options(gen, node, options):
623 node.setAttribute('router', '1')
625 gen.addElement(node, "timeout", get_option(options, 'timeout'))
627 default_upcall = get_option(options, 'upcall')
630 if default_upcall or options.lustre_upcall:
631 if options.lustre_upcall:
632 gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
634 gen.addElement(node, 'lustreUpcall', default_upcall)
635 if options.group_upcall:
636 gen.addElement(node, 'groupUpcall', options.group_upcall)
637 if default_upcall or options.portals_upcall:
638 if options.portals_upcall:
639 gen.addElement(node, 'portalsUpcall', options.portals_upcall)
641 gen.addElement(node, 'portalsUpcall', default_upcall)
643 gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
644 if options.subsystem:
645 gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
648 def do_add_node(gen, lustre, options, node_name):
649 uuid = new_uuid(node_name)
650 prof_name = new_name("PROFILE_" + node_name)
651 prof_uuid = new_uuid(prof_name)
652 profile = gen.profile(prof_name, prof_uuid)
653 node = gen.node(node_name, uuid, prof_uuid)
654 lustre.appendChild(node)
655 lustre.appendChild(profile)
657 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
658 set_node_options(gen, node, options)
662 def add_node(gen, lustre, options):
663 """ create a node with a network config """
665 node_name = get_option(options, 'node')
666 ret = findByName(lustre, node_name, "node")
668 print "Node:", node_name, "exists."
670 do_add_node(gen, lustre, options, node_name)
673 def add_net(gen, lustre, options):
674 """ create a node with a network config """
676 node_name = get_option(options, 'node')
677 nid = get_option(options, 'nid')
678 cluster_id = get_option(options, 'cluster_id')
679 hostaddr = get_option(options, 'hostaddr')
680 net_type = get_option(options, 'nettype')
682 if net_type in ('lnet','tcp','openib','ra'):
683 port = get_option_int(options, 'port')
684 elif net_type in ('elan','gm','iib','vib','lo','ptl'):
687 print "Unknown net_type: ", net_type
690 real_net_type = net_type
691 if net_type == 'lnet' and string.find(nid,'@') > 0:
692 real_net_type = string.split(nid,'@')[1]
697 print "Skipping the remote host networking test."
698 elif (real_net_type == 'tcp') and (nid != '*'):
700 print "Testing network on", node_name
701 target = string.split(nid,'@')[0]
702 if target != '*' and target != '\\*':
703 out = runcmd("ping -c 1 -w 5 %s" %target)
705 print "Could not connect to %s, please check network." % node_name
707 ret = findByName(lustre, node_name, "node")
709 node = do_add_node(gen, lustre, options, node_name)
712 set_node_options(gen, node, options)
714 net_name = new_name('NET_'+ node_name +'_'+ net_type)
715 net_uuid = new_uuid(net_name)
716 node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
718 node_add_profile(gen, node, "network", net_uuid)
721 def add_route(gen, lustre, options):
722 """ create a node with a network config """
724 node_name = get_option(options, 'node')
725 gw_net_type = get_option(options, 'nettype')
726 gw = get_option(options, 'gw')
727 gw_cluster_id = get_option(options, 'gateway_cluster_id')
728 tgt_cluster_id = get_option(options, 'target_cluster_id')
729 lo = get_option(options, 'lo')
730 hi = get_option(options, 'hi')
734 node = findByName(lustre, node_name, "node")
736 error (node_name, " not found.")
738 rlist = node.getElementsByTagName('routetbl')
742 rtbl_name = new_name("RTBL_" + node_name)
743 rtbl_uuid = new_uuid(rtbl_name)
744 rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
745 node.appendChild(rtbl)
746 node_add_profile(gen, node, "routetbl", rtbl_uuid)
747 rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
751 def add_mds(gen, lustre, options):
752 node_name = get_option(options, 'node')
753 mds_name = get_option(options, 'mds')
754 mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
755 mdd_uuid = new_uuid(mdd_name)
757 mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
759 mds_uuid = get_option(options, 'mdsuuid')
761 if lookup(lustre, mds_uuid):
762 error("Duplicate MDS UUID:", mds_uuid)
764 mds_uuid = new_uuid(mds_name)
766 mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
767 lustre.appendChild(mds)
769 mds = lookup(lustre, mds_uuid)
771 mds.setAttribute('failover', "1")
773 mds.setAttribute('failover,',"0")
775 devname = get_option(options, 'dev')
776 size = get_option(options, 'size')
777 fstype = get_option(options, 'fstype')
778 journal_size = get_option(options, 'journal_size')
779 inode_size = get_option(options, 'inode_size')
780 nspath = get_option(options, 'nspath')
781 mkfsoptions = get_option(options, 'mkfsoptions')
782 mountfsoptions = get_option(options, 'mountfsoptions')
783 quota = get_option(options, 'quota')
784 group_upcall = get_option(options, 'group_upcall')
786 node_uuid = name2uuid(lustre, node_name, 'node')
788 node = findByName(lustre, node_name, "node")
789 node_add_profile(gen, node, "mdsdev", mdd_uuid)
790 net_uuid = get_net_uuid(lustre, node_name)
792 error("NODE: ", node_name, "not found")
794 mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
795 get_format_flag(options), node_uuid, mds_uuid,
796 size, journal_size, inode_size, nspath, mkfsoptions,
797 mountfsoptions, quota, group_upcall)
798 lustre.appendChild(mdd)
801 def add_ost(gen, lustre, options):
802 node_name = get_option(options, 'node')
803 lovname = get_option(options, 'lov')
804 osdtype = get_option(options, 'osdtype')
806 node_uuid = name2uuid(lustre, node_name, 'node')
808 if osdtype == 'obdecho':
819 devname = get_option(options, 'dev') # can be unset for bluearcs
820 size = get_option(options, 'size')
821 fstype = get_option(options, 'fstype')
822 journal_size = get_option(options, 'journal_size')
823 inode_size = get_option(options, 'inode_size')
824 mkfsoptions = get_option(options, 'mkfsoptions')
825 mountfsoptions = get_option(options, 'mountfsoptions')
826 quota = get_option(options, 'quota')
828 nspath = get_option(options, 'nspath')
830 ostname = get_option(options, 'ost')
832 ostname = new_name('OST_'+ node_name)
834 osdname = new_name("OSD_" + ostname + "_" + node_name)
835 osd_uuid = new_uuid(osdname)
837 ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
839 ost_uuid = get_option(options, 'ostuuid')
841 if lookup(lustre, ost_uuid):
842 error("Duplicate OST UUID:", ost_uuid)
844 ost_uuid = new_uuid(ostname)
846 ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
847 lustre.appendChild(ost)
849 lov = findByName(lustre, lovname, "lov")
851 error('add_ost:', '"'+lovname+'"', "lov element not found.")
852 lov_add_obd(gen, lov, ost_uuid)
854 ost = lookup(lustre, ost_uuid)
857 ost.setAttribute('failover', "1")
859 ost.setAttribute('failover', "0")
862 osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
863 get_format_flag(options), ost_uuid, node_uuid, size,
864 journal_size, inode_size, nspath, mkfsoptions,
865 mountfsoptions, quota)
867 node = findByName(lustre, node_name, "node")
869 ## if node_add_profile(gen, node, 'oss', oss_uuid):
871 ## oss_uuid = new_uuid(ossname)
872 ## oss = gen.oss(ossname, oss_uuid)
873 ## lustre.appendChild(oss)
875 node_add_profile(gen, node, 'osd', osd_uuid)
876 lustre.appendChild(osd)
879 def add_cobd(gen, lustre, options):
880 node_name = get_option(options, 'node')
881 name = new_name('COBD_' + node_name)
882 uuid = new_uuid(name)
884 real_name = get_option(options, 'real_obd')
885 cache_name = get_option(options, 'cache_obd')
887 real_uuid = name2uuid(lustre, real_name, tag='obd')
888 cache_uuid = name2uuid(lustre, cache_name, tag='obd')
890 node = findByName(lustre, node_name, "node")
891 node_add_profile(gen, node, "cobd", uuid)
892 cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
893 lustre.appendChild(cobd)
896 def add_echo_client(gen, lustre, options):
897 """ add an echo client to the profile for this node. """
898 node_name = get_option(options, 'node')
899 lov_name = get_option(options, 'ost')
901 node = findByName(lustre, node_name, 'node')
903 echoname = new_name('ECHO_'+ node_name)
904 echo_uuid = new_uuid(echoname)
905 node_add_profile(gen, node, 'echoclient', echo_uuid)
907 lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
909 lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
911 echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
912 lustre.appendChild(echo)
915 def add_lov(gen, lustre, options):
918 lov_orig = get_option(options, 'lov')
919 name = new_name(lov_orig)
921 warning("name:", lov_orig, "already used. using:", name)
923 mds_name = get_option(options, 'mds')
924 stripe_sz = get_option_int(options, 'stripe_sz')
925 stripe_cnt = get_option_int(options, 'stripe_cnt')
927 info("default stripe count (0) - will use %d stripe(s) per file" \
928 % DEFAULT_STRIPE_CNT)
929 pattern = get_option_int(options, 'stripe_pattern')
930 uuid = new_uuid(name)
932 ret = findByName(lustre, name, "lov")
934 error("LOV: ", name, " already exists.")
936 mds_uuid = name2uuid(lustre, mds_name, 'mds')
937 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
938 lustre.appendChild(lov)
940 # add an lovconfig entry to the active mdsdev profile
941 lovconfig_name = new_name('LVCFG_' + name)
942 lovconfig_uuid = new_uuid(lovconfig_name)
943 mds = findByName(lustre, mds_name, "mds")
944 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
945 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
946 lustre.appendChild(lovconfig)
948 def add_default_lov(gen, lustre, mds_name, lov_name):
949 """ create a default lov """
951 stripe_sz = DEFAULT_STRIPE_SZ
952 stripe_cnt = DEFAULT_STRIPE_CNT
953 pattern = DEFAULT_STRIPE_PATTERN
954 uuid = new_uuid(lov_name)
956 ret = findByName(lustre, lov_name, "lov")
958 error("LOV: ", lov_name, " already exists.")
960 mds_uuid = name2uuid(lustre, mds_name, 'mds')
961 lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
962 lustre.appendChild(lov)
964 # add an lovconfig entry to the active mdsdev profile
965 lovconfig_name = new_name('LVCFG_' + lov_name)
966 lovconfig_uuid = new_uuid(lovconfig_name)
967 mds = findByName(lustre, mds_name)
968 mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
969 lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
970 lustre.appendChild(lovconfig)
972 def new_filesystem(gen, lustre, mds_uuid, obd_uuid):
973 fs_name = new_name("FS_fsname")
974 fs_uuid = new_uuid(fs_name)
975 mds = lookup(lustre, mds_uuid)
976 mds.appendChild(gen.ref("filesystem", fs_uuid))
977 fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid)
978 lustre.appendChild(fs)
981 def get_fs_uuid(gen, lustre, mds_name, obd_name):
982 mds_uuid = name2uuid(lustre, mds_name, tag='mds')
983 obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
984 fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
986 fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid)
989 def add_mtpt(gen, lustre, options):
990 """ create mtpt on a node """
991 node_name = get_option(options, 'node')
993 path = get_option(options, 'path')
994 clientoptions = get_option(options, "clientoptions")
995 fs_name = get_option(options, 'filesystem')
997 lov_name = get_option(options, 'lov')
998 ost_name = get_option(options, 'ost')
999 mds_name = get_option(options, 'mds')
1002 error("--add mtpt requires --lov lov_name or --ost ost_name")
1004 warning("use default value for lov, due no --lov lov_name provided")
1005 lov_name = new_name("lov_default")
1006 add_default_lov(gen, lustre, mds_name, lov_name)
1007 ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1009 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1010 lov = findByName(lustre, lov_name, "lov")
1011 lov_add_obd(gen, lov, ost_uuid)
1014 fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name)
1016 fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1018 name = new_name('MNT_'+ node_name)
1020 ret = findByName(lustre, name, "mountpoint")
1022 # this can't happen, because new_name creates unique names
1023 error("MOUNTPOINT: ", name, " already exists.")
1025 uuid = new_uuid(name)
1026 mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions)
1027 node = findByName(lustre, node_name, "node")
1029 error('node:', node_name, "not found.")
1030 node_add_profile(gen, node, "mountpoint", uuid)
1031 lustre.appendChild(mtpt)
1033 ############################################################
1034 # Command line processing
1036 class OptionError (exceptions.Exception):
1037 def __init__(self, args):
1040 def get_option(options, tag):
1041 """Look for tag in options hash and return the value if set. If not
1042 set, then if return default it is set, otherwise exception."""
1043 if options.__getattr__(tag) != None:
1044 return options.__getattr__(tag)
1046 raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1048 def get_option_int(options, tag):
1049 """Return an integer option. Raise exception if the value is not an int"""
1050 val = get_option(options, tag)
1054 raise OptionError("--%s <num> (value must be integer)" % (tag))
1057 # simple class for profiling
1064 self._start = time.time()
1065 def stop(self, msg=''):
1066 self._stop = time.time()
1070 return self._stop - self._start
1071 def display(self, msg):
1073 str = '%s: %g secs' % (msg, d)
1076 #################################################################
1077 # function cmdlinesplit used to split cmd line from batch file
1079 def cmdlinesplit(cmdline):
1081 double_quote = re.compile(r'"(([^"\\]|\\.)*)"')
1082 single_quote = re.compile(r"'(.*?)'")
1083 escaped = re.compile(r'\\(.)')
1084 esc_quote = re.compile(r'\\([\\"])')
1085 outside = re.compile(r"""([^\s\\'"]+)""")
1089 while i < len(cmdline):
1092 match = double_quote.match(cmdline, i)
1094 print "Unmatched double quote:", cmdline
1097 if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1098 else: arg = arg + esc_quote.sub(r'\1', match.group(1))
1101 match = single_quote.match(cmdline, i)
1103 print "Unmatched single quote:", cmdline
1106 if arg is None: arg = match.group(1)
1107 else: arg = arg + match.group(1)
1110 match = escaped.match(cmdline, i)
1112 print "Unmatched backslash", cmdline
1115 if arg is None: arg = match.group(1)
1116 else: arg = arg + match.group(1)
1118 elif c in string.whitespace:
1120 arg_list.append(str(arg))
1122 while i < len(cmdline) and cmdline[i] in string.whitespace:
1125 match = outside.match(cmdline, i)
1128 if arg is None: arg = match.group()
1129 else: arg = arg + match.group()
1131 if arg != None: arg_list.append(str(arg))
1135 ############################################################
1139 def add(devtype, gen, lustre, options):
1140 if devtype == 'net':
1141 add_net(gen, lustre, options)
1142 elif devtype == 'mtpt':
1143 add_mtpt(gen, lustre, options)
1144 elif devtype == 'mds':
1145 add_mds(gen, lustre, options)
1146 elif devtype == 'ost':
1147 add_ost(gen, lustre, options)
1148 elif devtype == 'lov':
1149 add_lov(gen, lustre, options)
1150 elif devtype == 'route':
1151 add_route(gen, lustre, options)
1152 elif devtype == 'node':
1153 add_node(gen, lustre, options)
1154 elif devtype == 'echo_client':
1155 add_echo_client(gen, lustre, options)
1156 elif devtype == 'cobd':
1157 add_cobd(gen, lustre, options)
1159 error("unknown device type:", devtype)
1161 def do_command(gen, lustre, options, args):
1163 add(options.add, gen, lustre, options)
1165 error("Missing command")
1168 cl = Lustre.Options("lmc", "", lmc_options)
1170 options, args = cl.parse(sys.argv[1:])
1171 except Lustre.OptionError, e:
1175 panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1177 if options.reference:
1184 outFile = options.merge
1185 if os.access(outFile, os.R_OK):
1186 doc = xml.dom.minidom.parse(outFile)
1188 doc = new_lustre(xml.dom.minidom)
1190 doc = xml.dom.minidom.parse(options.input)
1192 doc = new_lustre(xml.dom.minidom)
1195 outFile = options.output
1197 lustre = doc.documentElement
1199 if lustre.tagName != "lustre":
1200 print "Existing config not valid."
1203 gen = GenConfig(doc)
1206 fp = open(options.batch)
1207 batchCommands = fp.readlines()
1209 for cmd in batchCommands:
1211 options, args = cl.parse(cmdlinesplit(cmd))
1212 if options.merge or options.input or options.output:
1213 print "The batchfile should not contain --merge, --input or --output."
1215 do_command(gen, lustre, options, args)
1216 except OptionError, e:
1218 except Lustre.OptionError, e:
1222 do_command(gen, lustre, options, args)
1223 except OptionError, e:
1224 panic(string.join(sys.argv),e)
1225 except Lustre.OptionError, e:
1229 timestr = string.split(str(time.time()), '.')
1230 gen.recordtime(timestr[0])
1235 printDoc(doc, open(outFile,"w"))
1237 if __name__ == "__main__":