Whamcloud - gitweb
- fix return code in mds_getattr_internal
[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 = 888 # 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 --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
65
66 --mds device [size]
67    Create a MDS using the device
68    Requires --node 
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 mdc_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 """
91     sys.exit(1)
92
93 def error(*args):
94     msg = string.join(map(str,args))
95     print msg
96     sys.exit(1)
97     
98 #
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
104 def new_name(base):
105     ctr = 2
106     ret = base
107     while names.has_key(ret):
108         ret = "%s_%d" % (base, ctr)
109         ctr = 1 + ctr
110     names[ret] = 1
111     return ret
112
113 def get_uuid(name):
114     return "%s_UUID" % (name)
115
116 ldlm_name = 'ldlm'
117 ldlm_uuid = 'ldlm_UUID'
118 def new_lustre(dom):
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)
123
124 names = {}
125 uuids = {}
126
127 def init_names(doc):
128     """initialize auto-name generation tables"""
129     global names, uuids
130     # get all elements that contain a name attribute
131     for n in doc.childNodes:
132         if n.nodeType == n.ELEMENT_NODE:
133             if getName(n):
134                 names[getName(n)] = 1
135                 uuids[getUUID(n)] = 1
136             init_names(n)
137
138 def get_format_flag(options):
139     if options.has_key('format'):
140         if options['format']:
141             return 'yes'
142     return 'no'
143
144 class GenConfig:
145     doc = None
146     dom = None
147     def __init__(self, doc):
148         self.doc = doc
149
150     def ref(self, type, uuid):
151         """ generate <[type]_ref uuidref="[uuid]"/> """
152         tag = "%s_ref" % (type)
153         ref = self.doc.createElement(tag)
154         ref.setAttribute("uuidref", uuid)
155         return ref
156     
157     def newService(self, tag, name, uuid):
158         """ create a new  service elmement, which requires name and uuid attributes """
159         new = self.doc.createElement(tag)
160         new.setAttribute("name", name);
161         new.setAttribute("uuid", uuid);
162         return new
163     
164     def addText(self, node, str):
165         txt = self.doc.createTextNode(str)
166         node.appendChild(txt)
167
168     def addElement(self, node, tag, str=None):
169         """ create a new element and add it as a child to node. If str is passed,
170             a text node is created for the new element"""
171         new = self.doc.createElement(tag)
172         if str:
173             self.addText(new, str)
174         node.appendChild(new)
175         return new
176
177     def network(self, name, uuid, hostname, net, port=0):
178         """create <network> node"""
179         network = self.newService("network", name, uuid)
180         network.setAttribute("type", net);
181         self.addElement(network, "server", hostname)
182         if port:
183             self.addElement(network, "port", "%d" %(port))
184         return network
185
186     def node(self, name, uuid):
187         """ create a host """
188         node = self.newService("node", name, uuid)
189         self.addElement(node, 'profile')
190         return node
191
192     def ldlm(self, name, uuid):
193         """ create a ldlm """
194         ldlm = self.newService("ldlm", name, uuid)
195         return ldlm
196
197     def obd(self, name, uuid, fs, devname, format, dev_size=0):
198         obd = self.newService("obd", name, uuid)
199         obd.setAttribute('type', 'obdfilter')
200         self.addElement(obd, "fstype", fs)
201         dev = self.addElement(obd, "device", devname)
202         if (dev_size):
203             dev.setAttribute("size", "%s" % (dev_size))
204         self.addElement(obd, "autoformat", format)
205         return obd
206
207     def osc(self, name, uuid, obd_uuid, net_uuid):
208         osc = self.newService("osc", name, uuid)
209         osc.appendChild(self.ref("ost", net_uuid))
210         osc.appendChild(self.ref("obd", obd_uuid))
211         return osc
212
213     def ost(self, name, uuid, obd_uuid, net_uuid):
214         ost = self.newService("ost", name, uuid)
215         ost.appendChild(self.ref("network", net_uuid))
216         ost.appendChild(self.ref("obd", obd_uuid))
217         return ost
218
219     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern):
220         lov = self.newService("lov", name, uuid)
221         lov.appendChild(self.ref("mds", mds_uuid))
222         devs = self.addElement(lov, "devices" )
223         devs.setAttribute("stripesize", stripe_sz)
224         devs.setAttribute("stripeoffset", stripe_off)
225         devs.setAttribute("pattern", pattern)
226         return lov
227
228     def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid,
229             failover_uuid = "", dev_size=0 ):
230         mds = self.newService("mds", name, uuid)
231         self.addElement(mds, "fstype", fs)
232         dev = self.addElement(mds, "device", devname)
233         if dev_size:
234             dev.setAttribute("size", "%s" % (dev_size))
235         self.addElement(mds, "autoformat", format)
236         mds.appendChild(self.ref("network", net_uuid))
237         mds.appendChild(self.ref("node", node_uuid))
238         if failover_uuid:
239             mds.appendChild(self.ref("failover", failover_uuid))
240         return mds
241
242     def mdc(self, name, uuid, mds_uuid):
243         mdc = self.newService("mdc", name, uuid)
244         mdc.appendChild(self.ref("mds", mds_uuid))
245         return mdc
246
247     def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path):
248         mtpt = self.newService("mountpoint", name, uuid)
249         mtpt.appendChild(self.ref("mdc", mdc_uuid))
250         mtpt.appendChild(self.ref("osc", osc_uuid))
251         self.addElement(mtpt, "path", path)
252         return mtpt
253
254 def getName(n):
255     return n.getAttribute('name')
256
257 def getUUID(node):
258     return node.getAttribute('uuid')
259
260
261 def findByName(lustre, name, tag = ""):
262     for n in lustre.childNodes:
263         if n.nodeType == n.ELEMENT_NODE:
264             if tag and n.nodeName != tag:
265                 continue
266             if getName(n) == name:
267                 return n
268             else:
269                 n = findByName(n, name)
270                 if n: return n
271     return None
272
273 def lookup(node, uuid):
274     for n in node.childNodes:
275         if n.nodeType == n.ELEMENT_NODE:
276             if getUUID(n) == uuid:
277                 return n
278             else:
279                 n = lookup(n, uuid)
280                 if n: return n
281     return None
282             
283
284 def mds2node(lustre, mds_name):
285     """ Find the node a MDS is configured on """
286     mds = findByName(lustre, mds_name, 'mds')
287     ref = mds.getElementsByTagName('node_ref')
288     if not ref:
289         error("no node found for:", mds_name)
290     node_uuid = ref[0].getAttribute('uuidref')
291     node = lookup(lustre, node_uuid)
292     if not node:
293         error("no node found for :", mds_name)
294     return node
295
296 def name2uuid(lustre, name, tag="",  fatal=1):
297     ret = findByName(lustre, name, tag)
298     if not ret:
299         if fatal:
300             error('name2uuid:', name, "not found.")
301         else:
302             return ""
303     return getUUID(ret)
304     
305 # XXX: assumes only one network element per node. will fix this
306 # as soon as support for routers is added
307 def get_net_uuid(lustre, node_name):
308     """ get a network uuid for a node_name """
309     node = findByName(lustre, node_name, "node")
310     if not node:
311         error ("node not found:", node_name)
312     net = node.getElementsByTagName('network')
313     if net:
314         return getUUID(net[0])
315     return None
316
317 def lov_add_osc(gen, lov, osc_uuid):
318     devs = lov.getElementsByTagName('devices')
319     if len(devs) == 1:
320         devs[0].appendChild(gen.ref("osc", osc_uuid))
321     else:
322         error("No devices element found for LOV:", lov)
323                             
324 def node_add_profile(gen, node, ref, uuid):
325     ret = node.getElementsByTagName('profile')
326     if not ret:
327         error('node has no profile:', node)
328     ret[0].appendChild(gen.ref(ref, uuid))
329     
330 #
331 # Create a new obd, osc, and ost. Add them to the DOM.
332 #
333 def add_ost(gen, lustre, options, args):
334     if len(args) < 1:
335         usage()
336
337     if options.has_key('node'):
338         node_name = options['node']
339     else:
340         error("--ost requires a --node argument")
341
342     if options.has_key('lov'):
343         lovname = options['lov']
344     else:
345         lovname = ''
346
347     devname = args[0]
348     if len(args) > 1:
349         size = args[1]
350     else:
351         size = 0
352
353     obdname = new_name('OBD_'+ node_name)
354     oscname = new_name('OSC_'+ node_name)
355     ostname = new_name('OST_'+ node_name)
356     obd_uuid = get_uuid(obdname)
357     ost_uuid = get_uuid(ostname)
358     osc_uuid = get_uuid(oscname)
359
360     net_uuid = get_net_uuid(lustre, node_name)
361     if not net_uuid:
362         error("NODE: ", node_name, "not found")
363     
364     obd = gen.obd(obdname, obd_uuid,  "extN", devname, get_format_flag(options), size)
365     ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid)
366     osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid)
367     
368     if lovname:
369         lov = findByName(lustre, lovname, "lov")
370         if not lov:
371             error("LOV:", lovname, "not found.")
372         lov_add_osc(gen, lov, osc_uuid)
373
374     node = findByName(lustre, node_name, "node")
375     node_add_profile(gen, node, 'obd', obd_uuid)
376     node_add_profile(gen, node, 'ost', ost_uuid)
377
378     lustre.appendChild(obd)
379     lustre.appendChild(osc)
380     lustre.appendChild(ost)
381                    
382 def add_net(gen, lustre, options, args):
383     """ create a node with a network config """
384     if len(args) < 2:
385         usage()
386
387     node_name = options['node']
388     nid = args[0]
389     net_type = args[1]
390
391     if net_type == 'tcp':
392         if len(args) > 2:
393             port = int(args[2])
394         else:
395             port = DEFAULT_PORT
396         # add send, recv buffer size here
397     elif net_type in ('elan', 'gm'):
398         port = 0
399     else:
400         print "Unknown net_type: ", net_type
401         sys.exit(2)
402
403     ret = findByName(lustre, node_name, "node")
404     if not ret:
405         node = do_add_node(gen, lustre, node_name)
406     else:
407         node = ret
408     net_name = new_name('NET_'+ node_name +'_'+ net_type)
409     net_uuid = get_uuid(net_name)
410     node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port))
411     node_add_profile(gen, node, "network", net_uuid)
412
413 def do_add_node(gen, lustre,  node_name):
414     uuid = get_uuid(node_name)
415     node = gen.node(node_name, uuid)
416     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
417     lustre.appendChild(node)
418     return node
419     
420 def add_node(gen, lustre, options, args):
421     """ create a node with a network config """
422     if len(args) > 1:
423         usage()
424
425     node_name = options['node']
426
427     ret = findByName(lustre, node_name, "node")
428     if ret:
429         print "Node:", node_name, "exists."
430         return
431     do_add_node(gen, lustre, node_name)
432
433
434 def add_lov(gen, lustre, options, args):
435     """ create a lov """
436     if len(args) < 4:
437         usage()
438
439     name = options['lov']
440     mds_name = args[0]
441     stripe_sz = args[1]
442     stripe_off = args[2]
443     pattern = args[3]
444     uuid = get_uuid(name)
445
446     ret = findByName(lustre, name, "lov")
447     if ret:
448         error("LOV: ", name, " already exists.")
449
450     mds_uuid = name2uuid(lustre, mds_name)
451
452     node = mds2node(lustre, mds_name)
453     node_add_profile(gen, node, "lov", uuid)
454     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern)
455     lustre.appendChild(lov)
456
457 def add_mtpt(gen, lustre, options, args):
458     """ create mtpt on a node """
459     if len(args) < 3:
460         usage()
461
462     if options.has_key('node'):
463         node_name = options['node']
464     else:
465         error("--mtpt requires a --node argument")
466
467     path = args[0]
468     mds_name = args[1]
469     lov_name = args[2]
470     mdc_name = 'MDC_' + mds_name
471
472     name = new_name('MNT_'+ node_name)
473
474     ret = findByName(lustre, name, "mountpoint")
475     if ret:
476         error("MOUNTPOINT: ", name, " already exists.")
477
478     mdc_uuid = name2uuid(lustre, mdc_name)
479     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
480     if not lov_uuid:
481         lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1)
482
483     uuid = get_uuid(name)
484     mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
485     node = findByName(lustre, node_name, "node")
486     if not node:
487             error('node:',  node_name, "not found.")
488     node_add_profile(gen, node, "mountpoint", uuid)
489     node_add_profile(gen, node, "mdc", mdc_uuid)
490     lustre.appendChild(mtpt)
491
492 def add_mdc(gen, lustre, options, args):
493     """ create mtpt on a node """
494     if len(args) < 1:
495         usage()
496
497     if options.has_key('node'):
498         node_name = options['node']
499     else:
500         error("--mdc requires a --node argument")
501
502     mdc_name = args[0]
503     mdc_uuid = name2uuid(lustre, mdc_name)
504
505     node = findByName(lustre, node_name, "node")
506     if not node:
507             error('node:',  node_name, "not found.")
508     node_add_profile(gen, node, "mdc", mdc_uuid)
509
510 def add_mds(gen, lustre, options, args):
511     if len(args) < 1:
512         usage()
513
514     if options.has_key('node'):
515         node_name = options['node']
516     else:
517         error("--mds requires a --node argument")
518
519     mds_name = new_name(options['mds'])
520     devname = args[0]
521     if len(args) > 1:
522         size = args[1]
523     else:
524         size = 0
525
526     mdc_name = 'MDC_' + mds_name
527     mds_uuid = get_uuid(mds_name)
528     mdc_uuid = get_uuid(mdc_name)
529
530     node_uuid = name2uuid(lustre, node_name)
531
532     node = findByName(lustre, node_name, "node")
533     node_add_profile(gen, node, "mds", mds_uuid)
534     net_uuid = get_net_uuid(lustre, node_name)
535     if not net_uuid:
536         error("NODE: ", node_name, "not found")
537
538
539     mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options),
540                   net_uuid, node_uuid, dev_size=size)
541     mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid)
542     lustre.appendChild(mds)
543     lustre.appendChild(mdc)
544                    
545
546 #
547 # Command line processing
548 #
549
550 def parse_cmdline(argv):
551     short_opts = "ho:i:m:"
552     long_opts = ["ost", "mtpt", "lov=", "node=", "mds=", "net",
553                  "mdc", "merge=", "format", "reformat", "output=",
554                  "in=", "help"]
555     opts = []
556     args = []
557     options = {}
558     try:
559         opts, args = getopt.getopt(argv, short_opts, long_opts)
560     except getopt.GetoptError:
561         print "invalid opt"
562         usage()
563
564     for o, a in opts:
565         if o in ("-h", "--help"):
566             usage()
567         if o in ("-o", "--output"):
568             options['output'] = a
569         if o == "--ost":
570             options['ost'] = 1
571         if o == "--mds":
572             options['mds'] = a
573         if o == "--mdc":
574             options['mdc'] = 1
575         if o == "--net":
576             options['net'] = 1
577         if o == "--mtpt":
578             options['mtpt'] = 1
579         if o == "--node":
580             options['node'] = a
581         if o == "--lov":
582             options['lov'] = a
583         if o in ("-m", "--merge"):
584             options['merge'] = a
585         if o == "--format":
586             options['format'] = 1
587         if o  == "--reformat":
588             options['reformat'] = 1
589         if o  in ("--in" , "-i"):
590             options['in'] = a
591             
592     return options, args
593
594
595 import time
596 class chrono:
597     def __init__(self):
598         self._start = 0
599     def start(self):
600         self._stop = 0
601         self._start = time.time()
602     def stop(self, msg=''):
603         self._stop = time.time()
604         if msg:
605             self.display(msg)
606     def dur(self):
607         return self._stop - self._start
608     def display(self, msg):
609         d = self.dur()
610         str = '%s: %g secs' % (msg, d)
611         print str
612
613
614 def main():
615     options, args = parse_cmdline(sys.argv[1:])
616     outFile = '-'
617
618     if options.has_key('merge'):
619         outFile = options['merge']
620         doc = xml.dom.minidom.parse(outFile)
621     elif options.has_key('in'):
622         doc = xml.dom.minidom.parse(options['in'])
623     else:
624         doc = new_lustre(xml.dom.minidom)
625
626     if options.has_key('output'):
627         outFile = options['output']
628
629     lustre = doc.documentElement
630     init_names(lustre)
631     if lustre.tagName != "lustre":
632         print "Existing config not valid."
633         sys.exit(1)
634
635     gen = GenConfig(doc)
636     if options.has_key('ost'):
637         add_ost(gen, lustre, options, args)
638     elif options.has_key('mtpt'):
639         add_mtpt(gen, lustre, options, args)
640     elif options.has_key('mds'):
641         add_mds(gen, lustre, options, args)
642     elif options.has_key('mdc'):
643         add_mdc(gen, lustre, options, args)
644     elif options.has_key('net'):
645         add_net(gen, lustre, options, args)
646     elif options.has_key('lov'):
647         add_lov(gen, lustre, options, args)
648     elif options.has_key('node'):
649         add_node(gen, lustre, options, args)
650     else:
651         print "Missing command"
652         usage()
653
654     if outFile == '-':
655         PrettyPrint(doc)
656     else:
657         PrettyPrint(doc, open(outFile,"w"))
658
659 if __name__ == "__main__":
660     main()
661
662