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