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