Whamcloud - gitweb
- configure lov on mds
[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 from xml.xpath import Evaluate
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 def init_names(lustre):
127     """initialize auto-name generation tables"""
128     global names, uuids
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
133
134 class GenConfig:
135     doc = None
136     dom = None
137     def __init__(self, doc):
138         self.doc = doc
139
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)
145         return ref
146     
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);
152         return new
153     
154     def addText(self, node, str):
155         txt = self.doc.createTextNode(str)
156         node.appendChild(txt)
157
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)
162         if str:
163             self.addText(new, str)
164         node.appendChild(new)
165         return new
166
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)
172         if port:
173             self.addElement(network, "port", "%d" %(port))
174         return network
175
176     def node(self, name, uuid):
177         """ create a host """
178         node = self.newService("node", name, uuid)
179         self.addElement(node, 'profile')
180         return node
181
182     def ldlm(self, name, uuid):
183         """ create a ldlm """
184         ldlm = self.newService("ldlm", name, uuid)
185         return ldlm
186
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)
192         if (dev_size):
193             dev.setAttribute("size", "%s" % (dev_size))
194         self.addElement(obd, "autoformat", format)
195         return obd
196
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))
201         return osc
202
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))
207         return ost
208
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)
216         return lov
217
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)
222         if dev_size:
223             dev.setAttribute("size", "%s" % (dev_size))
224         self.addElement(mds, "autoformat", format)
225         mds.appendChild(self.ref("network", net_uuid))
226         if failover_uuid:
227             mds.appendChild(self.ref("failover", failover_uuid))
228         return mds
229
230     def mdc(self, name, uuid, mds_uuid):
231         mdc = self.newService("mdc", name, uuid)
232         mdc.appendChild(self.ref("mds", mds_uuid))
233         return mdc
234
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)
240         return mtpt
241
242 def getName(n):
243     return n.getAttribute('name')
244
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:
249                 continue
250             if getName(n) == name:
251                 return n
252             else:
253                 n = findByName(n, name)
254                 if n: return n
255     return None
256
257
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)
262     if not ref:
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)
267     if not node:
268         error("no node found for :", mds_name)
269     return node[0]
270     
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")
276     if not node:
277         error ("node not found:", node_name)
278     net = Evaluate("./network", node)
279     if net:
280         return net[0].getAttribute("uuid")
281     return None
282
283 def lov_add_osc(gen, lov, osc_uuid):
284     devs = lov.getElementsByTagName('devices')
285     if len(devs) == 1:
286         devs[0].appendChild(gen.ref("osc", osc_uuid))
287     else:
288         error("No devices element found for LOV:", lov)
289                             
290 def node_add_profile(gen, node, ref, uuid):
291     ret = node.getElementsByTagName('profile')
292     if not ret:
293         error('node has no profile:', node)
294     ret[0].appendChild(gen.ref(ref, uuid))
295     
296 #
297 # Create a new obd, osc, and ost. Add them to the DOM.
298 #
299 def add_ost(gen, lustre, options, args):
300     if len(args) < 1:
301         usage()
302
303     if options.has_key('node'):
304         node_name = options['node']
305     else:
306         error("--ost requires a --node argument")
307
308     if options.has_key('lov'):
309         lovname = options['lov']
310     else:
311         lovname = ''
312
313     devname = args[0]
314     if len(args) > 1:
315         size = args[1]
316     else:
317         size = 0
318
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)
325
326     net_uuid = get_net_uuid(lustre, node_name)
327     if not net_uuid:
328         error("NODE: ", node_name, "not found")
329     
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)
333     
334     if lovname:
335         lov = findByName(lustre, lovname, "lov")
336         if not lov:
337             error("LOV:", lovname, "not found.")
338         lov_add_osc(gen, lov, osc_uuid)
339
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)
343
344     lustre.appendChild(obd)
345     lustre.appendChild(osc)
346     lustre.appendChild(ost)
347                    
348 def add_net(gen, lustre, options, args):
349     """ create a node with a network config """
350     if len(args) < 2:
351         usage()
352
353     node_name = options['node']
354     nid = args[0]
355     net_type = args[1]
356
357     if net_type == 'tcp':
358         if len(args) > 2:
359             port = int(args[2])
360         else:
361             port = DEFAULT_PORT
362         # add send, recv buffer size here
363     elif net_type in ('elan', 'gm'):
364         port = 0
365     else:
366         print "Unknown net_type: ", net_type
367         sys.exit(2)
368
369     ret = findByName(lustre, node_name, "node")
370     if not ret:
371         node = do_add_node(gen, lustre, node_name)
372     else:
373         node = ret
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)
378
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)
384     return node
385     
386 def add_node(gen, lustre, options, args):
387     """ create a node with a network config """
388     if len(args) > 1:
389         usage()
390
391     node_name = options['node']
392
393     ret = findByName(lustre, node_name, "node")
394     if ret:
395         print "Node:", node_name, "exists."
396         return
397     do_add_node(gen, lustre, node_name)
398
399
400 def add_lov(gen, lustre, options, args):
401     """ create a lov """
402     if len(args) < 4:
403         usage()
404
405     name = options['lov']
406     mds_name = args[0]
407     stripe_sz = args[1]
408     stripe_off = args[2]
409     pattern = args[3]
410     uuid = get_uuid(name)
411
412     ret = findByName(lustre, name, "lov")
413     if ret:
414         error("LOV: ", name, " already exists.")
415
416     ret = findByName(lustre, mds_name, "mds")
417     if not ret:
418         error(mds_name, "not found.")
419     mds_uuid = ret.getAttribute("uuid")
420
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)
425
426 def add_mtpt(gen, lustre, options, args):
427     """ create mtpt on a node """
428     if len(args) < 3:
429         usage()
430
431     if options.has_key('node'):
432         node_name = options['node']
433     else:
434         error("--mtpt requires a --node argument")
435
436     path = args[0]
437     mds_name = args[1]
438     lov_name = args[2]
439     mdc_name = 'MDC_' + mds_name
440
441     name = new_name('MNT_'+ node_name)
442
443     ret = findByName(lustre, name, "mountpoint")
444     if ret:
445         error("MOUNTPOINT: ", name, " already exists.")
446
447     ret = findByName(lustre, lov_name, "lov")
448     if not ret:
449         ret = findByName(lustre, lov_name, "osc")
450         if not ret:
451             error(lov_name, "not found.")
452     lov_uuid = ret.getAttribute("uuid")
453
454     ret = findByName(lustre, mdc_name, "mdc")
455     if not ret:
456         error("MDC: ", mdc_name, "not found.")
457     mdc_uuid = ret.getAttribute("uuid")
458
459     uuid = get_uuid(name)
460     mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path)
461     node = findByName(lustre, node_name, "node")
462     if not 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)
467
468 def add_mdc(gen, lustre, options, args):
469     """ create mtpt on a node """
470     if len(args) < 1:
471         usage()
472
473     if options.has_key('node'):
474         node_name = options['node']
475     else:
476         error("--mdc requires a --node argument")
477
478     mdc_name = args[0]
479
480     ret = findByName(lustre, mdc_name, "mdc")
481     if not ret:
482         error("MDC: ", mdc_name, "not found.")
483     mdc_uuid = ret.getAttribute("uuid")
484
485     node = findByName(lustre, node_name, "node")
486     if not node:
487             error('node:',  node_name, "not found.")
488     node_add_profile(gen, node, "mdc", mdc_uuid)
489
490 def add_mds(gen, lustre, options, args):
491     if len(args) < 1:
492         usage()
493
494     if options.has_key('node'):
495         node_name = options['node']
496     else:
497         error("--mds requires a --node argument")
498
499     mds_name = new_name(options['mds'])
500     devname = args[0]
501     if len(args) > 1:
502         size = args[1]
503     else:
504         size = 0
505
506     mdc_name = 'MDC_' + mds_name
507     mds_uuid = get_uuid(mds_name)
508     mdc_uuid = get_uuid(mdc_name)
509
510     node = findByName(lustre, node_name, "node")
511     if not node:
512         error('node:', node_name, 'not found')
513     node_add_profile(gen, node, "mds", mds_uuid)
514
515     net_uuid = get_net_uuid(lustre, node_name)
516     if not net_uuid:
517         error("NODE: ", node_name, "not found")
518
519
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)
524                    
525
526 #
527 # Command line processing
528 #
529
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=",
534                  "in=", "help"]
535     opts = []
536     args = []
537     options = {}
538     try:
539         opts, args = getopt.getopt(argv, short_opts, long_opts)
540     except getopt.GetoptError:
541         print "invalid opt"
542         usage()
543
544     for o, a in opts:
545         if o in ("-h", "--help"):
546             usage()
547         if o in ("-o", "--output"):
548             options['output'] = a
549         if o == "--ost":
550             options['ost'] = 1
551         if o == "--mds":
552             options['mds'] = a
553         if o == "--mdc":
554             options['mdc'] = 1
555         if o == "--net":
556             options['net'] = 1
557         if o == "--mtpt":
558             options['mtpt'] = 1
559         if o == "--node":
560             options['node'] = a
561         if o == "--lov":
562             options['lov'] = a
563         if o in ("-m", "--merge"):
564             options['merge'] = a
565         if o == "--format":
566             options['format'] = 1
567         if o  == "--reformat":
568             options['reformat'] = 1
569         if o  in ("--in" , "-i"):
570             options['in'] = a
571             
572     return options, args
573
574 def main():
575     options, args = parse_cmdline(sys.argv[1:])
576     outFile = '-'
577
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'])
583     else:
584         doc = new_lustre(xml.dom.minidom)
585
586     if options.has_key('output'):
587         outFile = options['output']
588
589     lustre = doc.documentElement
590     init_names(lustre)
591     lustre = doc.documentElement
592     if lustre.tagName != "lustre":
593         print "Existing config not valid."
594         sys.exit(1)
595
596     gen = GenConfig(doc)
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)
611     else:
612         print "Missing command"
613         usage()
614
615     if outFile == '-':
616         PrettyPrint(doc)
617     else:
618         PrettyPrint(doc, open(outFile,"w"))
619 if __name__ == "__main__":
620     main()
621
622