Whamcloud - gitweb
b=3031
[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, re
29 import xml.dom.minidom
30
31 def printDoc(doc, stream=sys.stdout):
32     try:
33         from xml.dom.ext import PrettyPrint
34         PrettyPrint(doc, stream)
35     except ImportError:
36         stream.write(doc.toxml())
37         stream.write("\n")
38
39
40 PYMOD_DIR = "/usr/lib/lustre/python"
41
42 def development_mode():
43     base = os.path.dirname(sys.argv[0])
44     if os.access(base+"/Makefile.am", os.R_OK):
45         return 1
46     return 0
47
48 if not development_mode():
49     sys.path.append(PYMOD_DIR)
50
51 import Lustre
52
53 DEFAULT_PORT = 988
54 DEFAULT_STRIPE_SZ = 1048576
55 DEFAULT_STRIPE_CNT = 1
56 DEFAULT_STRIPE_PATTERN = 0
57 UUID_MAX_LENGTH = 31
58
59 def reference():
60     print """usage: lmc --add object [object parameters]
61
62 Object creation command summary:
63
64 --add node
65   --node node_name
66   --timeout num
67   --upcall path
68   --lustre_upcall path
69   --portals_upcall path
70   --ptldebug debug_level
71   --subsystem subsystem_name
72
73 --add net
74   --node node_name
75   --nid nid
76   --cluster_id 
77   --nettype tcp|elan|gm|openib|iib|vib|ra
78   --hostaddr ip[/netmask]
79   --port port
80   --tcpbuf size
81   --irq_affinity 0|1
82   --router
83
84 --add mds
85   --node node_name
86   --mds mds_name
87   --failover
88   --dev path
89   --backdev path
90   --fstype ldiskfs|ext3
91   --backfstype ldiskfs|ext3|tmpfs
92   --size size
93   --nspath
94   --journal_size size
95   --inode_size size
96   --mdsuuid uuid
97   --lmv lmv_name
98   --mkfsoptions options
99   --mountfsoptions options
100   --root_squash uid:gid
101   --no_root_squash ptl_nid 
102
103 --add lov
104   --lov lov_name
105   --mds mds_name
106   --stripe_sz num
107   --stripe_cnt num
108   --stripe_pattern num
109   --lmv lmv_name
110
111 --add ost
112   --node node_name
113   --ost ost_name 
114   --failover
115   --lov lov_name 
116   --index index
117   --dev path
118   --backdev path
119   --size size
120   --fstype ldiskfs|ext3
121   --backfstype ldiskfs|ext3|tmpfs
122   --journal_size size
123   --inode_size size
124   --osdtype obdecho|obdfilter
125   --ostuuid uuid
126   --mkfsoptions options
127   --mountfsoptions options
128   --nspath
129  
130 --delete ost
131   --node node_name
132   --ost ost_name
133   --migrate
134 --deactivate ost
135   --node node_name
136   --ost ost_name
137
138 --add mtpt  - Mountpoint
139   --node node_name
140   --path /mnt/point
141   --mds mds_name
142   --lmv lmv_name
143   --ost ost_name OR --lov lov_name
144   --clientoptions options
145
146 --add route
147   --node nodename
148   --router
149   --gw nid
150   --gateway_cluster_id nid
151   --target_cluster_id nid
152   --lo nid
153   --hi nid
154
155 --add echo_client
156   --node nodename
157
158 --add mgmt  - Management/monitoring service
159   --node node_name
160   --mgmt mgmt_service_name
161
162 --add lmv
163   --lmv lmv_name
164
165 --add cobd
166   --node node_name
167   --master_obd obd_name
168   --cache_obd obd_name
169
170 --add cmobd
171   --node node_name
172   --master_obd obd_name
173   --cache_obd obd_name
174
175 --commit - Close a configuration version, and start a new one
176 """
177
178 PARAM = Lustre.Options.PARAM
179 PARAMLIST = Lustre.Options.PARAMLIST
180 lmc_options = [
181     # lmc input/output options
182     ('reference', "Print short reference for commands."),
183     ('verbose,v', "Print system commands as they are run."),
184     ('merge,m', "Append to the specified config file.", PARAM),
185     ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
186     ('input,i', "", PARAM),
187     ('batch', "Used to execute lmc commands in batch mode.", PARAM),
188
189     # commands
190     ('add', "", PARAM),
191     ('delete', "", PARAM),
192     ('deactivate', "", PARAM),
193     ('commit', "Commit all config changes and start a new version"),
194
195     # node options
196     ('node', "Add a new node in the cluster configuration.", PARAM),
197     ('timeout', "Set timeout to initiate recovery.", PARAM),
198     ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
199     ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
200     ('portals_upcall', "Set location of portals upcall script.", PARAM),
201     ('ptldebug', "Set the portals debug level",  PARAM),
202     ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log",  PARAM),
203
204     # network 
205     ('nettype', "Specify the network type. This can be tcp/elan/gm/openib/iib/vib/ra.", PARAM),
206     ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
207     ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
208     ('hostaddr', "Optional argument to specify the host address.", PARAMLIST),
209     ('cluster_id', "Specify the cluster ID", PARAM, "0"),
210
211     # routes
212     ('route', "Add a new route for the cluster.", PARAM),
213     ('router', "Optional flag to mark a node as router."),
214     ('gw', "Specify the nid of the gateway for a route.", PARAM),
215     ('gateway_cluster_id', "", PARAM, "0"),
216     ('target_cluster_id', "", PARAM, "0"),
217     ('lo', "For a range route, this is the low value nid.", PARAM),
218     ('hi', "For a range route, this is a hi value nid.", PARAM,""),
219
220     # servers: mds and ost
221     ('mds', "Specify MDS name.", PARAM,""),
222     ('ost', "Specify the OST name.", PARAM,""),
223     ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
224     ('failover', "Enable failover support on OSTs or MDS?"),
225     ('group', "", PARAM),
226     ('dev', "Path of the device on local system.", PARAM,""),
227     ('backdev', "Path of the device for backing storage on local system.", PARAM,""),
228     ('size', "Specify the size of the device if needed.", PARAM,"0"),
229     ('journal_size', "Specify new journal size for underlying file system.", PARAM,"0"),
230     ('inode_size', "Specify new inode size for underlying file system.", PARAM,"0"),
231     ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
232     ('backfstype', "Optional argument to specify the backing filesystem type.", PARAM, "ext3"),
233     ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
234     ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""),
235     ('ostuuid', "Optional argument to specify OST UUID", PARAM,""),
236     ('mdsuuid', "Optional argument to specify MDS UUID", PARAM,""),
237     ('root_squash', "MDS squash root to appointed uid.", PARAM, ""),
238     ('no_root_squash', "Don't squash root for appointed nid.", PARAM, ""),
239     ('nspath', "Local mount point of server namespace.", PARAM,""),
240     ('format', ""),
241     ('migrate', "used for offline migrate of an ost in conjunctio with add/delete"),
242
243     # clients: mountpoint and echo
244     ('echo_client', "", PARAM),
245     ('path', "Specify the mountpoint for Lustre.", PARAM),
246     ('filesystem', "Lustre filesystem name", PARAM,""),
247     ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""),
248
249     # lov
250     ('lov', "Specify LOV name.", PARAM,""),
251     ('index', "Specify index for OBD in LOV target table.", PARAM),
252     ('stripe_sz', "Specify the stripe size in bytes.", PARAM),
253     ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0),
254     ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
255
256     # cobd
257     
258     ('master_obd', "Specify the real device for the cache obd system.", PARAM),
259     ('cache_obd', "Specify the cache device for the cache obd system.", PARAM),
260     ('cobd', "Specify COBD name", PARAM),
261
262     # cmobd
263     ('master_obd', "Specify the master device for the cmobd system.", PARAM),
264     ('cache_obd',  "Specify the cache device for the cmobd obd system.", PARAM),
265     ('cmobd',      "Specify COBD name", PARAM),
266
267
268     ('mgmt', "Specify management/monitoring service name.", PARAM, ""),
269
270     # lmv
271     ('lmv', "Specify LMV name.", PARAM,""),
272     ]
273
274 def error(*args):
275     msg = string.join(map(str,args))
276     raise OptionError("Error: " +  msg)
277
278 def panic(cmd, msg):
279     print "! " + cmd
280     print msg
281     sys.exit(1)
282
283
284 def warning(*args):
285     msg = string.join(map(str,args))
286     print "Warning: ", msg
287
288 #
289 # manage names and uuids
290 # need to initialize this by walking tree to ensure
291 # no duplicate names or uuids are created.
292 # this are just place holders for now.
293 # consider changing this to be like OBD-dev-host
294 def new_name(base):
295     ctr = 2
296     ret = base
297     while names.has_key(ret):
298         ret = "%s_%d" % (base, ctr)
299         ctr = 1 + ctr
300     names[ret] = 1
301     return ret
302
303 def new_uuid(name):
304     ctr = 2
305     ret = "%s_UUID" % (name)
306     if len(ret) > UUID_MAX_LENGTH:
307         ret = ret[-UUID_MAX_LENGTH:]
308     while uuids.has_key(ret):
309         ret = "%s_UUID_%d" % (name, ctr)
310         ctr = 1 + ctr
311         if len(ret) > UUID_MAX_LENGTH:
312             ret = ret[-UUID_MAX_LENGTH:]
313     uuids[ret] = 1
314     return ret
315
316
317 ldlm_name = 'ldlm'
318 ldlm_uuid = 'ldlm_UUID'
319
320 def new_lustre(dom):
321     """Create a new empty lustre document"""
322     # adding ldlm here is a bit of a hack, but one is enough.
323     str = """<lustre version="%s">
324     <ldlm name="%s" uuid="%s"/>
325     </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
326     return dom.parseString(str)
327
328 names = {}
329 uuids = {}
330
331 def init_names(doc):
332     """initialize auto-name generation tables"""
333     global names, uuids
334     # get all elements that contain a name attribute
335     for n in doc.childNodes:
336         if n.nodeType == n.ELEMENT_NODE:
337             if getName(n):
338                 names[getName(n)] = 1
339                 uuids[getUUID(n)] = 1
340             init_names(n)
341
342 def get_format_flag(options):
343     if options.format:
344         return 'yes'
345     return 'no'
346
347 ############################################################
348 # Build config objects using DOM
349 #
350 class GenConfig:
351     doc = None
352     dom = None
353     def __init__(self, doc):
354         self.doc = doc
355
356     def ref(self, type, uuid):
357         """ generate <[type]_ref uuidref="[uuid]"/> """
358         tag = "%s_ref" % (type)
359         ref = self.doc.createElement(tag)
360         ref.setAttribute("uuidref", uuid)
361         return ref
362         
363     def dev(self, devname):
364         """ generate <dev devpath="[devname]"/> """
365         tgt = self.doc.createElement('dev')
366         tgt.setAttribute("dev", devname)
367         return tgt
368
369     def newService(self, tag, name, uuid):
370         """ create a new  service elmement, which requires name and uuid attributes """
371         new = self.doc.createElement(tag)
372         new.setAttribute("uuid", uuid);
373         new.setAttribute("name", name);
374         return new
375
376     def addText(self, node, str):
377         txt = self.doc.createTextNode(str)
378         node.appendChild(txt)
379
380     def addElement(self, node, tag, str=None):
381         """ create a new element and add it as a child to node. If str is passed,
382             a text node is created for the new element"""
383         new = self.doc.createElement(tag)
384         if str:
385             self.addText(new, str)
386         node.appendChild(new)
387         return new
388
389     def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
390                 port=0):
391         """create <network> node"""
392         network = self.newService("network", name, uuid)
393         network.setAttribute("nettype", net);
394         self.addElement(network, "nid", nid)
395         self.addElement(network, "clusterid", cluster_id)
396         for host in  hostaddr:
397             self.addElement(network, "hostaddr", host)
398         if port:
399             self.addElement(network, "port", "%d" %(port))
400         
401         return network
402
403     def routetbl(self, name, uuid):
404         """create <routetbl> node"""
405         rtbl = self.newService("routetbl", name, uuid)
406         return rtbl
407
408     def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
409         """ create one entry for the route table """
410         ref = self.doc.createElement('route')
411         ref.setAttribute("type", gw_net_type)
412         ref.setAttribute("gw", gw)
413         ref.setAttribute("gwclusterid", gw_cluster_id)
414         ref.setAttribute("tgtclusterid", tgt_cluster_id)
415         ref.setAttribute("lo", lo)
416         if hi:
417             ref.setAttribute("hi", hi)
418         return ref
419
420     def profile(self, name, uuid):
421         """ create a host """
422         profile = self.newService("profile", name, uuid)
423         return profile
424
425     def node(self, name, uuid, prof_uuid):
426         """ create a host """
427         node = self.newService("node", name, uuid)
428         node.appendChild(self.ref("profile", prof_uuid))
429         return node
430
431     def ldlm(self, name, uuid):
432         """ create a ldlm """
433         ldlm = self.newService("ldlm", name, uuid)
434         return ldlm
435
436     def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid,
437             node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", 
438             mkfsoptions="", mountfsoptions="", backfstype="", backdevname=""):
439         osd = self.newService("osd", name, uuid)
440         osd.setAttribute('osdtype', osdtype)
441         osd.appendChild(self.ref("target", ost_uuid))
442         osd.appendChild(self.ref("node", node_uuid))
443         osd.appendChild(self.dev(devname)) 
444
445         if fstype:
446             self.addElement(osd, "fstype", fstype)
447         if backfstype:
448             self.addElement(osd, "backfstype", backfstype)
449         if backdevname:
450             self.addElement(osd, "backdevpath", backdevname)
451         if devname:
452             dev = self.addElement(osd, "devpath", devname)
453             self.addElement(osd, "autoformat", format)
454             if dev_size:
455                 self.addElement(osd, "devsize", "%s" % (dev_size))
456             if journal_size:
457                 self.addElement(osd, "journalsize", "%s" % (journal_size))
458             if inode_size:
459                 self.addElement(osd, "inodesize", "%s" % (inode_size))
460             if mkfsoptions:
461                 self.addElement(osd, "mkfsoptions", mkfsoptions)
462             if mountfsoptions:
463                 self.addElement(osd, "mountfsoptions", mountfsoptions)
464         if nspath:
465             self.addElement(osd, "nspath", nspath)
466         return osd
467
468     def cobd(self, name, uuid, master_uuid, cache_uuid):
469         cobd = self.newService("cobd", name, uuid)
470         cobd.appendChild(self.ref("masterobd",master_uuid))
471         cobd.appendChild(self.ref("cacheobd",cache_uuid))
472         return cobd
473
474     def cmobd(self, name, uuid, master_uuid, cache_uuid):
475         cmobd = self.newService("cmobd", name, uuid)
476         cmobd.appendChild(self.ref("masterobd",master_uuid))
477         cmobd.appendChild(self.ref("cacheobd",cache_uuid))
478         return cmobd
479
480     def ost(self, name, uuid, osd_uuid, group=""):
481         ost = self.newService("ost", name, uuid)
482         ost.appendChild(self.ref("active", osd_uuid))
483         if group:
484             self.addElement(ost, "group", group)
485         return ost
486
487     def oss(self, name, uuid):
488         oss = self.newService("oss", name, uuid)
489         return oss
490
491     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
492         lov = self.newService("lov", name, uuid)
493         lov.appendChild(self.ref("mds", mds_uuid))
494         lov.setAttribute("stripesize", str(stripe_sz))
495         lov.setAttribute("stripecount", str(stripe_cnt))
496         lov.setAttribute("stripepattern", str(pattern))
497         return lov
498
499     def lov_tgt(self, obd_uuid, index, generation):
500         tgt = self.doc.createElement('lov_tgt')
501         tgt.setAttribute("uuidref", obd_uuid)
502         tgt.setAttribute("index", index)
503         tgt.setAttribute("generation", generation)
504         tgt.setAttribute("active", '1')
505         return tgt
506
507     def lovconfig(self, name, uuid, lov_uuid):
508         lovconfig = self.newService("lovconfig", name, uuid)
509         lovconfig.appendChild(self.ref("lov", lov_uuid))
510         return lovconfig
511
512     def lmv_tgt(self, mdt_uuid):
513         tgt = self.doc.createElement('lmv_tgt')
514         tgt.setAttribute("uuidref", mdt_uuid)
515         return tgt
516
517     def lmv(self, name, uuid):
518         lmv = self.newService("lmv", name, uuid)
519         return lmv
520
521     def mds(self, name, uuid, mdd_uuid, group="", lmv=""):
522         mds = self.newService("mds", name, uuid)
523         mds.appendChild(self.ref("active", mdd_uuid))
524         if group:
525             self.addElement(mds, "group", group)
526         return mds
527
528     def mdsdev(self, name, uuid, fstype, devname, format, node_uuid,
529                mds_uuid, dev_size=0, journal_size=0, inode_size=256,
530                nspath="", mkfsoptions="", mountfsoptions="", backfstype="", 
531                backdevname="",lmv_uuid="", root_squash="", no_root_squash=""):
532         mdd = self.newService("mdsdev", name, uuid)
533         self.addElement(mdd, "fstype", fstype)
534         if backfstype:
535             self.addElement(mdd, "backfstype", backfstype)
536         dev = self.addElement(mdd, "devpath", devname)
537         if backdevname:
538             self.addElement(mdd, "backdevpath", backdevname)
539         self.addElement(mdd, "autoformat", format)
540         if dev_size:
541             self.addElement(mdd, "devsize", "%s" % (dev_size))
542         if journal_size:
543             self.addElement(mdd, "journalsize", "%s" % (journal_size))
544         if inode_size:
545             self.addElement(mdd, "inodesize", "%s" % (inode_size))
546         if nspath:
547             self.addElement(mdd, "nspath", nspath)
548         if mkfsoptions:
549             self.addElement(mdd, "mkfsoptions", mkfsoptions)
550         if mountfsoptions:
551             self.addElement(mdd, "mountfsoptions", mountfsoptions)
552         if root_squash:
553             self.addElement(mdd, "root_squash", root_squash)
554         if no_root_squash:
555             self.addElement(mdd, "no_root_squash", no_root_squash)
556         mdd.appendChild(self.ref("node", node_uuid))
557         mdd.appendChild(self.ref("target", mds_uuid))
558         
559         dev = self.dev(devname)
560         if dev != None:
561             mdd.appendChild(dev)
562
563         if lmv_uuid:
564             mdd.appendChild(self.ref("lmv", lmv_uuid))
565             self.addElement(mdd, "lmv", lmv_uuid)
566
567         return mdd
568
569     def mgmt(self, mgmt_name, mgmt_uuid, node_uuid):
570         mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid)
571         mgmt.appendChild(self.ref("node", node_uuid))
572         # Placeholder until mgmt-service failover.
573         mgmt.appendChild(self.ref("active", mgmt_uuid))
574         return mgmt
575
576     def mountpoint(self, name, uuid, fs_uuid, path, clientoptions):
577         mtpt = self.newService("mountpoint", name, uuid)
578         mtpt.appendChild(self.ref("filesystem", fs_uuid))
579         self.addElement(mtpt, "path", path)
580         if clientoptions:
581             self.addElement(mtpt, "clientoptions", clientoptions)
582         return mtpt
583
584     def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid):
585         fs = self.newService("filesystem", name, uuid)
586         fs.appendChild(self.ref("mds", mds_uuid))
587         fs.appendChild(self.ref("obd", obd_uuid))
588         if mgmt_uuid:
589             fs.appendChild(self.ref("mgmt", mgmt_uuid))
590         return fs
591
592     def echo_client(self, name, uuid, osc_uuid):
593         ec = self.newService("echoclient", name, uuid)
594         ec.appendChild(self.ref("obd", osc_uuid))
595         return ec
596
597     def update(self, version):
598         new = self.doc.createElement("update")
599         new.setAttribute("version", version)
600         return new
601
602     def add(self, lov, ost, index, gen):
603         new = self.doc.createElement("add")
604         new.setAttribute("lov_uuidref", lov)
605         new.setAttribute("ost_uuidref", ost)
606         new.setAttribute("index", index)
607         new.setAttribute("generation", gen)
608         return new
609
610     def delete(self, lov, ost, index, gen, options):
611         if options.delete:
612             new = self.doc.createElement("delete")
613         else:
614             new = self.doc.createElement("deactivate")
615         new.setAttribute("lov_uuidref", lov)
616         new.setAttribute("ost_uuidref", ost)
617         new.setAttribute("index", index)
618         new.setAttribute("generation", gen)
619         return new
620
621 ############################################################
622 # Utilities to query a DOM tree
623 # Using this functions we can treat use config information
624 # directly as a database.
625 def getName(n):
626     return n.getAttribute('name')
627
628 def getUUID(node):
629     return node.getAttribute('uuid')
630
631 def findLastUpdate(lustre):
632     node = None
633     version = 0
634     for n in lustre.childNodes:
635         if n.nodeType == n.ELEMENT_NODE:
636             if n.nodeName != 'update':
637                 continue
638             tmp = int(n.getAttribute('version'))
639             if not tmp:
640                 error('malformed XML: update tag without a version attribute')
641             if tmp != version + 1:
642                 error('malformed XML: expecting update record '+str(version + 1)+', found '+str(tmp)+'.')
643             version = tmp
644             node = n
645     return node
646
647 def addUpdate(gen, lustre, node):
648     update = findLastUpdate(lustre)
649     if not update:
650         return
651     #add_record = update.getElementsByTagName('add')
652     #if not add_record:
653     #    add_record = gen.add()
654     #    update.appendChild(add_record)
655     #else:
656     #    add_record = add_record[0]
657     #add_record.appendChild(node)
658     update.appendChild(node)
659
660 def delUpdate(gen, lustre, node):
661     update = findLastUpdate(lustre)
662     if not update:
663         return
664     update.appendChild(node)
665
666 def findByName(lustre, name, tag = ""):
667     for n in lustre.childNodes:
668         if n.nodeType == n.ELEMENT_NODE:
669             if tag and n.nodeName != tag:
670                 continue
671             if getName(n) == name:
672                 return n
673             else:
674                 n = findByName(n, name)
675                 if n: return n
676     return None
677
678
679 def lookup(node, uuid):
680     for n in node.childNodes:
681         if n.nodeType == n.ELEMENT_NODE:
682             if getUUID(n) == uuid:
683                 return n
684             else:
685                 n = lookup(n, uuid)
686                 if n: return n
687     return None
688
689
690 def name2uuid(lustre, name, tag="",  fatal=1):
691     ret = findByName(lustre, name, tag)
692     if not ret:
693         if fatal:
694             error('name2uuid:', '"'+name+'"', tag, 'element not found.')
695         else:
696             return ""
697     return getUUID(ret)
698
699 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
700     for n in lustre.childNodes:
701         if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
702             if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
703                 return getUUID(n)
704     return None
705
706 # XXX: assumes only one network element per node. will fix this
707 # as soon as support for routers is added
708 def get_net_uuid(lustre, node_name):
709     """ get a network uuid for a node_name """
710     node = findByName(lustre, node_name, "node")
711     if not node:
712         error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
713     net = node.getElementsByTagName('network')
714     if net:
715         return getUUID(net[0])
716     return None
717
718 def lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options):
719     tgt.setAttribute('uuidref', osc_uuid)
720     if options.migrate:
721         gener = int(tgt.getAttribute('generation'))
722     else:
723         gener = int(tgt.getAttribute('generation')) + 1
724     tgt.setAttribute('generation', str(gener))
725     tgt.setAttribute('active', '1')
726     lov_index = int(tgt.getAttribute('index'))
727     addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index),
728               str(gener)))
729     return
730
731 def lov_add_obd(gen, lustre, lov, osc_uuid, options):
732     lov_name = getName(lov)
733     if options.index:
734         lov_index = get_option_int(options, 'index')
735         for tgt in lustre.getElementsByTagName('lov_tgt'):
736             if str(lov_index) == tgt.getAttribute('index'):
737                 uuidref = tgt.getAttribute('uuidref')
738                 if uuidref != '':
739                     raise OptionError("%s --index %d is still in use: %s" %
740                                       (lov_name, lov_index, uuidref))
741                 lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options)
742                 return
743     else:
744          lov_index = 0
745          for tgt in lustre.getElementsByTagName('lov_tgt'):
746              uuidref = tgt.getAttribute('uuidref')
747              tmp = int(tgt.getAttribute('index'))
748              if tmp != lov_index:
749                  error('malformed xml: LOV targets are not ordered; found index '+str(tmp)+', expected '+str(lov_index)+'.')
750              uuidref = tgt.getAttribute('uuidref')
751              if uuidref == '':
752                  lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options)
753                  return
754              lov_index = lov_index + 1
755
756     lov.appendChild(gen.lov_tgt(osc_uuid, str(lov_index), '1'))
757     addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), '1'))
758
759 def lov_del_obd(gen, lustre, lov, osc_uuid, options):
760     lov_name = getName(lov)
761     if options.index:
762         lov_index = get_option_int(options, 'index')
763         for tgt in lustre.getElementsByTagName('lov_tgt'):
764             index = tgt.getAttribute('index')
765             if index == lov_index:
766                 uuidref = tgt.getAttribute('uuidref')
767                 if uuidref != osc_uuid:
768                     raise OptionError("%s --index %d contains %s, not %s" %
769                                       (lov_name, lov_index, osc_uuid, uuidref))
770                 if options.delete:
771                     tgt.setAttribute('uuidref', '')
772
773                 # bump the generation just in case...
774                 if options.migrate:
775                     gen = int(tgt.getAttribute('generation'))
776                 else:
777                     gen = int(tgt.getAttribute('generation')) + 1
778
779                 tgt.setAttribute('active', '0')
780                 tgt.setAttribute('generation', str(gen))
781                 return
782         raise OptionError("%s --index %d not in use by %s." %
783                           (lov_name, lov_index, osc_uuid))
784
785     for tgt in lustre.getElementsByTagName('lov_tgt'):
786         uuidref = tgt.getAttribute('uuidref')
787         if uuidref == osc_uuid:
788             genera = int(tgt.getAttribute('generation'))
789             delete_rec = gen.delete(getUUID(lov),
790                                     osc_uuid,tgt.getAttribute('index'),
791                                     str(genera), options)
792             delUpdate(gen, lustre, delete_rec)
793
794             if options.delete:
795                 tgt.setAttribute('uuidref', '')
796             if not options.migrate:
797                 genera = genera + 1
798             tgt.setAttribute('active', '0')
799             tgt.setAttribute('generation', str(genera))
800
801 def lmv_add_obd(gen, lmv, mdc_uuid):
802     lmv.appendChild(gen.lmv_tgt(mdc_uuid))
803                             
804 def ref_exists(profile, uuid):
805     elist = profile.childNodes
806     for e in elist:
807         if e.nodeType == e.ELEMENT_NODE:
808             ref = e.getAttribute('uuidref')
809             if ref == uuid:
810                 return 1
811     return 0
812
813 # ensure that uuid is not already in the profile
814 # return true if uuid is added
815 def node_add_profile(gen, node, ref, uuid):
816     refname = "%s_ref" % "profile"
817     ret = node.getElementsByTagName(refname)
818     if not ret:
819         error('node has no profile ref:', node)
820     prof_uuid = ret[0].getAttribute('uuidref')
821     profile = lookup(node.parentNode, prof_uuid)
822     if not profile:
823         error("no profile found:", prof_uuid)
824     if ref_exists(profile, uuid):
825         return 0
826     profile.appendChild(gen.ref(ref, uuid))
827     return 1
828
829 def get_attr(dom_node, attr, default=""):
830     v = dom_node.getAttribute(attr)
831     if v:
832         return v
833     return default
834
835 ############################################################
836 # Top level commands
837 #
838 def set_node_options(gen, node, options):
839     if options.router:
840         node.setAttribute('router', '1')
841     if options.timeout:
842         gen.addElement(node, "timeout", get_option(options, 'timeout'))
843     if options.upcall:
844         default_upcall =  get_option(options, 'upcall')
845     else:
846         default_upcall = ''
847     if default_upcall or options.lustre_upcall:
848         if options.lustre_upcall:
849             gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
850         else:
851             gen.addElement(node, 'lustreUpcall', default_upcall)
852     if default_upcall or options.portals_upcall:
853         if options.portals_upcall:
854             gen.addElement(node, 'portalsUpcall', options.portals_upcall)
855         else:
856             gen.addElement(node, 'portalsUpcall', default_upcall)
857     if options.ptldebug:
858         gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
859     if options.subsystem:
860         gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
861     return node
862
863 def do_add_node(gen, lustre,  options, node_name):
864     uuid = new_uuid(node_name)
865     prof_name = new_name("PROFILE_" + node_name)
866     prof_uuid = new_uuid(prof_name)
867     profile = gen.profile(prof_name, prof_uuid)
868     node = gen.node(node_name, uuid, prof_uuid)
869     lustre.appendChild(node)
870     lustre.appendChild(profile)
871
872     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
873     set_node_options(gen, node, options)
874
875     return node
876
877
878 def add_node(gen, lustre, options):
879     """ create a node with a network config """
880
881     node_name = get_option(options, 'node')
882     ret = findByName(lustre, node_name, "node")
883     if ret:
884         print "Node:", node_name, "exists."
885         return
886     do_add_node(gen, lustre, options, node_name)
887
888
889 def add_net(gen, lustre, options):
890     """ create a node with a network config """
891
892     node_name = get_option(options, 'node')
893     nid = get_option(options, 'nid')
894     cluster_id = get_option(options, 'cluster_id')
895     hostaddr = get_option(options, 'hostaddr')
896     net_type = get_option(options, 'nettype')
897
898     if net_type in ('tcp','openib','ra'):
899         port = get_option_int(options, 'port')
900     elif net_type in ('elan', 'gm', 'iib', 'vib', 'lo', 'cray_kern_nal'):
901         port = 0
902     else:
903         print "Unknown net_type: ", net_type
904         sys.exit(2)
905
906     ret = findByName(lustre, node_name, "node")
907     if not ret:
908         node = do_add_node(gen, lustre, options, node_name)
909     else:
910         node = ret
911         set_node_options(gen, node, options)
912
913     net_name = new_name('NET_'+ node_name +'_'+ net_type)
914     net_uuid = new_uuid(net_name)
915     node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
916                                  hostaddr, port))
917     node_add_profile(gen, node, "network", net_uuid)
918
919
920 def add_route(gen, lustre, options):
921     """ create a node with a network config """
922
923     node_name = get_option(options, 'node')
924     gw_net_type = get_option(options, 'nettype')
925     gw = get_option(options, 'gw')
926     gw_cluster_id = get_option(options, 'gateway_cluster_id')
927     tgt_cluster_id = get_option(options, 'target_cluster_id')
928     lo = get_option(options, 'lo')
929     hi = get_option(options, 'hi')
930     if not hi:
931         hi = lo
932
933     node = findByName(lustre, node_name, "node")
934     if not node:
935         error (node_name, " not found.")
936
937     rlist = node.getElementsByTagName('routetbl')
938     if len(rlist) > 0:
939         rtbl = rlist[0]
940     else:
941         rtbl_name = new_name("RTBL_" + node_name)
942         rtbl_uuid = new_uuid(rtbl_name)
943         rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
944         node.appendChild(rtbl)
945         node_add_profile(gen, node, "routetbl", rtbl_uuid)
946     rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
947                                lo, hi))
948
949 def add_mds(gen, lustre, options):
950     node_name = get_option(options, 'node')
951     mds_name = get_option(options, 'mds')
952     if not mds_name:
953         mds_name = new_name('MDS_'+ node_name)
954     lmv_name = get_option(options, 'lmv')
955     mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
956     mdd_uuid = new_uuid(mdd_name)
957
958     lmv_uuid = ""
959     if lmv_name:
960         lmv = findByName(lustre, lmv_name, "lmv")
961         if not lmv:
962             error('add_mds:', '"' + lmv_name + '"', "lmv element not found.")
963         lmv_uuid = name2uuid(lustre, lmv_name, fatal=0)
964
965     mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
966     if not mds_uuid:
967         mds_uuid = get_option(options, 'mdsuuid')
968         if mds_uuid:
969             if lookup(lustre, mds_uuid):
970                 error("Duplicate MDS UUID:", mds_uuid)
971         else:
972             mds_uuid = new_uuid(mds_name)
973         mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
974         lustre.appendChild(mds)
975         if lmv_name:
976             lmv_add_obd(gen, lmv, mds_uuid)
977     else:
978         mds = lookup(lustre, mds_uuid)
979
980     if options.failover:
981         mds.setAttribute('failover', "1")
982
983     devname = get_option(options, 'dev')
984     backdevname = get_option(options, 'backdev')
985     size = get_option(options, 'size')
986     fstype = get_option(options, 'fstype')
987     backfstype = get_option(options, 'backfstype')
988     journal_size = get_option(options, 'journal_size')
989     inode_size = get_option(options, 'inode_size')
990     nspath = get_option(options, 'nspath')
991     mkfsoptions = get_option(options, 'mkfsoptions')
992     mountfsoptions = get_option(options, 'mountfsoptions')
993     root_squash = get_option(options, 'root_squash')
994     no_root_squash = get_option(options, 'no_root_squash')
995
996     node_uuid = name2uuid(lustre, node_name, 'node')
997
998     node = findByName(lustre, node_name, "node")
999     node_add_profile(gen, node, "mdsdev", mdd_uuid)
1000     net_uuid = get_net_uuid(lustre, node_name)
1001     if not net_uuid:
1002         error("NODE: ", node_name, "not found")
1003
1004     if lmv_name:
1005         mds.appendChild(gen.ref("lmv", lmv_uuid))
1006
1007     mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
1008                      get_format_flag(options), node_uuid, mds_uuid,
1009                      size, journal_size, inode_size, nspath, mkfsoptions, 
1010                      mountfsoptions, backfstype, backdevname,lmv_uuid, 
1011                      root_squash, no_root_squash)
1012     lustre.appendChild(mdd)
1013
1014
1015 def add_mgmt(gen, lustre, options):
1016     node_name = get_option(options, 'node')
1017     node_uuid = name2uuid(lustre, node_name, 'node')
1018     mgmt_name = get_option(options, 'mgmt')
1019     if not mgmt_name:
1020         mgmt_name = new_name('MGMT_' + node_name)
1021     mgmt_uuid = name2uuid(lustre, mgmt_name, 'mgmt', fatal=0)
1022     if not mgmt_uuid:
1023         mgmt_uuid = new_uuid(mgmt_name)
1024         mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid)
1025         lustre.appendChild(mgmt)
1026     else:
1027         mgmt = lookup(lustre, mgmt_uuid)
1028
1029     node = findByName(lustre, node_name, "node")
1030     node_add_profile(gen, node, 'mgmt', mgmt_uuid)
1031
1032 def add_ost(gen, lustre, options):
1033     node_name = get_option(options, 'node')
1034     lovname = get_option(options, 'lov')
1035     osdtype = get_option(options, 'osdtype')
1036
1037     node_uuid = name2uuid(lustre, node_name, 'node')
1038
1039     if osdtype == 'obdecho':
1040         fstype = ''
1041         backfstype = ''
1042         devname = ''
1043         backdevname = ''
1044         size = 0
1045         journal_size = ''
1046         inode_size = ''
1047         mkfsoptions = ''
1048         mountfsoptions = ''
1049     else:
1050         devname = get_option(options, 'dev') # can be unset for bluearcs
1051         backdevname = get_option(options, 'backdev')
1052         size = get_option(options, 'size')
1053         fstype = get_option(options, 'fstype')
1054         backfstype = get_option(options, 'backfstype')
1055         journal_size = get_option(options, 'journal_size')
1056         inode_size = get_option(options, 'inode_size')
1057         mkfsoptions = get_option(options, 'mkfsoptions')
1058         mountfsoptions = get_option(options, 'mountfsoptions')
1059
1060     nspath = get_option(options, 'nspath')
1061
1062     ostname = get_option(options, 'ost')
1063     if not ostname:
1064         ostname = new_name('OST_'+ node_name)
1065
1066     osdname = new_name("OSD_" + ostname + "_" + node_name)
1067     osd_uuid = new_uuid(osdname)
1068
1069     ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
1070     if not ost_uuid:
1071         ost_uuid = get_option(options, 'ostuuid')
1072         if ost_uuid:
1073             if lookup(lustre, ost_uuid):
1074                 error("Duplicate OST UUID:", ost_uuid)
1075         else:
1076             ost_uuid = new_uuid(ostname)
1077
1078         ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
1079         lustre.appendChild(ost)
1080     else:
1081         ost = lookup(lustre, ost_uuid)
1082
1083     if lovname:
1084         lov = findByName(lustre, lovname, "lov")
1085         if not lov:
1086             error('add_ost:', '"'+lovname+'"', "lov element not found.")
1087         lov_add_obd(gen, lustre, lov, ost_uuid, options)
1088
1089     if options.failover:
1090         ost.setAttribute('failover', "1")
1091
1092
1093     osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
1094                   get_format_flag(options), ost_uuid, node_uuid, size,
1095                   journal_size, inode_size, nspath, mkfsoptions, 
1096                   mountfsoptions, backfstype, backdevname)
1097
1098     node = findByName(lustre, node_name, "node")
1099
1100 ##     if node_add_profile(gen, node, 'oss', oss_uuid):
1101 ##         ossname = 'OSS'
1102 ##         oss_uuid = new_uuid(ossname)
1103 ##         oss = gen.oss(ossname, oss_uuid)
1104 ##         lustre.appendChild(oss)
1105
1106     node_add_profile(gen, node, 'osd', osd_uuid)
1107     lustre.appendChild(osd)
1108
1109 def del_ost(gen, lustre, options):
1110     ostname = get_option(options, 'ost')
1111     if not ostname:
1112         raise OptionError("del_ost: --ost requires a <ost name>")
1113     ost = findByName(lustre, ostname, "ost")
1114     if not ost:
1115         error('del_ost: ', 'Unable to find ', ostname)
1116     ost_uuid = name2uuid(lustre, ostname, fatal=0)
1117     if not ost_uuid:
1118         error('del_ost: ', 'Unable to find uuid for ', ostname)
1119     lovname = get_option(options, 'lov')
1120     if lovname:
1121         lov = findByName(lustre, lovname, "lov")
1122         if not lov:
1123             error('del_ost:', '"'+lovname+'"', "lov element not found.")
1124         lov_del_obd(gen, lustre, lov, ost_uuid, options)
1125         # if the user specified a speficic LOV don't delete the OST itself
1126         return
1127
1128     # remove OSD references from all LOVs
1129     for n in lustre.getElementsByTagName('lov'):
1130         lov_del_obd(gen, lustre, n, ost_uuid, options)
1131     if not options.migrate:
1132         return
1133     # delete the OSDs
1134     for osd in lustre.getElementsByTagName('osd'):
1135         if ref_exists(osd, ost_uuid):
1136             osd_uuid = osd.getAttribute('uuid')
1137             # delete all profile references to this OSD
1138             for profile in lustre.getElementsByTagName('profile'):
1139                 for osd_ref in profile.getElementsByTagName('osd_ref'):
1140                     if osd_uuid == osd_ref.getAttribute('uuidref'):
1141                         profile.removeChild(osd_ref)
1142             lustre.removeChild(osd)
1143
1144     # delete the OST
1145     lustre.removeChild(ost)
1146
1147 def add_cmobd(gen, lustre, options):
1148     node_name = get_option(options, 'node')
1149     name = get_option(options, 'cmobd')
1150     uuid = new_uuid(name)
1151
1152     master_name = get_option(options, 'master_obd')
1153     cache_name = get_option(options, 'cache_obd')
1154
1155     master_uuid = name2uuid(lustre, master_name, tag='lov', fatal=0)
1156     cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0)
1157
1158     if not master_uuid or not cache_uuid:
1159         if not master_uuid:
1160             master_uuid = name2uuid(lustre, master_name, tag='ost', fatal=0)
1161         if not cache_uuid:
1162             cache_uuid = name2uuid(lustre, cache_name, tag='ost', fatal=0)
1163
1164     if not master_uuid or not cache_uuid:
1165         if not master_uuid:
1166             master_uuid = name2uuid(lustre, master_name, tag='lmv', fatal=0)
1167         if not cache_uuid:
1168             cache_uuid = name2uuid(lustre, cache_name, tag='lmv', fatal=0)
1169
1170     if not master_uuid or not cache_uuid:
1171         if not master_uuid:
1172             master_uuid = name2uuid(lustre, master_name, tag='mds', fatal=0)
1173         if not cache_uuid:
1174             cache_uuid = name2uuid(lustre, cache_name, tag='mds', fatal=0)
1175
1176     if not master_uuid: 
1177         panic("add_cmobd", "cannot find master_uuid by name '" + 
1178               master_name + "'")
1179     if not cache_uuid: 
1180         panic("add_cmobd", "cannot find cache_uuid by name '" + 
1181               cache_name + "'")
1182
1183     node = findByName(lustre, node_name, "node")
1184     node_add_profile(gen, node, "cmobd", uuid)
1185
1186     master_node = lookup(lustre, master_uuid)
1187     cache_node = lookup(lustre, cache_uuid)
1188     if not master_node:
1189         panic("cmobd_add", "cannot find master node by its uuid " + 
1190               master_uuid);
1191     if not cache_node:
1192         panic("cmobd_add", "cannot find cache node by its uuid " + 
1193               cache_uuid);
1194
1195     active = master_node.getElementsByTagName('active_ref')
1196     if active:
1197         active_uuid = active[0].getAttribute('uuidref')
1198         active_node = lookup(lustre, active_uuid)
1199         if not active_node.getElementsByTagName('obdtype'):
1200             gen.addElement(active_node, 'obdtype', 'master')
1201
1202     active = cache_node.getElementsByTagName('active_ref')
1203     if active:
1204         active_uuid = active[0].getAttribute('uuidref')
1205         active_node = lookup(lustre, active_uuid)
1206         if not active_node.getElementsByTagName('obdtype'):
1207             gen.addElement(active_node, 'obdtype', 'cache')
1208
1209     cmobd = gen.cmobd(name, uuid, master_uuid, cache_uuid)
1210     lustre.appendChild(cmobd)
1211
1212 def add_cobd(gen, lustre, options):
1213     node_name = get_option(options, 'node')
1214     name = get_option(options, 'cobd')
1215     uuid = new_uuid(name)
1216
1217     master_name = get_option(options, 'master_obd')
1218     cache_name = get_option(options, 'cache_obd')
1219
1220     master_uuid = name2uuid(lustre, master_name, tag='lov', fatal=0)
1221     cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0)
1222
1223     if not master_uuid or not cache_uuid:
1224         master_uuid = name2uuid(lustre, master_name, tag='ost', fatal=0)
1225         cache_uuid = name2uuid(lustre, cache_name, tag='ost', fatal=0)
1226
1227     if master_uuid:
1228         node = lookup(lustre, master_uuid)
1229         rets = node.getElementsByTagName('lov_tgt')
1230         for ret in rets:
1231             ost_uuid = ret.getAttribute('uuidref')
1232             ost_node = lookup(lustre, ost_uuid)
1233             active = ost_node.getElementsByTagName('active_ref')
1234             if active:
1235                 osd_uuid = active[0].getAttribute('uuidref')
1236                 osd_node = lookup(lustre, osd_uuid)
1237                 if not osd_node.getElementsByTagName('obdtype'):
1238                     gen.addElement(osd_node, 'obdtype', 'master')
1239
1240     if cache_uuid:
1241         node = lookup(lustre, cache_uuid)
1242         rets = node.getElementsByTagName('lov_tgt')
1243         for ret in rets:
1244             ost_uuid = ret.getAttribute('uuidref')
1245             ost_node = lookup(lustre, ost_uuid)
1246             active = ost_node.getElementsByTagName('active_ref')
1247             if active:
1248                 osd_uuid = active[0].getAttribute('uuidref')
1249                 osd_node = lookup(lustre, osd_uuid)
1250                 if not osd_node.getElementsByTagName('obdtype'):
1251                     gen.addElement(osd_node, 'obdtype', 'cache')
1252
1253     if not master_uuid or not cache_uuid:
1254         master_uuid = name2uuid(lustre,master_name, tag='mds')
1255         cache_uuid = name2uuid(lustre,cache_name, tag='mds')
1256         if master_uuid:
1257             mds_node = lookup(lustre, master_uuid)
1258             ret = mds_node.getElementsByTagName('active_ref')
1259             if ret:
1260                 mdsdev_uuid = ret[0].getAttribute('uuidref')
1261                 mdsdev_node = lookup(lustre, mdsdev_uuid)
1262                 if not mdsdev_node.getElementsByTagName('obdtype'):
1263                     gen.addElement(mdsdev_node, 'obdtype', 'master')
1264         if cache_uuid:
1265             mds_node = lookup(lustre, cache_uuid)
1266             ret = mds_node.getElementsByTagName('active_ref')
1267             if ret:
1268                 mdsdev_uuid = ret[0].getAttribute('uuidref')
1269                 mdsdev_node = lookup(lustre, mdsdev_uuid)
1270                 if not mdsdev_node.getElementsByTagName('obdtype'):
1271                     gen.addElement(mdsdev_node, 'obdtype', 'cache')
1272
1273     node = findByName(lustre, node_name, "node")
1274     cobd = gen.cobd(name, uuid, master_uuid, cache_uuid)
1275     lustre.appendChild(cobd)
1276
1277 def add_echo_client(gen, lustre, options):
1278     """ add an echo client to the profile for this node. """
1279     node_name = get_option(options, 'node')
1280     lov_name = get_option(options, 'ost')
1281
1282     node = findByName(lustre, node_name, 'node')
1283
1284     echoname = new_name('ECHO_'+ node_name)
1285     echo_uuid = new_uuid(echoname)
1286     node_add_profile(gen, node, 'echoclient', echo_uuid)
1287
1288     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
1289     if not lov_uuid:
1290         lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
1291
1292     echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
1293     lustre.appendChild(echo)
1294
1295
1296 def add_lov(gen, lustre, options):
1297     """ create a lov """
1298
1299     lmv_name = get_option(options, 'lmv')
1300     lov_orig = get_option(options, 'lov')
1301     name = new_name(lov_orig)
1302     if name != lov_orig:
1303         warning("name:", lov_orig, "already used. using:", name)
1304
1305     mds_name = get_option(options, 'mds')
1306     if not mds_name and not lmv_name:
1307         error("LOV: MDS or LMV must be specified.");
1308
1309     stripe_sz = get_option_int(options, 'stripe_sz')
1310     stripe_cnt = get_option_int(options, 'stripe_cnt')
1311     pattern = get_option_int(options, 'stripe_pattern')
1312     uuid = new_uuid(name)
1313
1314     ret = findByName(lustre, name, "lov")
1315     if ret:
1316         error("LOV: ", name, " already exists.")
1317
1318     if lmv_name:
1319         mds_uuid = name2uuid(lustre, lmv_name, 'lmv')
1320     else:
1321         mds_uuid = name2uuid(lustre, mds_name, 'mds')
1322
1323     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1324     lustre.appendChild(lov)
1325
1326     # add an lovconfig entry to the active mdsdev profile
1327     lovconfig_name = new_name('LVCFG_' + name)
1328     lovconfig_uuid = new_uuid(lovconfig_name)
1329     if mds_name:
1330         mds = findByName(lustre, mds_name, "mds")
1331         mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1332         mds.appendChild(gen.ref("client", uuid))
1333
1334     if lmv_name:
1335         lmv = findByName(lustre, lmv_name, "lmv")
1336         lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1337         lmv.appendChild(gen.ref("client", uuid))
1338
1339     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1340     lustre.appendChild(lovconfig)
1341
1342 def add_default_lov(gen, lustre, mds_name, lov_name):
1343     """ create a default lov """
1344
1345     stripe_sz = DEFAULT_STRIPE_SZ
1346     stripe_cnt = DEFAULT_STRIPE_CNT
1347     pattern = DEFAULT_STRIPE_PATTERN
1348     uuid = new_uuid(lov_name)
1349
1350     ret = findByName(lustre, lov_name, "lov")
1351     if ret:
1352         error("LOV: ", lov_name, " already exists.")
1353
1354     mds_uuid = name2uuid(lustre, mds_name, 'mds')
1355     lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1356     lustre.appendChild(lov)
1357
1358     # add an lovconfig entry to the active mdsdev profile
1359     lovconfig_name = new_name('LVCFG_' + lov_name)
1360     lovconfig_uuid = new_uuid(lovconfig_name)
1361     mds = findByName(lustre, mds_name)
1362     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1363     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1364     lustre.appendChild(lovconfig)
1365
1366 def add_lmv(gen, lustre, options):
1367     """ create a lmv """
1368
1369     lmv_orig = get_option(options, 'lmv')
1370     name = new_name(lmv_orig)
1371     if name != lmv_orig:
1372         warning("name:", lmv_orig, "already used. using:", name)
1373
1374     uuid = new_uuid(name)
1375     ret = findByName(lustre, name, "lmv")
1376     if ret:
1377         error("LMV: ", name, " already exists.")
1378
1379     lmv = gen.lmv(name, uuid)
1380     lustre.appendChild(lmv)
1381     
1382 def find_client(lustre, mds_uuid, client_uuid):
1383     mds = lookup(lustre, mds_uuid)
1384     clients = mds.getElementsByTagName('client_ref')
1385     
1386     if clients:
1387         for client in clients:
1388             if client.getAttribute("uuidref") == client_uuid:
1389                 return 1
1390     return 0
1391     
1392 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
1393     fs_name = new_name("FS_fsname")
1394     fs_uuid = new_uuid(fs_name)
1395     
1396     mds = lookup(lustre, mds_uuid)
1397     clients = mds.getElementsByTagName('client_ref')
1398     
1399     if find_client(lustre, mds_uuid, obd_uuid) == 0:
1400         mds.appendChild(gen.ref("client", obd_uuid))
1401
1402     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, 
1403                         obd_uuid, mgmt_uuid)
1404                         
1405     lustre.appendChild(fs)
1406     return fs_uuid
1407
1408 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
1409     mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0)
1410     if not mds_uuid:
1411         mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0)
1412     if not mds_uuid:
1413         mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=0)
1414     if not mds_uuid:
1415         error("mds '" + mds_name + "' is not found")
1416
1417     obd_uuid = name2uuid(lustre, obd_name, tag='ost', fatal=0)
1418     if not obd_uuid:
1419         obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
1420     if not obd_uuid:
1421         obd_uuid = name2uuid(lustre, obd_name, tag='cobd', fatal=0)
1422     if not obd_uuid:
1423         error("ost '" + obd_name + "' is not found")
1424         
1425     if mgmt_name:
1426         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
1427     else:
1428         mgmt_uuid = ''
1429
1430     fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
1431     if not fs_uuid:
1432         fs_uuid = new_filesystem(gen, lustre, mds_uuid, 
1433                                  obd_uuid, mgmt_uuid)
1434     return fs_uuid
1435
1436 def add_mtpt(gen, lustre, options):
1437     """ create mtpt on a node """
1438     node_name = get_option(options, 'node')
1439
1440     path = get_option(options, 'path')
1441     clientoptions = get_option(options, "clientoptions")
1442     fs_name = get_option(options, 'filesystem')
1443
1444     lov_name = get_option(options, 'lov')
1445     ost_name = get_option(options, 'ost')
1446     mds_name = get_option(options, 'mds')
1447     if mds_name == '':
1448         mds_name = get_option(options, 'lmv')
1449         if mds_name == '':
1450             error("--add mtpt requires either --mds or --lmv.")
1451     if lov_name == '':
1452         if ost_name == '':
1453             error("--add mtpt requires --lov lov_name or --ost ost_name")
1454         else:
1455             warning("use default value for lov, due no --lov lov_name provided")
1456             lov_name = new_name("lov_default")
1457             add_default_lov(gen, lustre, mds_name, lov_name)
1458             ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1459             if not ost_uuid:
1460                 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1461             lov = findByName(lustre, lov_name, "lov")
1462             lov_add_obd(gen, lustre, lov, ost_uuid, options)
1463
1464     if fs_name == '':
1465         mgmt_name = get_option(options, 'mgmt')
1466         fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name)
1467     else:
1468         fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1469
1470     name = new_name('MNT_'+ node_name)
1471
1472     ret = findByName(lustre, name, "mountpoint")
1473     if ret:
1474         # this can't happen, because new_name creates unique names
1475         error("MOUNTPOINT: ", name, " already exists.")
1476
1477     uuid = new_uuid(name)
1478     mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions)
1479     node = findByName(lustre, node_name, "node")
1480     if not node:
1481         error('node:',  node_name, "not found.")
1482     node_add_profile(gen, node, "mountpoint", uuid)
1483     lustre.appendChild(mtpt)
1484
1485 def commit_version(gen, lustre):
1486     update = findLastUpdate(lustre)
1487     if update:
1488         version = int(update.getAttribute("version")) + 1
1489     else:
1490         version = 1
1491
1492     new = gen.update(str(version))
1493     lustre.appendChild(new)
1494     
1495
1496 ############################################################
1497 # Command line processing
1498 #
1499 class OptionError (exceptions.Exception):
1500     def __init__(self, args):
1501         self.args = args
1502
1503 def get_option(options, tag):
1504     """Look for tag in options hash and return the value if set. If not
1505     set, then if return default it is set, otherwise exception."""
1506     if options.__getattr__(tag) != None:
1507         return options.__getattr__(tag)
1508     else:
1509         raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1510
1511 def get_option_int(options, tag):
1512     """Return an integer option.  Raise exception if the value is not an int"""
1513     val = get_option(options, tag)
1514     try:
1515         n = int(val)
1516     except ValueError:
1517         raise OptionError("--%s <num> (value must be integer)" % (tag))
1518     return n
1519
1520 # simple class for profiling
1521 import time
1522 class chrono:
1523     def __init__(self):
1524         self._start = 0
1525     def start(self):
1526         self._stop = 0
1527         self._start = time.time()
1528     def stop(self, msg=''):
1529         self._stop = time.time()
1530         if msg:
1531             self.display(msg)
1532     def dur(self):
1533         return self._stop - self._start
1534     def display(self, msg):
1535         d = self.dur()
1536         str = '%s: %g secs' % (msg, d)
1537         print str
1538
1539 #################################################################
1540 # function cmdlinesplit used to split cmd line from batch file
1541 #
1542 def cmdlinesplit(cmdline):
1543
1544     double_quote  = re.compile(r'"(([^"\\]|\\.)*)"')
1545     single_quote  = re.compile(r"'(.*?)'")
1546     escaped = re.compile(r'\\(.)')
1547     esc_quote = re.compile(r'\\([\\"])')
1548     outside = re.compile(r"""([^\s\\'"]+)""") #" fucking emacs.
1549
1550     arg_list = []
1551     i = 0; arg = None
1552     while i < len(cmdline):
1553         c = cmdline[i]
1554         if c == '"':
1555             match = double_quote.match(cmdline, i)
1556             if not match:
1557                 print "Unmatched double quote:", cmdline
1558                 sys.exit(1)
1559             i = match.end()
1560             if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1561             else:           arg = arg + esc_quote.sub(r'\1', match.group(1))
1562
1563         elif c == "'":
1564             match = single_quote.match(cmdline, i)
1565             if not match:
1566                 print "Unmatched single quote:", cmdline
1567                 sys.exit(1)
1568             i = match.end()
1569             if arg is None: arg = match.group(1)
1570             else:           arg = arg + match.group(1)
1571
1572         elif c == "\\":
1573             match = escaped.match(cmdline, i)
1574             if not match:
1575                 print "Unmatched backslash", cmdline
1576                 sys.exit(1)
1577             i = match.end()
1578             if arg is None: arg = match.group(1)
1579             else:           arg = arg + match.group(1)
1580
1581         elif c in string.whitespace:
1582             if arg != None:
1583                 arg_list.append(str(arg))
1584             arg = None
1585             while i < len(cmdline) and cmdline[i] in string.whitespace:
1586                 i = i + 1
1587         else:
1588             match = outside.match(cmdline, i)
1589             assert match
1590             i = match.end()
1591             if arg is None: arg = match.group()
1592             else:           arg = arg + match.group()
1593
1594     if arg != None: arg_list.append(str(arg))
1595
1596     return arg_list
1597
1598 ############################################################
1599 # Main
1600 #
1601
1602 def add(devtype, gen, lustre, options):
1603     if devtype == 'net':
1604         add_net(gen, lustre, options)
1605     elif devtype == 'mtpt':
1606         add_mtpt(gen, lustre, options)
1607     elif devtype == 'mds':
1608         add_mds(gen, lustre, options)
1609     elif devtype == 'ost':
1610         add_ost(gen, lustre, options)
1611     elif devtype == 'lov':
1612         add_lov(gen, lustre, options)
1613     elif devtype == 'route':
1614         add_route(gen, lustre, options)
1615     elif devtype == 'node':
1616         add_node(gen, lustre, options)
1617     elif devtype == 'echo_client':
1618         add_echo_client(gen, lustre, options)
1619     elif devtype == 'cobd':
1620         add_cobd(gen, lustre, options)
1621     elif devtype == 'cmobd':
1622         add_cmobd(gen, lustre, options)
1623     elif devtype == 'mgmt':
1624         add_mgmt(gen, lustre, options)
1625     elif devtype == 'lmv':
1626         add_lmv(gen, lustre, options)
1627     else:
1628         error("unknown device type:", devtype)
1629
1630 def delete(devtype, gen, lustre, options):
1631     if devtype == 'ost':
1632         del_ost(gen, lustre, options)
1633     elif options.delete:
1634         error("delete not supported for device type:", devtype)
1635     elif options.deactivate:
1636         error("deactivate not supported for device type:", devtype)
1637     else:
1638         error("in delete(), but neither .delete nor .deactivate are set.  Tell CFS.")
1639
1640 def commit(gen, lustre):
1641     commit_version(gen, lustre)
1642
1643 def do_command(gen, lustre, options, args):
1644     if options.add:
1645         add(options.add, gen, lustre, options)
1646     elif options.delete:
1647         delete(options.delete, gen, lustre, options)
1648     elif options.deactivate:
1649         delete(options.deactivate, gen, lustre, options)
1650     elif options.commit:
1651         commit(gen, lustre)
1652     else:
1653         error("Missing command")
1654
1655 def main():
1656     cl = Lustre.Options("lmc", "", lmc_options)
1657     try:
1658         options, args = cl.parse(sys.argv[1:])
1659     except Lustre.OptionError, e:
1660         panic("lmc", e)
1661
1662     if len(args) > 0:
1663         panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1664
1665     if options.reference:
1666         reference()
1667         sys.exit(0)
1668
1669     outFile = '-'
1670
1671     if options.merge:
1672         outFile = options.merge
1673         if os.access(outFile, os.R_OK):
1674             doc = xml.dom.minidom.parse(outFile)
1675         else:
1676             doc = new_lustre(xml.dom.minidom)
1677     elif options.input:
1678         doc = xml.dom.minidom.parse(options.input)
1679     else:
1680         doc = new_lustre(xml.dom.minidom)
1681
1682     if options.output:
1683         outFile = options.output
1684
1685     lustre = doc.documentElement
1686     init_names(lustre)
1687     if lustre.tagName != "lustre":
1688         print "Existing config not valid."
1689         sys.exit(1)
1690
1691     gen = GenConfig(doc)
1692
1693     if options.batch:
1694         fp = open(options.batch)
1695         batchCommands = fp.readlines()
1696         fp.close()
1697         for cmd in batchCommands:
1698             try:
1699                 options, args = cl.parse(cmdlinesplit(cmd))
1700                 if options.merge or options.input or options.output:
1701                     print "The batchfile should not contain --merge, --input or --output."
1702                     sys.exit(1)
1703                 do_command(gen, lustre, options, args)
1704             except OptionError, e:
1705                 panic(cmd, e)
1706             except Lustre.OptionError, e:
1707                 panic(cmd, e)
1708     else:
1709         try:
1710             do_command(gen, lustre, options, args)
1711         except OptionError, e:
1712             panic(string.join(sys.argv),e)
1713         except Lustre.OptionError, e:
1714             panic("lmc", e)
1715
1716     if outFile == '-':
1717         printDoc(doc)
1718     else:
1719         printDoc(doc, open(outFile,"w"))
1720
1721 if __name__ == "__main__":
1722     main()