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