3 # Copyright (C) 2002 Cluster File Systems, Inc.
4 # Author: Robert Read <rread@clusterfs.com>
6 # This file is part of Lustre, http://www.lustre.org.
8 # Lustre is free software; you can redistribute it and/or
9 # modify it under the terms of version 2 of the GNU General Public
10 # License as published by the Free Software Foundation.
12 # Lustre is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Lustre; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 lmc - lustre configurtion data manager
25 Basic plan for lmc usage:
26 lmc --output config.xml --node nodename nid nettype [port=2346]
27 lmc --merge config.xml --lov lovname stripsize stripeoffset
28 lmc --merge config.xml --ost /dev/name nodename lovname [size=9999]
29 lmc --merge config.xml --mtpt /mnt/lustre lovname nodename
32 import sys, getopt, string
33 import xml.dom.minidom
34 from xml.dom.ext import PrettyPrint
35 from xml.xpath import Evaluate
37 DEFAULT_PORT = 888 # XXX What is the right default acceptor port to use?
40 print """usage: lmc [--node --ost | --mtpt | --lov] args
42 --node node_name hostname nettype [port]
43 Node_name is used to refer to this node during the configure process.
44 Nettype is either tcp, elan, or gm.
46 --lov lov_name mdc_name stripe_sz stripe_off pattern
47 Creates a logical volume
49 --mds node_name device [size]
51 --ost device node_name lov_name [size]
52 Creates an OBD/OST/OSC configuration triplet for a new device.
53 When used on "host", the device will be initialized and the OST
54 will be enabled. On client nodes, the OSC will be avaiable.
55 If lov_name is used, this device is added to lov.
57 --mtpt node_name mds_name lov_name /mnt/point
58 Creates a client mount point.
61 --merge="xml file" Add the new objects to an existing file
62 --format Format the partitions if unformated
63 --reformat Reformat partitions (this should be an lconf arg,
69 msg = string.join(map(str,args))
74 # manage names and uuids
75 # need to initialize this by walking tree to ensure
76 # no duplicate names or uuids are created.
77 # this are just place holders for now.
78 # consider changing this to be like OBD-dev-host
82 while names.has_key(ret):
83 ret = "%s_%d" % (base, ctr)
89 return "%s_UUID" % (name)
92 """Create a new empty lustre document"""
93 str = """<lustre> </lustre>"""
94 return dom.parseString(str)
98 def init_names(lustre):
99 """initialize auto-name generation tables"""
101 # get all elements that contain a name attribute
102 for n in Evaluate("//@name/..", lustre):
103 names[n.getAttribute("name")] = 1
104 uuids[n.getAttribute("uuid")] = 1
109 def __init__(self, doc):
112 def ref(self, type, uuid):
113 """ generate <[type]_ref uuidref="[uuid]"/> """
114 tag = "%s_ref" % (type)
115 ref = self.doc.createElement(tag)
116 ref.setAttribute("uuidref", uuid)
119 def newService(self, tag, name, uuid):
120 """ create a new service elmement, which requires name and uuid attributes """
121 new = self.doc.createElement(tag)
122 new.setAttribute("name", name);
123 new.setAttribute("uuid", uuid);
126 def addText(self, node, str):
127 txt = self.doc.createTextNode(str)
128 node.appendChild(txt)
130 def addElement(self, node, tag, str=None):
131 """ create a new element and add it as a child to node. If str is passed,
132 a text node is created for the new element"""
133 new = self.doc.createElement(tag)
135 self.addText(new, str)
136 node.appendChild(new)
139 def network(self, name, uuid, hostname, net, port=0):
140 """create <network> node"""
141 network = self.newService("network", name, uuid)
142 network.setAttribute("type", net);
143 self.addElement(network, "server", hostname)
145 self.addElement(network, "port", "%d" %(port))
148 def node(self, name, uuid):
149 """ create a host """
150 node = self.newService("node", name, uuid)
153 def obd(self, name, uuid, fs, devname, format, dev_size=0):
154 obd = self.newService("obd", name, uuid)
155 obd.setAttribute('type', 'obdfilter')
156 self.addElement(obd, "fstype", fs)
157 dev = self.addElement(obd, "device", devname)
159 dev.setAttribute("size", "%s" % (dev_size))
160 self.addElement(obd, "autoformat", format)
163 def osc(self, name, uuid, obd_uuid, net_uuid):
164 osc = self.newService("osc", name, uuid)
165 osc.appendChild(self.ref("ost", net_uuid))
166 osc.appendChild(self.ref("obd", obd_uuid))
169 def ost(self, name, uuid, obd_uuid, net_uuid):
170 ost = self.newService("ost", name, uuid)
171 ost.appendChild(self.ref("network", net_uuid))
172 ost.appendChild(self.ref("obd", obd_uuid))
175 def lov(self, name, uuid, stripe_sz, stripe_off, pattern):
176 lov = self.newService("lov", name, uuid)
177 devs = self.addElement(lov, "devices" )
178 devs.setAttribute("stripesize", stripe_sz)
179 devs.setAttribute("stripeoffset", stripe_off)
180 devs.setAttribute("pattern", pattern)
183 def mds(self, name, uuid, fs, devname, format, net_uuid, failover_uuid = "", dev_size=0 ):
184 mds = self.newService("mds", name, uuid)
185 self.addElement(mds, "fstype", fs)
186 dev = self.addElement(mds, "device", devname)
188 dev.setAttribute("size", "%s" % (dev_size))
189 self.addElement(mds, "autoformat", format)
190 mds.appendChild(self.ref("network", net_uuid))
192 mds.appendChild(self.ref("failover", failover_uuid))
195 def mdc(self, name, uuid, mds_uuid):
196 mdc = self.newService("mdc", name, uuid)
197 mdc.appendChild(self.ref("mds", mds_uuid))
200 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
201 mtpt = self.newService("mtpt", name, uuid)
202 mtpt.appendChild(self.ref("mdc", mdc_uuid))
203 mtpt.appendChild(self.ref("osc", osc_uuid))
204 self.addElement(mtpt, "path", path)
207 def findByName(lustre, name, tag = "*"):
208 path = '//%s[@name="%s"]' % (tag, name)
209 ret = Evaluate(path, lustre)
214 # XXX: assumes only one network element per node. will fix this
215 # as soon as support for routers is added
216 def get_net_uuid(lustre, node_name):
217 """ get a network uuid for a node_name """
218 node = findByName(lustre, node_name, "node")
220 error ("node not found:", node_name)
221 net = Evaluate("./network", node)
223 return net[0].getAttribute("uuid")
226 def lov_add_osc(gen, lov, osc_uuid):
227 devs = Evaluate("devices", lov)
229 devs[0].appendChild(gen.ref("osc", osc_uuid))
231 error("No devices element found for LOV:", lov)
235 # Create a new obd, osc, and ost. Add them to the DOM.
237 def add_ost(gen, lustre, options, args):
249 obdname = new_name(node_name+"_" + "obd")
250 oscname = new_name(node_name+"_"+"osc")
251 ostname = new_name(node_name+"_"+"ost")
252 obd_uuid = get_uuid(obdname)
253 ost_uuid = get_uuid(ostname)
254 osc_uuid = get_uuid(oscname)
256 net_uuid = get_net_uuid(lustre, node_name)
258 error("NODE: ", node_name, "not found")
260 lov = findByName(lustre, lovname, "lov")
262 error("LOV:", lovname, "not found.")
264 obd = gen.obd(obdname, obd_uuid, "extN", devname, "no", size)
265 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
266 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
268 lov_add_osc(gen, lov, osc_uuid)
269 lustre.appendChild(obd)
270 lustre.appendChild(osc)
271 lustre.appendChild(ost)
273 def add_node(gen, lustre, options, args):
274 """ create a node with a network config """
288 elif nettype in ('elan', 'gm'):
291 print "Unknown nettype: ", nettype
294 uuid = get_uuid(name)
295 node = gen.node(name, uuid)
296 net_name = name+"_net"
297 net_uuid = get_uuid(net_name)
298 node.appendChild(gen.network(net_name, net_uuid, nid, nettype, port))
299 lustre.appendChild(node)
302 def add_lov(gen, lustre, options, args):
312 ret = findByName(lustre, name, "lov")
314 error("LOV: ", name, " already exists.")
316 uuid = get_uuid(name)
317 lov = gen.lov(name,uuid,stripe_sz, stripe_off, pattern)
318 lustre.appendChild(lov)
320 def add_mtpt(gen, lustre, options, args):
321 """ create mtpt on a node """
330 name = new_name(node_name + "_mtpt")
332 ret = findByName(lustre, name, "mountpoint")
334 error("MOUNTPOINT: ", name, " already exists.")
336 ret = findByName(lustre, lov_name, "lov")
338 error("LOV: ", lov_name, " not found.")
339 lov_uuid = ret.getAttribute("uuid")
341 ret = findByName(lustre, mdc_name, "mdc")
343 error("MDC: ", mdc_name, " not found.")
344 mdc_uuid = ret.getAttribute("uuid")
346 uuid = get_uuid(name)
347 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
348 lustre.appendChild(mtpt)
351 def add_mds(gen, lustre, options, args):
362 ret = findByName(lustre, node_name, "node")
364 mds_name = new_name(node_name+"_" + "mds")
365 mdc_name = new_name(node_name+"_"+"mdc")
366 mds_uuid = get_uuid(mds_name)
367 mdc_uuid = get_uuid(mdc_name)
369 net_uuid = get_net_uuid(lustre, node_name)
371 error("NODE: ", node_name, "not found")
374 mds = gen.mds(mds_name, mds_uuid, "extN", devname, "no", net_uuid)
375 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
376 lustre.appendChild(mds)
377 lustre.appendChild(mdc)
381 # Command line processing
384 def parse_cmdline(argv):
386 long_opts = ["ost", "mtpt", "lov", "node", "mds",
387 "merge=", "format", "reformat", "output=",
393 opts, args = getopt.getopt(argv, short_opts, long_opts)
394 except getopt.GetoptError:
399 if o in ("-h", "--help"):
401 if o in ("-o", "--output"):
402 options['output'] = a
416 options['format'] = 1
417 if o == "--reformat":
418 options['reformat'] = 1
419 if o in ("--in" , "-i"):
425 options, args = parse_cmdline(sys.argv[1:])
428 if options.has_key('merge'):
429 outFile = options['merge']
430 doc = xml.dom.minidom.parse(outFile)
431 elif options.has_key('in'):
432 doc = xml.dom.minidom.parse(options['in'])
434 doc = new_lustre(xml.dom.minidom)
436 if options.has_key('output'):
437 outFile = options['output']
439 lustre = doc.documentElement
441 lustre = doc.documentElement
442 if lustre.tagName != "lustre":
443 print "Existing config not valid."
447 if options.has_key('ost'):
448 add_ost(gen, lustre, options, args)
449 elif options.has_key('node'):
450 add_node(gen, lustre, options, args)
451 elif options.has_key('mtpt'):
452 add_mtpt(gen, lustre, options, args)
453 elif options.has_key('lov'):
454 add_lov(gen, lustre, options, args)
455 elif options.has_key('mds'):
456 add_mds(gen, lustre, options, args)
458 print "Missing command"
464 PrettyPrint(doc, open(outFile,"w"))
465 if __name__ == "__main__":