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