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