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