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:
27 ./lmc --output config.xml --node server --net server1 tcp
28 ./lmc --merge config.xml --node client --net client1 tcp
32 ./lmc --merge config.xml --node server --mds mds1 /tmp/mds1 50000
35 ./lmc --merge config.xml --lov lov1 mds1 65536 0 0
36 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost1 100000
37 ./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost2 100000
39 # create client config
40 ./lmc --merge config.xml --node client --mtpt /mnt/lustre mds1 lov1
44 import sys, getopt, string
45 import xml.dom.minidom
46 from xml.dom.ext import PrettyPrint
47 from xml.xpath import Evaluate
49 DEFAULT_PORT = 888 # XXX What is the right default acceptor port to use?
52 print """usage: lmc [--node --ost | --mtpt | --lov] args
55 Node_name by itself it will create a new node. When used with other
56 commands it specifies the node to modify
58 --net hostname nettype [port, recv_buf, send_buf]
59 Nettype is either tcp, elan, or gm.
60 Requires a node argument
62 --lov lov_name [mdc_name stripe_sz stripe_off pattern]
63 Creates a logical volume
64 When used with other commands, it specifics the lov to modify
67 Create a MDS using the device
71 Configures a MDC for a node.
75 Creates an OBD/OST/OSC configuration triplet for a new device.
76 When used on "host", the device will be initialized and the OST
77 will be enabled. On client nodes, the OSC will be avaiable.
79 If --lov lov_name is used, this device is added to lov.
81 --mtpt /mnt/point mdc_name lov_name|osc_name
82 Creates a client mount point.
86 --merge="xml file" Add the new objects to an existing file
87 --format Format the partitions if unformated
88 --reformat Reformat partitions (this should be an lconf arg,
94 msg = string.join(map(str,args))
99 # manage names and uuids
100 # need to initialize this by walking tree to ensure
101 # no duplicate names or uuids are created.
102 # this are just place holders for now.
103 # consider changing this to be like OBD-dev-host
107 while names.has_key(ret):
108 ret = "%s_%d" % (base, ctr)
114 return "%s_UUID" % (name)
117 ldlm_uuid = 'ldlm_UUID'
119 """Create a new empty lustre document"""
120 # adding ldlm here is a bit of a hack, but one is enough.
121 str = """<lustre> <ldlm name="%s" uuid="%s"/> </lustre>""" % (ldlm_name, ldlm_uuid)
122 return dom.parseString(str)
126 def init_names(lustre):
127 """initialize auto-name generation tables"""
129 # get all elements that contain a name attribute
130 for n in Evaluate("//@name/..", lustre):
131 names[n.getAttribute("name")] = 1
132 uuids[n.getAttribute("uuid")] = 1
137 def __init__(self, doc):
140 def ref(self, type, uuid):
141 """ generate <[type]_ref uuidref="[uuid]"/> """
142 tag = "%s_ref" % (type)
143 ref = self.doc.createElement(tag)
144 ref.setAttribute("uuidref", uuid)
147 def newService(self, tag, name, uuid):
148 """ create a new service elmement, which requires name and uuid attributes """
149 new = self.doc.createElement(tag)
150 new.setAttribute("name", name);
151 new.setAttribute("uuid", uuid);
154 def addText(self, node, str):
155 txt = self.doc.createTextNode(str)
156 node.appendChild(txt)
158 def addElement(self, node, tag, str=None):
159 """ create a new element and add it as a child to node. If str is passed,
160 a text node is created for the new element"""
161 new = self.doc.createElement(tag)
163 self.addText(new, str)
164 node.appendChild(new)
167 def network(self, name, uuid, hostname, net, port=0):
168 """create <network> node"""
169 network = self.newService("network", name, uuid)
170 network.setAttribute("type", net);
171 self.addElement(network, "server", hostname)
173 self.addElement(network, "port", "%d" %(port))
176 def node(self, name, uuid):
177 """ create a host """
178 node = self.newService("node", name, uuid)
179 self.addElement(node, 'profile')
182 def ldlm(self, name, uuid):
183 """ create a ldlm """
184 ldlm = self.newService("ldlm", name, uuid)
187 def obd(self, name, uuid, fs, devname, format, dev_size=0):
188 obd = self.newService("obd", name, uuid)
189 obd.setAttribute('type', 'obdfilter')
190 self.addElement(obd, "fstype", fs)
191 dev = self.addElement(obd, "device", devname)
193 dev.setAttribute("size", "%s" % (dev_size))
194 self.addElement(obd, "autoformat", format)
197 def osc(self, name, uuid, obd_uuid, net_uuid):
198 osc = self.newService("osc", name, uuid)
199 osc.appendChild(self.ref("ost", net_uuid))
200 osc.appendChild(self.ref("obd", obd_uuid))
203 def ost(self, name, uuid, obd_uuid, net_uuid):
204 ost = self.newService("ost", name, uuid)
205 ost.appendChild(self.ref("network", net_uuid))
206 ost.appendChild(self.ref("obd", obd_uuid))
209 def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
210 lov = self.newService("lov", name, uuid)
211 lov.appendChild(self.ref("mds", mds_uuid))
212 devs = self.addElement(lov, "devices" )
213 devs.setAttribute("stripesize", stripe_sz)
214 devs.setAttribute("stripeoffset", stripe_off)
215 devs.setAttribute("pattern", pattern)
218 def mds(self, name, uuid, fs, devname, format, net_uuid, failover_uuid = "", dev_size=0 ):
219 mds = self.newService("mds", name, uuid)
220 self.addElement(mds, "fstype", fs)
221 dev = self.addElement(mds, "device", devname)
223 dev.setAttribute("size", "%s" % (dev_size))
224 self.addElement(mds, "autoformat", format)
225 mds.appendChild(self.ref("network", net_uuid))
227 mds.appendChild(self.ref("failover", failover_uuid))
230 def mdc(self, name, uuid, mds_uuid):
231 mdc = self.newService("mdc", name, uuid)
232 mdc.appendChild(self.ref("mds", mds_uuid))
235 def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
236 mtpt = self.newService("mountpoint", name, uuid)
237 mtpt.appendChild(self.ref("mdc", mdc_uuid))
238 mtpt.appendChild(self.ref("osc", osc_uuid))
239 self.addElement(mtpt, "path", path)
243 return n.getAttribute('name')
245 def findByName(lustre, name, tag = ""):
246 for n in lustre.childNodes:
247 if n.nodeType == n.ELEMENT_NODE:
248 if tag and n.nodeName != tag:
250 if getName(n) == name:
253 n = findByName(n, name)
258 def mds2node(lustre, mds_name):
259 """ Find the node a MDS is configured on """
260 msd = findByName(lustre, mds_name, 'mds')
261 ref = Evaluate('./network_ref', msd)
263 error("no network found for:", mds_name)
264 net_uuid = ref[0].getAttribute('uuidref')
265 path = '//node/network[@uuid="%s"]/..' % (net_uuid)
266 node = Evaluate(path, lustre)
268 error("no node found for :", mds_name)
271 # XXX: assumes only one network element per node. will fix this
272 # as soon as support for routers is added
273 def get_net_uuid(lustre, node_name):
274 """ get a network uuid for a node_name """
275 node = findByName(lustre, node_name, "node")
277 error ("node not found:", node_name)
278 net = Evaluate("./network", node)
280 return net[0].getAttribute("uuid")
283 def lov_add_osc(gen, lov, osc_uuid):
284 devs = lov.getElementsByTagName('devices')
286 devs[0].appendChild(gen.ref("osc", osc_uuid))
288 error("No devices element found for LOV:", lov)
290 def node_add_profile(gen, node, ref, uuid):
291 ret = node.getElementsByTagName('profile')
293 error('node has no profile:', node)
294 ret[0].appendChild(gen.ref(ref, uuid))
297 # Create a new obd, osc, and ost. Add them to the DOM.
299 def add_ost(gen, lustre, options, args):
303 if options.has_key('node'):
304 node_name = options['node']
306 error("--ost requires a --node argument")
308 if options.has_key('lov'):
309 lovname = options['lov']
319 obdname = new_name('OBD_'+ node_name)
320 oscname = new_name('OSC_'+ node_name)
321 ostname = new_name('OST_'+ node_name)
322 obd_uuid = get_uuid(obdname)
323 ost_uuid = get_uuid(ostname)
324 osc_uuid = get_uuid(oscname)
326 net_uuid = get_net_uuid(lustre, node_name)
328 error("NODE: ", node_name, "not found")
330 obd = gen.obd(obdname, obd_uuid, "extN", devname, "no", size)
331 ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
332 osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
335 lov = findByName(lustre, lovname, "lov")
337 error("LOV:", lovname, "not found.")
338 lov_add_osc(gen, lov, osc_uuid)
340 node = findByName(lustre, node_name, "node")
341 node_add_profile(gen, node, 'obd', obd_uuid)
342 node_add_profile(gen, node, 'ost', ost_uuid)
344 lustre.appendChild(obd)
345 lustre.appendChild(osc)
346 lustre.appendChild(ost)
348 def add_net(gen, lustre, options, args):
349 """ create a node with a network config """
353 node_name = options['node']
357 if net_type == 'tcp':
362 # add send, recv buffer size here
363 elif net_type in ('elan', 'gm'):
366 print "Unknown net_type: ", net_type
369 ret = findByName(lustre, node_name, "node")
371 node = do_add_node(gen, lustre, node_name)
374 net_name = new_name('NET_'+ node_name +'_'+ net_type)
375 net_uuid = get_uuid(net_name)
376 node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port))
377 node_add_profile(gen, node, "network", net_uuid)
379 def do_add_node(gen, lustre, node_name):
380 uuid = get_uuid(node_name)
381 node = gen.node(node_name, uuid)
382 node_add_profile(gen, node, 'ldlm', ldlm_uuid)
383 lustre.appendChild(node)
386 def add_node(gen, lustre, options, args):
387 """ create a node with a network config """
391 node_name = options['node']
393 ret = findByName(lustre, node_name, "node")
395 print "Node:", node_name, "exists."
397 do_add_node(gen, lustre, node_name)
400 def add_lov(gen, lustre, options, args):
405 name = options['lov']
410 uuid = get_uuid(name)
412 ret = findByName(lustre, name, "lov")
414 error("LOV: ", name, " already exists.")
416 ret = findByName(lustre, mds_name, "mds")
418 error(mds_name, "not found.")
419 mds_uuid = ret.getAttribute("uuid")
421 node = mds2node(lustre, mds_name)
422 node_add_profile(gen, node, "lov", uuid)
423 lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
424 lustre.appendChild(lov)
426 def add_mtpt(gen, lustre, options, args):
427 """ create mtpt on a node """
431 if options.has_key('node'):
432 node_name = options['node']
434 error("--mtpt requires a --node argument")
439 mdc_name = 'MDC_' + mds_name
441 name = new_name('MNT_'+ node_name)
443 ret = findByName(lustre, name, "mountpoint")
445 error("MOUNTPOINT: ", name, " already exists.")
447 ret = findByName(lustre, lov_name, "lov")
449 ret = findByName(lustre, lov_name, "osc")
451 error(lov_name, "not found.")
452 lov_uuid = ret.getAttribute("uuid")
454 ret = findByName(lustre, mdc_name, "mdc")
456 error("MDC: ", mdc_name, "not found.")
457 mdc_uuid = ret.getAttribute("uuid")
459 uuid = get_uuid(name)
460 mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
461 node = findByName(lustre, node_name, "node")
463 error('node:', node_name, "not found.")
464 node_add_profile(gen, node, "mountpoint", uuid)
465 node_add_profile(gen, node, "mdc", mdc_uuid)
466 lustre.appendChild(mtpt)
468 def add_mdc(gen, lustre, options, args):
469 """ create mtpt on a node """
473 if options.has_key('node'):
474 node_name = options['node']
476 error("--mdc requires a --node argument")
480 ret = findByName(lustre, mdc_name, "mdc")
482 error("MDC: ", mdc_name, "not found.")
483 mdc_uuid = ret.getAttribute("uuid")
485 node = findByName(lustre, node_name, "node")
487 error('node:', node_name, "not found.")
488 node_add_profile(gen, node, "mdc", mdc_uuid)
490 def add_mds(gen, lustre, options, args):
494 if options.has_key('node'):
495 node_name = options['node']
497 error("--mds requires a --node argument")
499 mds_name = new_name(options['mds'])
506 mdc_name = 'MDC_' + mds_name
507 mds_uuid = get_uuid(mds_name)
508 mdc_uuid = get_uuid(mdc_name)
510 node = findByName(lustre, node_name, "node")
512 error('node:', node_name, 'not found')
513 node_add_profile(gen, node, "mds", mds_uuid)
515 net_uuid = get_net_uuid(lustre, node_name)
517 error("NODE: ", node_name, "not found")
520 mds = gen.mds(mds_name, mds_uuid, "extN", devname, "no", net_uuid, dev_size=size)
521 mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
522 lustre.appendChild(mds)
523 lustre.appendChild(mdc)
527 # Command line processing
530 def parse_cmdline(argv):
531 short_opts = "ho:i:m:"
532 long_opts = ["ost", "mtpt", "lov=", "node=", "mds=", "net",
533 "mdc", "merge=", "format", "reformat", "output=",
539 opts, args = getopt.getopt(argv, short_opts, long_opts)
540 except getopt.GetoptError:
545 if o in ("-h", "--help"):
547 if o in ("-o", "--output"):
548 options['output'] = a
563 if o in ("-m", "--merge"):
566 options['format'] = 1
567 if o == "--reformat":
568 options['reformat'] = 1
569 if o in ("--in" , "-i"):
575 options, args = parse_cmdline(sys.argv[1:])
578 if options.has_key('merge'):
579 outFile = options['merge']
580 doc = xml.dom.minidom.parse(outFile)
581 elif options.has_key('in'):
582 doc = xml.dom.minidom.parse(options['in'])
584 doc = new_lustre(xml.dom.minidom)
586 if options.has_key('output'):
587 outFile = options['output']
589 lustre = doc.documentElement
591 lustre = doc.documentElement
592 if lustre.tagName != "lustre":
593 print "Existing config not valid."
597 if options.has_key('ost'):
598 add_ost(gen, lustre, options, args)
599 elif options.has_key('mtpt'):
600 add_mtpt(gen, lustre, options, args)
601 elif options.has_key('mds'):
602 add_mds(gen, lustre, options, args)
603 elif options.has_key('mdc'):
604 add_mdc(gen, lustre, options, args)
605 elif options.has_key('net'):
606 add_net(gen, lustre, options, args)
607 elif options.has_key('lov'):
608 add_lov(gen, lustre, options, args)
609 elif options.has_key('node'):
610 add_node(gen, lustre, options, args)
612 print "Missing command"
618 PrettyPrint(doc, open(outFile,"w"))
619 if __name__ == "__main__":