Whamcloud - gitweb
b49be8cb3fb98c714f439a97cfc81e68be0f2c73
[fs/lustre-release.git] / lustre / utils / lmc
1 #!/usr/bin/env python
2 #
3 #  Copyright (C) 2002 Cluster File Systems, Inc.
4 #   Author: Robert Read <rread@clusterfs.com>
5
6 #   This file is part of Lustre, http://www.lustre.org.
7 #
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.
11 #
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.
16 #
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.
20 #
21
22 """
23 lmc - lustre configurtion data  manager
24  
25  Basic plan for lmc usage:
26 # create nodes
27 ./lmc --output config.xml --node server --net server1 tcp 
28 ./lmc --merge config.xml  --node client --net client1 tcp
29
30
31 # configure server
32 ./lmc --merge config.xml  --node server --mds mds1 /tmp/mds1 50000
33
34 # create lov
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
38
39 # create client config
40 ./lmc --merge config.xml  --node client --mtpt /mnt/lustre mds1 lov1
41
42 """
43
44 import sys, getopt, string
45 import xml.dom.minidom
46 from xml.dom.ext import PrettyPrint
47
48
49 DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use?
50
51 def usage():
52     print """usage: lmc [--node --ost | --mtpt | --lov] args
53 Commands:
54 --node node_name 
55    Node_name by itself it will create a new node. When used with other
56      commands it specifies the node to modify
57
58 --net hostname nettype [port, recv_buf, send_buf]
59    Nettype is either tcp, elan, or gm.
60    Requires a node argument
61
62 --mds device [size]
63    Create a MDS using the device
64    Requires --node 
65
66 --lov lov_name [mdc_name stripe_sz stripe_off pattern]
67    Creates a logical volume
68    When used with other commands, it specifics the lov to modify
69
70 --mdc mdc_name
71    Configures a MDC for a node.
72    Requires --node 
73
74 --ost device [size]
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.
78    Requires --node
79    If --lov lov_name is used, this device is added to lov. 
80
81 --mtpt /mnt/point mds_name lov_name|osc_name 
82    Creates a client mount point.
83    Requires --node
84
85 Options:
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,
89                     I think)
90 --obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter.
91                     This is only useful for the --ost command.
92                     The device parameters are ignored for the obdecho type.
93 """
94     sys.exit(1)
95
96 def error(*args):
97     msg = string.join(map(str,args))
98     print msg
99     sys.exit(1)
100     
101 #
102 # manage names and uuids
103 # need to initialize this by walking tree to ensure
104 # no duplicate names or uuids are created.
105 # this are just place holders for now.
106 # consider changing this to be like OBD-dev-host
107 def new_name(base):
108     ctr = 2
109     ret = base
110     while names.has_key(ret):
111         ret = "%s_%d" % (base, ctr)
112         ctr = 1 + ctr
113     names[ret] = 1
114     return ret
115
116 def get_uuid(name):
117     return "%s_UUID" % (name)
118
119 ldlm_name = 'ldlm'
120 ldlm_uuid = 'ldlm_UUID'
121 def new_lustre(dom):
122     """Create a new empty lustre document"""
123     # adding ldlm here is a bit of a hack, but one is enough.
124     str = """<lustre> <ldlm name="%s" uuid="%s"/> </lustre>""" % (ldlm_name, ldlm_uuid)
125     return dom.parseString(str)
126
127 names = {}
128 uuids = {}
129
130 def init_names(doc):
131     """initialize auto-name generation tables"""
132     global names, uuids
133     # get all elements that contain a name attribute
134     for n in doc.childNodes:
135         if n.nodeType == n.ELEMENT_NODE:
136             if getName(n):
137                 names[getName(n)] = 1
138                 uuids[getUUID(n)] = 1
139             init_names(n)
140
141 def get_format_flag(options):
142     if options.has_key('format'):
143         if options['format']:
144             return 'yes'
145     return 'no'
146
147 class GenConfig:
148     doc = None
149     dom = None
150     def __init__(self, doc):
151         self.doc = doc
152
153     def ref(self, type, uuid):
154         """ generate <[type]_ref uuidref="[uuid]"/> """
155         tag = "%s_ref" % (type)
156         ref = self.doc.createElement(tag)
157         ref.setAttribute("uuidref", uuid)
158         return ref
159     
160     def newService(self, tag, name, uuid):
161         """ create a new  service elmement, which requires name and uuid attributes """
162         new = self.doc.createElement(tag)
163         new.setAttribute("name", name);
164         new.setAttribute("uuid", uuid);
165         return new
166     
167     def addText(self, node, str):
168         txt = self.doc.createTextNode(str)
169         node.appendChild(txt)
170
171     def addElement(self, node, tag, str=None):
172         """ create a new element and add it as a child to node. If str is passed,
173             a text node is created for the new element"""
174         new = self.doc.createElement(tag)
175         if str:
176             self.addText(new, str)
177         node.appendChild(new)
178         return new
179
180     def network(self, name, uuid, hostname, net, port=0):
181         """create <network> node"""
182         network = self.newService("network", name, uuid)
183         network.setAttribute("type", net);
184         self.addElement(network, "server", hostname)
185         if port:
186             self.addElement(network, "port", "%d" %(port))
187         return network
188
189     def node(self, name, uuid):
190         """ create a host """
191         node = self.newService("node", name, uuid)
192         self.addElement(node, 'profile')
193         return node
194
195     def ldlm(self, name, uuid):
196         """ create a ldlm """
197         ldlm = self.newService("ldlm", name, uuid)
198         return ldlm
199
200     def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0):
201         obd = self.newService("obd", name, uuid)
202         obd.setAttribute('type', obdtype)
203         if fs:
204             self.addElement(obd, "fstype", fs)
205         if devname:
206             dev = self.addElement(obd, "device", devname)
207             if (dev_size):
208                 dev.setAttribute("size", "%s" % (dev_size))
209             self.addElement(obd, "autoformat", format)
210         return obd
211
212     def osc(self, name, uuid, obd_uuid, net_uuid):
213         osc = self.newService("osc", name, uuid)
214         osc.appendChild(self.ref("ost", net_uuid))
215         osc.appendChild(self.ref("obd", obd_uuid))
216         return osc
217
218     def ost(self, name, uuid, obd_uuid, net_uuid):
219         ost = self.newService("ost", name, uuid)
220         ost.appendChild(self.ref("network", net_uuid))
221         ost.appendChild(self.ref("obd", obd_uuid))
222         return ost
223
224     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
225         lov = self.newService("lov", name, uuid)
226         lov.appendChild(self.ref("mds", mds_uuid))
227         devs = self.addElement(lov, "devices" )
228         devs.setAttribute("stripesize", stripe_sz)
229         devs.setAttribute("stripeoffset", stripe_off)
230         devs.setAttribute("pattern", pattern)
231         return lov
232
233     def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
234             failover_uuid = "", dev_size=0 ):
235         mds = self.newService("mds", name, uuid)
236         self.addElement(mds, "fstype", fs)
237         dev = self.addElement(mds, "device", devname)
238         if dev_size:
239             dev.setAttribute("size", "%s" % (dev_size))
240         self.addElement(mds, "autoformat", format)
241         mds.appendChild(self.ref("network", net_uuid))
242         mds.appendChild(self.ref("node", node_uuid))
243         if failover_uuid:
244             mds.appendChild(self.ref("failover", failover_uuid))
245         return mds
246
247     def mdc(self, name, uuid, mds_uuid):
248         mdc = self.newService("mdc", name, uuid)
249         mdc.appendChild(self.ref("mds", mds_uuid))
250         return mdc
251
252     def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
253         mtpt = self.newService("mountpoint", name, uuid)
254         mtpt.appendChild(self.ref("mdc", mdc_uuid))
255         mtpt.appendChild(self.ref("osc", osc_uuid))
256         self.addElement(mtpt, "path", path)
257         return mtpt
258
259 def getName(n):
260     return n.getAttribute('name')
261
262 def getUUID(node):
263     return node.getAttribute('uuid')
264
265
266 def findByName(lustre, name, tag = ""):
267     for n in lustre.childNodes:
268         if n.nodeType == n.ELEMENT_NODE:
269             if tag and n.nodeName != tag:
270                 continue
271             if getName(n) == name:
272                 return n
273             else:
274                 n = findByName(n, name)
275                 if n: return n
276     return None
277
278 def lookup(node, uuid):
279     for n in node.childNodes:
280         if n.nodeType == n.ELEMENT_NODE:
281             if getUUID(n) == uuid:
282                 return n
283             else:
284                 n = lookup(n, uuid)
285                 if n: return n
286     return None
287             
288
289 def mds2node(lustre, mds_name):
290     """ Find the node a MDS is configured on """
291     mds = findByName(lustre, mds_name, 'mds')
292     ref = mds.getElementsByTagName('node_ref')
293     if not ref:
294         error("no node found for:", mds_name)
295     node_uuid = ref[0].getAttribute('uuidref')
296     node = lookup(lustre, node_uuid)
297     if not node:
298         error("no node found for :", mds_name)
299     return node
300
301 def name2uuid(lustre, name, tag="",  fatal=1):
302     ret = findByName(lustre, name, tag)
303     if not ret:
304         if fatal:
305             error('name2uuid:', name, "not found.")
306         else:
307             return ""
308     return getUUID(ret)
309     
310 # XXX: assumes only one network element per node. will fix this
311 # as soon as support for routers is added
312 def get_net_uuid(lustre, node_name):
313     """ get a network uuid for a node_name """
314     node = findByName(lustre, node_name, "node")
315     if not node:
316         error ("node not found:", node_name)
317     net = node.getElementsByTagName('network')
318     if net:
319         return getUUID(net[0])
320     return None
321
322 def lov_add_osc(gen, lov, osc_uuid):
323     devs = lov.getElementsByTagName('devices')
324     if len(devs) == 1:
325         devs[0].appendChild(gen.ref("osc", osc_uuid))
326     else:
327         error("No devices element found for LOV:", lov)
328                             
329 def node_add_profile(gen, node, ref, uuid):
330     ret = node.getElementsByTagName('profile')
331     if not ret:
332         error('node has no profile:', node)
333     ret[0].appendChild(gen.ref(ref, uuid))
334     
335 #
336 # Create a new obd, osc, and ost. Add them to the DOM.
337 #
338 def add_ost(gen, lustre, options, args):
339     lovname = ''
340     obdtype = 'obdfilter'
341     devname = ''
342     size = 0
343     fstype = 'extN'
344     
345     if options.has_key('node'):
346         node_name = options['node']
347     else:
348         error("--ost requires a --node argument")
349
350     if options.has_key('lov'):
351         lovname = options['lov']
352
353     if options.has_key('obdtype'):
354         obdtype = options['obdtype']
355
356     if obdtype == 'obdecho':
357         fstype = ''
358     else:
359         if len(args) < 1:
360             usage()
361         devname = args[0]
362         if len(args) > 1:
363             size = args[1]
364         
365     obdname = new_name('OBD_'+ node_name)
366     oscname = new_name('OSC_'+ node_name)
367     ostname = new_name('OST_'+ node_name)
368     obd_uuid = get_uuid(obdname)
369     ost_uuid = get_uuid(ostname)
370     osc_uuid = get_uuid(oscname)
371
372     net_uuid = get_net_uuid(lustre, node_name)
373     if not net_uuid:
374         error("NODE: ", node_name, "not found")
375     
376     obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size)
377     ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
378     osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
379     
380     if lovname:
381         lov = findByName(lustre, lovname, "lov")
382         if not lov:
383             error("LOV:", lovname, "not found.")
384         lov_add_osc(gen, lov, osc_uuid)
385
386     node = findByName(lustre, node_name, "node")
387     node_add_profile(gen, node, 'obd', obd_uuid)
388     node_add_profile(gen, node, 'ost', ost_uuid)
389
390     lustre.appendChild(obd)
391     lustre.appendChild(osc)
392     lustre.appendChild(ost)
393                    
394 # this is generally only used by llecho.sh
395 def add_osc(gen, lustre, options, args):
396     """ add the osc to the profile for this node. """
397     if len(args) < 1:
398         usage()
399     osc_name = args[0]
400     if options.has_key('node'):
401         node_name = options['node']
402     else:
403         error("--osc requires a --node argument")
404     osc_uuid = name2uuid(lustre, osc_name)
405     node = findByName(lustre, node_name, "node")
406     node_add_profile(gen, node, 'osc', osc_uuid)
407
408 def add_net(gen, lustre, options, args):
409     """ create a node with a network config """
410     if len(args) < 2:
411         usage()
412
413     node_name = options['node']
414     nid = args[0]
415     net_type = args[1]
416
417     if net_type == 'tcp':
418         if len(args) > 2:
419             port = int(args[2])
420         else:
421             port = DEFAULT_PORT
422         # add send, recv buffer size here
423     elif net_type in ('elan', 'gm'):
424         port = 0
425     else:
426         print "Unknown net_type: ", net_type
427         sys.exit(2)
428
429     ret = findByName(lustre, node_name, "node")
430     if not ret:
431         node = do_add_node(gen, lustre, node_name)
432     else:
433         node = ret
434     net_name = new_name('NET_'+ node_name +'_'+ net_type)
435     net_uuid = get_uuid(net_name)
436     node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port))
437     node_add_profile(gen, node, "network", net_uuid)
438
439 def do_add_node(gen, lustre,  node_name):
440     uuid = get_uuid(node_name)
441     node = gen.node(node_name, uuid)
442     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
443     lustre.appendChild(node)
444     return node
445     
446 def add_node(gen, lustre, options, args):
447     """ create a node with a network config """
448     if len(args) > 1:
449         usage()
450
451     node_name = options['node']
452
453     ret = findByName(lustre, node_name, "node")
454     if ret:
455         print "Node:", node_name, "exists."
456         return
457     do_add_node(gen, lustre, node_name)
458
459
460 def add_lov(gen, lustre, options, args):
461     """ create a lov """
462     if len(args) < 4:
463         usage()
464
465     name = options['lov']
466     mds_name = args[0]
467     stripe_sz = args[1]
468     stripe_off = args[2]
469     pattern = args[3]
470     uuid = get_uuid(name)
471
472     ret = findByName(lustre, name, "lov")
473     if ret:
474         error("LOV: ", name, " already exists.")
475
476     mds_uuid = name2uuid(lustre, mds_name)
477
478     node = mds2node(lustre, mds_name)
479     node_add_profile(gen, node, "lov", uuid)
480     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
481     lustre.appendChild(lov)
482
483 def add_mtpt(gen, lustre, options, args):
484     """ create mtpt on a node """
485     if len(args) < 3:
486         usage()
487
488     if options.has_key('node'):
489         node_name = options['node']
490     else:
491         error("--mtpt requires a --node argument")
492
493     path = args[0]
494     mds_name = args[1]
495     lov_name = args[2]
496     mdc_name = 'MDC_' + mds_name
497
498     name = new_name('MNT_'+ node_name)
499
500     ret = findByName(lustre, name, "mountpoint")
501     if ret:
502         error("MOUNTPOINT: ", name, " already exists.")
503
504     mdc_uuid = name2uuid(lustre, mdc_name)
505     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
506     if not lov_uuid:
507         lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
508
509     uuid = get_uuid(name)
510     mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
511     node = findByName(lustre, node_name, "node")
512     if not node:
513             error('node:',  node_name, "not found.")
514     node_add_profile(gen, node, "mountpoint", uuid)
515     node_add_profile(gen, node, "mdc", mdc_uuid)
516     lustre.appendChild(mtpt)
517
518 def add_mdc(gen, lustre, options, args):
519     """ create mtpt on a node """
520     if len(args) < 1:
521         usage()
522
523     if options.has_key('node'):
524         node_name = options['node']
525     else:
526         error("--mdc requires a --node argument")
527
528     mdc_name = args[0]
529     mdc_uuid = name2uuid(lustre, mdc_name)
530
531     node = findByName(lustre, node_name, "node")
532     if not node:
533             error('node:',  node_name, "not found.")
534     node_add_profile(gen, node, "mdc", mdc_uuid)
535
536 def add_mds(gen, lustre, options, args):
537     if len(args) < 1:
538         usage()
539
540     if options.has_key('node'):
541         node_name = options['node']
542     else:
543         error("--mds requires a --node argument")
544
545     mds_name = new_name(options['mds'])
546     devname = args[0]
547     if len(args) > 1:
548         size = args[1]
549     else:
550         size = 0
551
552     mdc_name = 'MDC_' + mds_name
553     mds_uuid = get_uuid(mds_name)
554     mdc_uuid = get_uuid(mdc_name)
555
556     node_uuid = name2uuid(lustre, node_name)
557
558     node = findByName(lustre, node_name, "node")
559     node_add_profile(gen, node, "mds", mds_uuid)
560     net_uuid = get_net_uuid(lustre, node_name)
561     if not net_uuid:
562         error("NODE: ", node_name, "not found")
563
564
565     mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
566                   net_uuid, node_uuid, dev_size=size)
567     mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
568     lustre.appendChild(mds)
569     lustre.appendChild(mdc)
570                    
571
572 #
573 # Command line processing
574 #
575
576 def parse_cmdline(argv):
577     short_opts = "ho:i:m:"
578     long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net",
579                  "mdc", "merge=", "format", "reformat", "output=",
580                  "obdtype=", "in=", "help"]
581     opts = []
582     args = []
583     options = {}
584     try:
585         opts, args = getopt.getopt(argv, short_opts, long_opts)
586     except getopt.error:
587         print "invalid opt"
588         usage()
589
590     for o, a in opts:
591         if o in ("-h", "--help"):
592             usage()
593         if o in ("-o", "--output"):
594             options['output'] = a
595         if o == "--ost":
596             options['ost'] = 1
597         if o == "--osc":
598             options['osc'] = 1
599         if o == "--mds":
600             options['mds'] = a
601         if o == "--mdc":
602             options['mdc'] = 1
603         if o == "--net":
604             options['net'] = 1
605         if o == "--mtpt":
606             options['mtpt'] = 1
607         if o == "--node":
608             options['node'] = a
609         if o == "--lov":
610             options['lov'] = a
611         if o in ("-m", "--merge"):
612             options['merge'] = a
613         if o == "--obdtype":
614             options['obdtype'] = a
615         if o == "--format":
616             options['format'] = 1
617         if o  == "--reformat":
618             options['reformat'] = 1
619         if o  in ("--in" , "-i"):
620             options['in'] = a
621             
622     return options, args
623
624
625 import time
626 class chrono:
627     def __init__(self):
628         self._start = 0
629     def start(self):
630         self._stop = 0
631         self._start = time.time()
632     def stop(self, msg=''):
633         self._stop = time.time()
634         if msg:
635             self.display(msg)
636     def dur(self):
637         return self._stop - self._start
638     def display(self, msg):
639         d = self.dur()
640         str = '%s: %g secs' % (msg, d)
641         print str
642
643
644 def main():
645     options, args = parse_cmdline(sys.argv[1:])
646     outFile = '-'
647
648     if options.has_key('merge'):
649         outFile = options['merge']
650         doc = xml.dom.minidom.parse(outFile)
651     elif options.has_key('in'):
652         doc = xml.dom.minidom.parse(options['in'])
653     else:
654         doc = new_lustre(xml.dom.minidom)
655
656     if options.has_key('output'):
657         outFile = options['output']
658
659     lustre = doc.documentElement
660     init_names(lustre)
661     if lustre.tagName != "lustre":
662         print "Existing config not valid."
663         sys.exit(1)
664
665     gen = GenConfig(doc)
666     if options.has_key('ost'):
667         add_ost(gen, lustre, options, args)
668     elif options.has_key('osc'):
669         add_osc(gen, lustre, options, args)
670     elif options.has_key('mtpt'):
671         add_mtpt(gen, lustre, options, args)
672     elif options.has_key('mds'):
673         add_mds(gen, lustre, options, args)
674     elif options.has_key('mdc'):
675         add_mdc(gen, lustre, options, args)
676     elif options.has_key('net'):
677         add_net(gen, lustre, options, args)
678     elif options.has_key('lov'):
679         add_lov(gen, lustre, options, args)
680     elif options.has_key('node'):
681         add_node(gen, lustre, options, args)
682     else:
683         print "Missing command"
684         usage()
685
686     if outFile == '-':
687         PrettyPrint(doc)
688     else:
689         PrettyPrint(doc, open(outFile,"w"))
690
691 if __name__ == "__main__":
692     main()
693
694