Whamcloud - gitweb
land 1.0.1 fixes on main development branch (head)
[fs/lustre-release.git] / lustre / utils / lmc
1 #!/usr/bin/env python
2 # Copyright (C) 2002 Cluster File Systems, Inc.
3 # Author: Robert Read <rread@clusterfs.com>
4
5 #   This file is part of Lustre, http://www.lustre.org.
6 #
7 #   Lustre is free software; you can redistribute it and/or
8 #   modify it under the terms of version 2 of the GNU General Public
9 #   License as published by the Free Software Foundation.
10 #
11 #   Lustre is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with Lustre; if not, write to the Free Software
18 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 #
20
21 """
22 lmc - lustre configuration data manager
23
24   See Lustre book (http://www.lustre.org/docs/lustre.pdf) for documentation on lmc.
25
26 """
27
28 import sys, os, getopt, string, exceptions
29 import xml.dom.minidom
30 from xml.dom.ext import PrettyPrint
31
32 PYMOD_DIR = "/usr/lib/lustre/python"
33
34 def development_mode():
35     base = os.path.dirname(sys.argv[0])
36     if os.access(base+"/Makefile.am", os.R_OK):
37         return 1
38     return 0
39
40 if not development_mode():
41     sys.path.append(PYMOD_DIR)
42
43 import Lustre
44
45 DEFAULT_PORT = 988 
46
47 def reference():
48     print """usage: lmc --add object [object parameters]
49
50 Object creation command summary:
51
52 --add node
53   --node node_name
54   --timeout num
55   --upcall path
56   --lustre_upcall path
57   --portals_upcall path
58
59 --add net
60   --node node_name
61   --nid nid
62   --cluster_id 
63   --nettype tcp|elan|toe|gm|scimac
64   --hostaddr addr
65   --port port
66   --tcpbuf size
67   --irq_affinity 0|1
68   --router
69
70 --add mds
71   --node node_name
72   --mds mds_name
73   --dev path
74   --fstype extN|ext3
75   --size size
76   --nspath
77   --journal_size size
78
79 --add lov
80   --lov lov_name
81   --mds mds_name
82   --stripe_sz num
83   --stripe_cnt num
84   --stripe_pattern num
85
86 --add ost
87   --node node_name
88   --ost ost_name 
89   --lov lov_name 
90   --dev path
91   --size size
92   --fstype extN|ext3
93   --journal_size size
94   --obdtype obdecho|obdfilter
95   --ostuuid uuid
96  
97 --add mtpt  - Mountpoint
98   --node node_name
99   --path /mnt/point
100   --mds mds_name
101   --ost ost_name OR --lov lov_name
102
103 --add route
104   --node nodename
105   --gw nid
106   --tgt nid
107   --lo nid
108   --hi nid
109
110 --add echo_client
111   --node nodename
112
113 --add mgmt  - Management/monitoring service
114   --node node_name
115   --mgmt mgmt_service_name
116 """
117
118 PARAM = Lustre.Options.PARAM
119 lmc_options = [
120     # lmc input/output options
121     ('reference', "Print short reference for commands."), 
122     ('verbose,v', "Print system commands as they are run."),
123     ('merge,m', "Append to the specified config file.", PARAM),
124     ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
125     ('input,i', "", PARAM),
126     ('batch', "Used to execute lmc commands in batch mode.", PARAM),
127
128     # commands
129     ('add', "", PARAM),
130     
131     # node options
132     ('node', "Add a new node in the cluster configuration.", PARAM),
133     ('timeout', "Set timeout to initiate recovery.", PARAM),
134     ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
135     ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
136     ('portals_upcall', "Set location of portals upcall script.", PARAM),
137
138     # network 
139     ('nettype', "Specify the network type. This can be tcp/elan/gm/scimac.", PARAM),
140     ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
141     ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"),
142     ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
143     ('irq_affinity', "Optional argument.", PARAM, 0),
144     ('hostaddr', "", PARAM,""),
145     ('cluster_id', "Specify the cluster ID", PARAM, "0"),
146
147     # routes
148     ('route', "Add a new route for the cluster.", PARAM),
149     ('router', "Optional flag to mark a node as router."),
150     ('gw', "Specify the nid of the gateway for a route.", PARAM),
151     ('gateway_cluster_id', "", PARAM, "0"),
152     ('target_cluster_id', "", PARAM, "0"),
153     ('lo', "For a range route, this is the low value nid.", PARAM),
154     ('hi', "For a range route, this is a hi value nid.", PARAM,""),
155
156     # servers: mds and ost
157     ('mds', "Specify MDS name.", PARAM),
158     ('ost', "Specify the OST name.", PARAM,""),
159     ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
160     ('failover', "Enable failover support on OSTs or MDS?"),
161     ('group', "", PARAM),
162     ('dev', "Path of the device on local system.", PARAM,""),
163     ('size', "Specify the size of the device if needed.", PARAM,"0"),
164     ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"),
165     ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
166     ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
167     ('ostuuid', "", PARAM,""),
168     ('nspath', "Local mount point of server namespace.", PARAM,""),
169     ('format', ""),
170
171     # clients: mountpoint and echo
172     ('echo_client', "", PARAM),
173     ('path', "Specify the mountpoint for Lustre.", PARAM),
174     ('filesystem', "Lustre filesystem name", PARAM,""),
175
176     # lov
177     ('lov', "Specify LOV name.", PARAM,""),
178     ('stripe_sz', "Specify the stripe size in bytes.", PARAM),
179     ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0),
180     ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
181
182     # cobd
183     ('real_obd', "", PARAM),
184     ('cache_obd', "", PARAM),
185
186     ('mgmt', "Specify management/monitoring service name.", PARAM, ""),
187     ]
188
189 def error(*args):
190     msg = string.join(map(str,args))
191     raise OptionError("Error: " +  msg)
192
193 def panic(cmd, msg):
194     print "! " + cmd
195     print msg
196     sys.exit(1)
197
198     
199 def warning(*args):
200     msg = string.join(map(str,args))
201     print "Warning: ", msg
202     
203 #
204 # manage names and uuids
205 # need to initialize this by walking tree to ensure
206 # no duplicate names or uuids are created.
207 # this are just place holders for now.
208 # consider changing this to be like OBD-dev-host
209 def new_name(base):
210     ctr = 2
211     ret = base
212     while names.has_key(ret):
213         ret = "%s_%d" % (base, ctr)
214         ctr = 1 + ctr
215     names[ret] = 1
216     return ret
217
218 def new_uuid(name):
219     return "%s_UUID" % (name)
220
221 ldlm_name = 'ldlm'
222 ldlm_uuid = 'ldlm_UUID'
223
224 def new_lustre(dom):
225     """Create a new empty lustre document"""
226     # adding ldlm here is a bit of a hack, but one is enough.
227     str = """<lustre version="%s">
228     <ldlm name="%s" uuid="%s"/>
229     </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
230     return dom.parseString(str)
231
232 names = {}
233 uuids = {}
234
235 def init_names(doc):
236     """initialize auto-name generation tables"""
237     global names, uuids
238     # get all elements that contain a name attribute
239     for n in doc.childNodes:
240         if n.nodeType == n.ELEMENT_NODE:
241             if getName(n):
242                 names[getName(n)] = 1
243                 uuids[getUUID(n)] = 1
244             init_names(n)
245
246 def get_format_flag(options):
247     if options.format:
248         return 'yes'
249     return 'no'
250
251 ############################################################
252 # Build config objects using DOM
253 #
254 class GenConfig:
255     doc = None
256     dom = None
257     def __init__(self, doc):
258         self.doc = doc
259
260     def ref(self, type, uuid):
261         """ generate <[type]_ref uuidref="[uuid]"/> """
262         tag = "%s_ref" % (type)
263         ref = self.doc.createElement(tag)
264         ref.setAttribute("uuidref", uuid)
265         return ref
266     
267     def newService(self, tag, name, uuid):
268         """ create a new  service elmement, which requires name and uuid attributes """
269         new = self.doc.createElement(tag)
270         new.setAttribute("uuid", uuid);
271         new.setAttribute("name", name);
272         return new
273     
274     def addText(self, node, str):
275         txt = self.doc.createTextNode(str)
276         node.appendChild(txt)
277
278     def addElement(self, node, tag, str=None):
279         """ create a new element and add it as a child to node. If str is passed,
280             a text node is created for the new element"""
281         new = self.doc.createElement(tag)
282         if str:
283             self.addText(new, str)
284         node.appendChild(new)
285         return new
286
287     def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
288                 port=0, tcpbuf=0, irq_aff=0):
289         """create <network> node"""
290         network = self.newService("network", name, uuid)
291         network.setAttribute("nettype", net);
292         self.addElement(network, "nid", nid)
293         self.addElement(network, "clusterid", cluster_id)
294         if hostaddr:
295             self.addElement(network, "hostaddr", hostaddr)
296         if port:
297             self.addElement(network, "port", "%d" %(port))
298         if tcpbuf:
299             self.addElement(network, "sendmem", "%d" %(tcpbuf))
300             self.addElement(network, "recvmem", "%d" %(tcpbuf))
301         if irq_aff:
302             self.addElement(network, "irqaffinity", "%d" %(irq_aff))
303             
304         return network
305
306     def routetbl(self, name, uuid):
307         """create <routetbl> node"""
308         rtbl = self.newService("routetbl", name, uuid)
309         return rtbl
310         
311     def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
312         """ create one entry for the route table """
313         ref = self.doc.createElement('route')
314         ref.setAttribute("type", gw_net_type)
315         ref.setAttribute("gw", gw)
316         ref.setAttribute("gwclusterid", gw_cluster_id)
317         ref.setAttribute("tgtclusterid", tgt_cluster_id)
318         ref.setAttribute("lo", lo)
319         if hi:
320             ref.setAttribute("hi", hi)
321         return ref
322     
323     def profile(self, name, uuid):
324         """ create a host """
325         profile = self.newService("profile", name, uuid)
326         return profile
327
328     def node(self, name, uuid, prof_uuid):
329         """ create a host """
330         node = self.newService("node", name, uuid)
331         node.appendChild(self.ref("profile", prof_uuid))
332         return node
333
334     def ldlm(self, name, uuid):
335         """ create a ldlm """
336         ldlm = self.newService("ldlm", name, uuid)
337         return ldlm
338
339     def osd(self, name, uuid, fs, osdtype, devname, format, ost_uuid,
340             node_uuid, dev_size=0, journal_size=0, nspath=""):
341         osd = self.newService("osd", name, uuid)
342         osd.setAttribute('osdtype', osdtype)
343         osd.appendChild(self.ref("target", ost_uuid))
344         osd.appendChild(self.ref("node", node_uuid))
345         if fs:
346             self.addElement(osd, "fstype", fs)
347         if devname:
348             dev = self.addElement(osd, "devpath", devname)
349             self.addElement(osd, "autoformat", format)
350             if dev_size:
351                 self.addElement(osd, "devsize", "%s" % (dev_size))
352             if journal_size:
353                 self.addElement(osd, "journalsize", "%s" % (journal_size))
354         if nspath:
355             self.addElement(osd, "nspath", nspath)
356         return osd
357
358     def cobd(self, name, uuid, real_uuid, cache_uuid):
359         cobd = self.newService("cobd", name, uuid)
360         cobd.appendChild(self.ref("realobd",real_uuid))
361         cobd.appendChild(self.ref("cacheobd",cache_uuid))
362         return cobd
363
364     def ost(self, name, uuid, osd_uuid, group=""):
365         ost = self.newService("ost", name, uuid)
366         ost.appendChild(self.ref("active", osd_uuid))
367         if group:
368             self.addElement(ost, "group", group)
369         return ost
370
371     def oss(self, name, uuid):
372         oss = self.newService("oss", name, uuid)
373         return oss
374
375     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
376         lov = self.newService("lov", name, uuid)
377         lov.appendChild(self.ref("mds", mds_uuid))
378         lov.setAttribute("stripesize", str(stripe_sz))
379         lov.setAttribute("stripecount", str(stripe_cnt))
380         lov.setAttribute("stripepattern", str(pattern))
381         return lov
382
383     def lovconfig(self, name, uuid, lov_uuid):
384         lovconfig = self.newService("lovconfig", name, uuid)
385         lovconfig.appendChild(self.ref("lov", lov_uuid))
386         return lovconfig
387
388     def mds(self, name, uuid, mdd_uuid, group=""):
389         mds = self.newService("mds", name, uuid)
390         mds.appendChild(self.ref("active",mdd_uuid))
391         if group:
392             self.addElement(mds, "group", group)
393         return mds
394
395     def mdsdev(self, name, uuid, fs, devname, format, node_uuid,
396             mds_uuid, dev_size=0, journal_size=0, nspath="", mkfsoptions=""):
397         mdd = self.newService("mdsdev", name, uuid)
398         self.addElement(mdd, "fstype", fs)
399         dev = self.addElement(mdd, "devpath", devname)
400         self.addElement(mdd, "autoformat", format)
401         if dev_size:
402                 self.addElement(mdd, "devsize", "%s" % (dev_size))
403         if journal_size:
404             self.addElement(mdd, "journalsize", "%s" % (journal_size))
405         if nspath:
406             self.addElement(mdd, "nspath", nspath)
407         if mkfsoptions:
408             self.addElement(mdd, "mkfsoptions", mkfsoptions)
409         mdd.appendChild(self.ref("node", node_uuid))
410         mdd.appendChild(self.ref("target", mds_uuid))
411         return mdd
412
413     def mgmt(self, mgmt_name, mgmt_uuid, node_uuid):
414         mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid)
415         mgmt.appendChild(self.ref("node", node_uuid))
416         # Placeholder until mgmt-service failover.
417         mgmt.appendChild(self.ref("active", mgmt_uuid))
418         return mgmt
419
420     def mountpoint(self, name, uuid, fs_uuid, path):
421         mtpt = self.newService("mountpoint", name, uuid)
422         mtpt.appendChild(self.ref("filesystem", fs_uuid))
423         self.addElement(mtpt, "path", path)
424         return mtpt
425
426     def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid):
427         fs = self.newService("filesystem", name, uuid)
428         fs.appendChild(self.ref("mds", mds_uuid))
429         fs.appendChild(self.ref("obd", obd_uuid))
430         if mgmt_uuid:
431             fs.appendChild(self.ref("mgmt", mgmt_uuid))
432         return fs
433         
434     def echo_client(self, name, uuid, osc_uuid):
435         ec = self.newService("echoclient", name, uuid)
436         ec.appendChild(self.ref("obd", osc_uuid))
437         return ec
438
439 ############################################################
440 # Utilities to query a DOM tree
441 # Using this functions we can treat use config information
442 # directly as a database.
443 def getName(n):
444     return n.getAttribute('name')
445
446 def getUUID(node):
447     return node.getAttribute('uuid')
448
449
450 def findByName(lustre, name, tag = ""):
451     for n in lustre.childNodes:
452         if n.nodeType == n.ELEMENT_NODE:
453             if tag and n.nodeName != tag:
454                 continue
455             if getName(n) == name:
456                 return n
457             else:
458                 n = findByName(n, name)
459                 if n: return n
460     return None
461
462
463 def lookup(node, uuid):
464     for n in node.childNodes:
465         if n.nodeType == n.ELEMENT_NODE:
466             if getUUID(n) == uuid:
467                 return n
468             else:
469                 n = lookup(n, uuid)
470                 if n: return n
471     return None
472
473
474 def name2uuid(lustre, name, tag="",  fatal=1):
475     ret = findByName(lustre, name, tag)
476     if not ret:
477         if fatal:
478             error('name2uuid:', '"'+name+'"', tag, 'element not found.')
479         else:
480             return ""
481     return getUUID(ret)
482     
483 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
484     for n in lustre.childNodes:
485         if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
486             if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
487                 return getUUID(n)
488     return None
489
490 # XXX: assumes only one network element per node. will fix this
491 # as soon as support for routers is added
492 def get_net_uuid(lustre, node_name):
493     """ get a network uuid for a node_name """
494     node = findByName(lustre, node_name, "node")
495     if not node:
496         error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
497     net = node.getElementsByTagName('network')
498     if net:
499         return getUUID(net[0])
500     return None
501
502
503 def lov_add_obd(gen, lov, osc_uuid):
504     lov.appendChild(gen.ref("obd", osc_uuid))
505                             
506 def ref_exists(profile, uuid):
507     elist = profile.childNodes
508     for e in elist:
509         if e.nodeType == e.ELEMENT_NODE:
510             ref = e.getAttribute('uuidref')
511             if ref == uuid:
512                 return 1
513     return 0
514         
515 # ensure that uuid is not already in the profile
516 # return true if uuid is added
517 def node_add_profile(gen, node, ref, uuid):
518     refname = "%s_ref" % "profile"
519     ret = node.getElementsByTagName(refname)
520     if not ret:
521         error('node has no profile ref:', node)
522     prof_uuid = ret[0].getAttribute('uuidref')
523     profile = lookup(node.parentNode, prof_uuid)
524     if not profile:
525         error("no profile found:", prof_uuid)
526     if ref_exists(profile, uuid):
527         return 0
528     profile.appendChild(gen.ref(ref, uuid))
529     return 1
530     
531 def get_attr(dom_node, attr, default=""):
532     v = dom_node.getAttribute(attr)
533     if v:
534         return v
535     return default
536
537 ############################################################
538 # Top level commands
539 #
540 def set_node_options(gen, node, options):
541     if options.router:
542         node.setAttribute('router', '1')
543     if options.timeout:
544         gen.addElement(node, "timeout", get_option(options, 'timeout'))
545     if options.upcall:
546         default_upcall =  get_option(options, 'upcall')
547     else:
548         default_upcall = ''
549     if default_upcall or options.lustre_upcall:
550         if options.lustre_upcall:
551             gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
552         else: 
553             gen.addElement(node, 'lustreUpcall', default_upcall)
554     if default_upcall or options.portals_upcall:
555         if options.portals_upcall:
556             gen.addElement(node, 'portalsUpcall', options.portals_upcall)
557         else:
558             gen.addElement(node, 'portalsUpcall', default_upcall)
559     return node
560
561 def do_add_node(gen, lustre,  options, node_name):
562     uuid = new_uuid(node_name)
563     prof_name = new_name("PROFILE_" + node_name)
564     prof_uuid = new_uuid(prof_name)
565     profile = gen.profile(prof_name, prof_uuid)
566     node = gen.node(node_name, uuid, prof_uuid)
567     lustre.appendChild(node)
568     lustre.appendChild(profile)
569
570     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
571     set_node_options(gen, node, options)
572     return node
573
574     
575 def add_node(gen, lustre, options):
576     """ create a node with a network config """
577
578     node_name = get_option(options, 'node')
579     ret = findByName(lustre, node_name, "node")
580     if ret:
581         print "Node:", node_name, "exists."
582         return
583     do_add_node(gen, lustre, options, node_name)
584
585
586 def add_net(gen, lustre, options):
587     """ create a node with a network config """
588
589     node_name = get_option(options, 'node')
590     nid = get_option(options, 'nid')
591     cluster_id = get_option(options, 'cluster_id')
592     hostaddr = get_option(options, 'hostaddr')
593     net_type = get_option(options, 'nettype')
594
595     if net_type in ('tcp', 'toe'):
596         port = get_option_int(options, 'port')
597         tcpbuf = get_option_int(options, 'tcpbuf')
598         irq_aff = get_option_int(options, 'irq_affinity')
599     elif net_type in ('elan', 'gm', 'scimac'):
600         port = 0
601         tcpbuf = 0
602         irq_aff = 0
603     else:
604         print "Unknown net_type: ", net_type
605         sys.exit(2)
606
607     ret = findByName(lustre, node_name, "node")
608     if not ret:
609         node = do_add_node(gen, lustre, options, node_name)
610     else:
611         node = ret
612         set_node_options(gen, node, options)
613
614     net_name = new_name('NET_'+ node_name +'_'+ net_type)
615     net_uuid = new_uuid(net_name)
616     node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
617                                  hostaddr, port, tcpbuf, irq_aff))
618     node_add_profile(gen, node, "network", net_uuid)
619
620
621 def add_route(gen, lustre, options):
622     """ create a node with a network config """
623
624     node_name = get_option(options, 'node')
625     gw_net_type = get_option(options, 'nettype')
626     gw = get_option(options, 'gw')
627     gw_cluster_id = get_option(options, 'gateway_cluster_id')
628     tgt_cluster_id = get_option(options, 'target_cluster_id')
629     lo = get_option(options, 'lo')
630     hi = get_option(options, 'hi')
631     if not hi:
632         hi = lo
633
634     node = findByName(lustre, node_name, "node")
635     if not node:
636         error (node_name, " not found.")
637     
638     rlist = node.getElementsByTagName('routetbl')
639     if len(rlist) > 0:
640         rtbl = rlist[0]
641     else:
642         rtbl_name = new_name("RTBL_" + node_name)
643         rtbl_uuid = new_uuid(rtbl_name)
644         rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
645         node.appendChild(rtbl)
646         node_add_profile(gen, node, "routetbl", rtbl_uuid)
647     rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
648                                lo, hi))
649
650
651 def add_mds(gen, lustre, options):
652     node_name = get_option(options, 'node')
653     mds_name = get_option(options, 'mds')
654     mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
655     mdd_uuid = new_uuid(mdd_name)
656
657     mds_uuid = name2uuid(lustre, mds_name, fatal=0)
658     if not mds_uuid:
659         mds_uuid = new_uuid(mds_name)
660         mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
661         lustre.appendChild(mds)
662     else:
663         mds = lookup(lustre, mds_uuid)
664     if options.failover:
665         mds.setAttribute('failover', "1")
666
667     devname = get_option(options, 'dev')
668     size = get_option(options, 'size')
669     fstype = get_option(options, 'fstype')
670     journal_size = get_option(options, 'journal_size')
671     nspath = get_option(options, 'nspath')
672     mkfsoptions = get_option(options, 'mkfsoptions')
673
674     node_uuid = name2uuid(lustre, node_name, 'node')
675
676     node = findByName(lustre, node_name, "node")
677     node_add_profile(gen, node, "mdsdev", mdd_uuid)
678     net_uuid = get_net_uuid(lustre, node_name)
679     if not net_uuid:
680         error("NODE: ", node_name, "not found")
681
682     mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
683                      get_format_flag(options), node_uuid, mds_uuid,
684                      size, journal_size, nspath, mkfsoptions)
685     lustre.appendChild(mdd)
686                    
687
688 def add_mgmt(gen, lustre, options):
689     node_name = get_option(options, 'node')
690     node_uuid = name2uuid(lustre, node_name)
691     mgmt_name = get_option(options, 'mgmt')
692     if not mgmt_name:
693         mgmt_name = new_name('MGMT_' + node_name)
694     mgmt_uuid = name2uuid(lustre, mgmt_name, fatal=0)
695     if not mgmt_uuid:
696         mgmt_uuid = new_uuid(mgmt_name)
697         mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid)
698         lustre.appendChild(mgmt)
699     else:
700         mgmt = lookup(lustre, mgmt_uuid)
701
702     node = findByName(lustre, node_name, "node")
703     node_add_profile(gen, node, 'mgmt', mgmt_uuid)
704
705 def add_ost(gen, lustre, options):
706     node_name = get_option(options, 'node')
707     lovname = get_option(options, 'lov')
708     osdtype = get_option(options, 'osdtype')
709
710     node_uuid = name2uuid(lustre, node_name)
711
712     if osdtype == 'obdecho':
713         fstype = ''
714         devname = ''
715         size = 0
716         fstype = ''
717         journal_size = ''
718     else:
719         devname = get_option(options, 'dev') # can be unset for bluearcs
720         size = get_option(options, 'size')
721         fstype = get_option(options, 'fstype')
722         journal_size = get_option(options, 'journal_size')
723         
724     nspath = get_option(options, 'nspath')
725
726     ostname = get_option(options, 'ost')
727     if not ostname:
728         ostname = new_name('OST_'+ node_name)
729
730     osdname = new_name("OSD_" + ostname + "_" + node_name)
731     osd_uuid = new_uuid(osdname)
732
733     ost_uuid = name2uuid(lustre, ostname, fatal=0)
734     if not ost_uuid:
735         ost_uuid = get_option(options, 'ostuuid')
736         if ost_uuid:
737             if lookup(lustre, ost_uuid):
738                 error("Duplicate OST UUID:", ost_uuid)
739         else:
740             ost_uuid = new_uuid(ostname)
741
742         ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
743         lustre.appendChild(ost)
744         if lovname:
745             lov = findByName(lustre, lovname, "lov")
746             if not lov:
747                 error('add_ost:', '"'+lovname+'"', "lov element not found.")
748             lov_add_obd(gen, lov, ost_uuid)
749     else:
750         ost = lookup(lustre, ost_uuid)
751
752     if options.failover:
753         ost.setAttribute('failover', "1")
754     
755
756     osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
757                   get_format_flag(options), ost_uuid, node_uuid, size,
758                   journal_size, nspath)
759
760     node = findByName(lustre, node_name, "node")
761
762 ##     if node_add_profile(gen, node, 'oss', oss_uuid):
763 ##         ossname = 'OSS'
764 ##         oss_uuid = new_uuid(ossname)
765 ##         oss = gen.oss(ossname, oss_uuid)
766 ##         lustre.appendChild(oss)
767
768     node_add_profile(gen, node, 'osd', osd_uuid)
769     lustre.appendChild(osd)
770
771                    
772 def add_cobd(gen, lustre, options):
773     node_name = get_option(options, 'node')
774     name = new_name('COBD_' + node_name)
775     uuid = new_uuid(name)
776
777     real_name = get_option(options, 'real_obd')
778     cache_name = get_option(options, 'cache_obd')
779     
780     real_uuid = name2uuid(lustre, real_name, tag='obd')
781     cache_uuid = name2uuid(lustre, cache_name, tag='obd')
782
783     node = findByName(lustre, node_name, "node")
784     node_add_profile(gen, node, "cobd", uuid)
785     cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
786     lustre.appendChild(cobd)
787
788
789 def add_echo_client(gen, lustre, options):
790     """ add an echo client to the profile for this node. """
791     node_name = get_option(options, 'node')
792     lov_name = get_option(options, 'ost')
793
794     node = findByName(lustre, node_name, 'node')
795
796     echoname = new_name('ECHO_'+ node_name)
797     echo_uuid = new_uuid(echoname)
798     node_add_profile(gen, node, 'echoclient', echo_uuid)
799
800     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
801     if not lov_uuid:
802         lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
803
804     echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
805     lustre.appendChild(echo)
806
807
808 def add_lov(gen, lustre, options):
809     """ create a lov """
810
811     lov_orig = get_option(options, 'lov')
812     name = new_name(lov_orig)
813     if name != lov_orig:
814         warning("name:", lov_orig, "already used. using:", name)
815
816     mds_name = get_option(options, 'mds')
817     stripe_sz = get_option_int(options, 'stripe_sz')
818     stripe_cnt = get_option_int(options, 'stripe_cnt')
819     pattern = get_option_int(options, 'stripe_pattern')
820     uuid = new_uuid(name)
821
822     ret = findByName(lustre, name, "lov")
823     if ret:
824         error("LOV: ", name, " already exists.")
825
826     mds_uuid = name2uuid(lustre, mds_name, 'mds')
827     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
828     lustre.appendChild(lov)
829     
830     # add an lovconfig entry to the active mdsdev profile
831     lovconfig_name = new_name('LVCFG_' + name)
832     lovconfig_uuid = new_uuid(lovconfig_name)
833     mds = findByName(lustre, mds_name)
834     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
835     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
836     lustre.appendChild(lovconfig)
837
838 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
839     fs_name = new_name("FS_fsname")
840     fs_uuid = new_uuid(fs_name)
841     mds = lookup(lustre, mds_uuid)
842     mds.appendChild(gen.ref("filesystem", fs_uuid))
843     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid)
844     lustre.appendChild(fs)
845     return fs_uuid
846
847 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
848     mds_uuid = name2uuid(lustre, mds_name, tag='mds')
849     obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
850     if not obd_uuid:
851         obd_uuid = name2uuid(lustre, obd_name, tag='ost', fatal=1)
852     if mgmt_name:
853         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
854     else:
855         mgmt_uuid = ''
856     fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
857     if not fs_uuid:
858         fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid)
859     return fs_uuid
860     
861 def add_mtpt(gen, lustre, options):
862     """ create mtpt on a node """
863     node_name = get_option(options, 'node')
864
865     path = get_option(options, 'path')
866     fs_name = get_option(options, 'filesystem')
867     if fs_name == '':
868         mds_name = get_option(options, 'mds')
869         lov_name = get_option(options, 'lov')
870         if lov_name == '':
871             lov_name = get_option(options, 'ost')
872             if lov_name == '':
873                 error("--add mtpt requires either --filesystem or --mds with an  --lov lov_name or --ost ost_name")
874         mgmt_name = get_option(options, 'mgmt')
875         fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name)
876     else:
877         fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
878
879     name = new_name('MNT_'+ node_name)
880
881     ret = findByName(lustre, name, "mountpoint")
882     if ret:
883         # this can't happen, because new_name creates unique names
884         error("MOUNTPOINT: ", name, " already exists.")
885
886     uuid = new_uuid(name)
887     mtpt = gen.mountpoint(name, uuid, fs_uuid, path)
888     node = findByName(lustre, node_name, "node")
889     if not node:
890         error('node:',  node_name, "not found.")
891     node_add_profile(gen, node, "mountpoint", uuid)
892     lustre.appendChild(mtpt)
893
894 ############################################################
895 # Command line processing
896 #
897 class OptionError (exceptions.Exception):
898     def __init__(self, args):
899         self.args = args
900
901 def get_option(options, tag):
902     """Look for tag in options hash and return the value if set. If not
903     set, then if return default it is set, otherwise exception."""
904     if options.__getattr__(tag) != None:
905         return options.__getattr__(tag)
906     else:
907         raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
908
909 def get_option_int(options, tag):
910     """Return an integer option.  Raise exception if the value is not an int"""
911     val = get_option(options, tag)
912     try:
913         n = int(val)
914     except ValueError:
915         raise OptionError("--%s <num> (value must be integer)" % (tag))        
916     return n
917
918 # simple class for profiling
919 import time
920 class chrono:
921     def __init__(self):
922         self._start = 0
923     def start(self):
924         self._stop = 0
925         self._start = time.time()
926     def stop(self, msg=''):
927         self._stop = time.time()
928         if msg:
929             self.display(msg)
930     def dur(self):
931         return self._stop - self._start
932     def display(self, msg):
933         d = self.dur()
934         str = '%s: %g secs' % (msg, d)
935         print str
936
937 ############################################################
938 # Main
939 #
940
941 def add(devtype, gen, lustre, options):
942     if devtype == 'net':
943         add_net(gen, lustre, options)
944     elif devtype == 'mtpt':
945         add_mtpt(gen, lustre, options)
946     elif devtype == 'mds':
947         add_mds(gen, lustre, options)
948     elif devtype == 'ost':
949         add_ost(gen, lustre, options)
950     elif devtype == 'lov':
951         add_lov(gen, lustre, options)
952     elif devtype == 'route':
953         add_route(gen, lustre, options)
954     elif devtype == 'node':
955         add_node(gen, lustre, options)
956     elif devtype == 'echo_client':
957         add_echo_client(gen, lustre, options)
958     elif devtype == 'cobd':
959         add_cobd(gen, lustre, options)
960     elif devtype == 'mgmt':
961         add_mgmt(gen, lustre, options)
962     else:
963         error("unknown device type:", devtype)
964     
965 def do_command(gen, lustre, options, args):
966     if options.add:
967         add(options.add, gen, lustre, options)
968     else:
969         error("Missing command")
970
971 def main():
972     cl = Lustre.Options("lmc", "", lmc_options)
973     try:
974         options, args = cl.parse(sys.argv[1:])
975     except Lustre.OptionError, e:
976         panic("lmc", e)
977
978     if len(args) > 0:
979         panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
980
981     if options.reference:
982         reference()
983         sys.exit(0)
984
985     outFile = '-'
986
987     if options.merge:
988         outFile = options.merge
989         if os.access(outFile, os.R_OK):
990             doc = xml.dom.minidom.parse(outFile)
991         else:
992             doc = new_lustre(xml.dom.minidom)
993     elif options.input:
994         doc = xml.dom.minidom.parse(options.input)
995     else:
996         doc = new_lustre(xml.dom.minidom)
997
998     if options.output:
999         outFile = options.output
1000
1001     lustre = doc.documentElement
1002     init_names(lustre)
1003     if lustre.tagName != "lustre":
1004         print "Existing config not valid."
1005         sys.exit(1)
1006
1007     gen = GenConfig(doc)
1008
1009     if options.batch:
1010         fp = open(options.batch)
1011         batchCommands = fp.readlines()
1012         fp.close()
1013         for cmd in batchCommands:
1014             try:
1015                 options, args = cl.parse(string.split(cmd))
1016                 do_command(gen, lustre, options, args)
1017             except OptionError, e:
1018                 panic(cmd, e)
1019             except Lustre.OptionError, e:
1020                 panic(cmd, e)
1021     else:
1022         try:
1023             do_command(gen, lustre, options, args)
1024         except OptionError, e:
1025             panic(string.join(sys.argv),e)
1026         except Lustre.OptionError, e:
1027             panic("lmc", e)
1028
1029     if outFile == '-':
1030         PrettyPrint(doc)
1031     else:
1032         PrettyPrint(doc, open(outFile,"w"))
1033
1034 if __name__ == "__main__":
1035     main()