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