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