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