Whamcloud - gitweb
land b_hd_sec_client_oss onto 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 --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     master_uuid = name2uuid(lustre, master_name, tag='lov', fatal=0)
1259     cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0)
1260
1261     if not master_uuid or not cache_uuid:
1262         master_uuid = name2uuid(lustre, master_name, tag='ost', fatal=0)
1263         cache_uuid = name2uuid(lustre, cache_name, tag='ost', fatal=0)
1264
1265     if master_uuid:
1266         node = lookup(lustre, master_uuid)
1267         rets = node.getElementsByTagName('lov_tgt')
1268         for ret in rets:
1269             ost_uuid = ret.getAttribute('uuidref')
1270             ost_node = lookup(lustre, ost_uuid)
1271             active = ost_node.getElementsByTagName('active_ref')
1272             if active:
1273                 osd_uuid = active[0].getAttribute('uuidref')
1274                 osd_node = lookup(lustre, osd_uuid)
1275                 if not osd_node.getElementsByTagName('obdtype'):
1276                     gen.addElement(osd_node, 'obdtype', 'master')
1277
1278     if cache_uuid:
1279         node = lookup(lustre, cache_uuid)
1280         rets = node.getElementsByTagName('lov_tgt')
1281         for ret in rets:
1282             ost_uuid = ret.getAttribute('uuidref')
1283             ost_node = lookup(lustre, ost_uuid)
1284             active = ost_node.getElementsByTagName('active_ref')
1285             if active:
1286                 osd_uuid = active[0].getAttribute('uuidref')
1287                 osd_node = lookup(lustre, osd_uuid)
1288                 if not osd_node.getElementsByTagName('obdtype'):
1289                     gen.addElement(osd_node, 'obdtype', 'cache')
1290
1291     if not master_uuid or not cache_uuid:
1292         master_uuid = name2uuid(lustre,master_name, tag='lmv')
1293         cache_uuid = name2uuid(lustre,cache_name, tag='lmv')
1294         if master_uuid:
1295             mds_node = lookup(lustre, master_uuid)
1296             ret = mds_node.getElementsByTagName('active_ref')
1297             if ret:
1298                 mdsdev_uuid = ret[0].getAttribute('uuidref')
1299                 mdsdev_node = lookup(lustre, mdsdev_uuid)
1300                 if not mdsdev_node.getElementsByTagName('obdtype'):
1301                     gen.addElement(mdsdev_node, 'obdtype', 'master')
1302         if cache_uuid:
1303             mds_node = lookup(lustre, cache_uuid)
1304             ret = mds_node.getElementsByTagName('active_ref')
1305             if ret:
1306                 mdsdev_uuid = ret[0].getAttribute('uuidref')
1307                 mdsdev_node = lookup(lustre, mdsdev_uuid)
1308                 if not mdsdev_node.getElementsByTagName('obdtype'):
1309                     gen.addElement(mdsdev_node, 'obdtype', 'cache')
1310
1311     node = findByName(lustre, node_name, "node")
1312     cobd = gen.cobd(name, uuid, master_uuid, cache_uuid)
1313     lustre.appendChild(cobd)
1314
1315 def add_echo_client(gen, lustre, options):
1316     """ add an echo client to the profile for this node. """
1317     node_name = get_option(options, 'node')
1318     lov_name = get_option(options, 'ost')
1319
1320     node = findByName(lustre, node_name, 'node')
1321
1322     echoname = new_name('ECHO_'+ node_name)
1323     echo_uuid = new_uuid(echoname)
1324     node_add_profile(gen, node, 'echoclient', echo_uuid)
1325
1326     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
1327     if not lov_uuid:
1328         lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
1329
1330     echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
1331     lustre.appendChild(echo)
1332
1333
1334 def add_lov(gen, lustre, options):
1335     """ create a lov """
1336
1337     lmv_name = get_option(options, 'lmv')
1338     cache_lmv_name = get_option(options, 'cachelmv')
1339     master_lmv_name = get_option(options, 'masterlmv')
1340     lov_orig = get_option(options, 'lov')
1341     name = new_name(lov_orig)
1342     if name != lov_orig:
1343         warning("name:", lov_orig, "already used. using:", name)
1344
1345     mds_name = get_option(options, 'mds')
1346     if not mds_name and not lmv_name and not cache_lmv_name and not master_lmv_name:
1347         error("LOV: MDS or LMV must be specified.");
1348
1349     stripe_sz = get_option_int(options, 'stripe_sz')
1350     stripe_cnt = get_option_int(options, 'stripe_cnt')
1351     pattern = get_option_int(options, 'stripe_pattern')
1352     uuid = new_uuid(name)
1353
1354     ret = findByName(lustre, name, "lov")
1355     if ret:
1356         error("LOV: ", name, " already exists.")
1357
1358     if lmv_name:
1359         mds_uuid = name2uuid(lustre, lmv_name, 'lmv')
1360     else:
1361         mds_uuid = name2uuid(lustre, mds_name, 'mds')
1362
1363     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1364     lustre.appendChild(lov)
1365
1366     # add an lovconfig entry to the active mdsdev profile
1367     lovconfig_name = new_name('LVCFG_' + name)
1368     lovconfig_uuid = new_uuid(lovconfig_name)
1369     if mds_name:
1370         mds = findByName(lustre, mds_name, "mds")
1371         mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1372         mds.appendChild(gen.ref("client", uuid))
1373
1374     if lmv_name:
1375         lmv = findByName(lustre, lmv_name, "lmv")
1376         lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1377         lmv.appendChild(gen.ref("client", uuid))
1378         
1379
1380     if cache_lmv_name:
1381         lmv = findByName(lustre, cache_lmv_name, "lmv")
1382         lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1383         lmv.appendChild(gen.ref("client", uuid))
1384     
1385     if master_lmv_name:
1386         lmv = findByName(lustre, master_lmv_name, "lmv")
1387         lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1388         lmv.appendChild(gen.ref("client", uuid))
1389
1390     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1391     lustre.appendChild(lovconfig)
1392
1393 def add_default_lov(gen, lustre, mds_name, lov_name):
1394     """ create a default lov """
1395
1396     stripe_sz = DEFAULT_STRIPE_SZ
1397     stripe_cnt = DEFAULT_STRIPE_CNT
1398     pattern = DEFAULT_STRIPE_PATTERN
1399     uuid = new_uuid(lov_name)
1400
1401     ret = findByName(lustre, lov_name, "lov")
1402     if ret:
1403         error("LOV: ", lov_name, " already exists.")
1404
1405     mds_uuid = name2uuid(lustre, mds_name, 'mds')
1406     lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
1407     lustre.appendChild(lov)
1408
1409     # add an lovconfig entry to the active mdsdev profile
1410     lovconfig_name = new_name('LVCFG_' + lov_name)
1411     lovconfig_uuid = new_uuid(lovconfig_name)
1412     mds = findByName(lustre, mds_name)
1413     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
1414     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
1415     lustre.appendChild(lovconfig)
1416
1417 def add_lmv(gen, lustre, options):
1418     """ create a lmv """
1419
1420     lmv_orig = get_option(options, 'lmv')
1421     name = new_name(lmv_orig)
1422     if name != lmv_orig:
1423         warning("name:", lmv_orig, "already used. using:", name)
1424
1425     uuid = new_uuid(name)
1426     ret = findByName(lustre, name, "lmv")
1427     if ret:
1428         error("LMV: ", name, " already exists.")
1429
1430     lmv = gen.lmv(name, uuid)
1431     lustre.appendChild(lmv)
1432     
1433 def find_client(lustre, mds_uuid, client_uuid):
1434     mds = lookup(lustre, mds_uuid)
1435     clients = mds.getElementsByTagName('client_ref')
1436     
1437     if clients:
1438         for client in clients:
1439             if client.getAttribute("uuidref") == client_uuid:
1440                 return 1
1441     return 0
1442     
1443 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
1444     fs_name = new_name("FS_fsname")
1445     fs_uuid = new_uuid(fs_name)
1446     
1447     mds = lookup(lustre, mds_uuid)
1448     clients = mds.getElementsByTagName('client_ref')
1449     
1450     if find_client(lustre, mds_uuid, obd_uuid) == 0:
1451         mds.appendChild(gen.ref("client", obd_uuid))
1452
1453     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, 
1454                         obd_uuid, mgmt_uuid)
1455                         
1456     lustre.appendChild(fs)
1457     return fs_uuid
1458
1459 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
1460     mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0)
1461     if not mds_uuid:
1462         mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0)
1463     if not mds_uuid:
1464         mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=0)
1465     if not mds_uuid:
1466         error("mds '" + mds_name + "' is not found")
1467
1468     obd_uuid = name2uuid(lustre, obd_name, tag='ost', fatal=0)
1469     if not obd_uuid:
1470         obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
1471     if not obd_uuid:
1472         obd_uuid = name2uuid(lustre, obd_name, tag='cobd', fatal=0)
1473     if not obd_uuid:
1474         error("ost '" + obd_name + "' is not found")
1475         
1476     if mgmt_name:
1477         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
1478     else:
1479         mgmt_uuid = ''
1480
1481     fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
1482     if not fs_uuid:
1483         fs_uuid = new_filesystem(gen, lustre, mds_uuid, 
1484                                  obd_uuid, mgmt_uuid)
1485     return fs_uuid
1486
1487 def add_mtpt(gen, lustre, options):
1488     """ create mtpt on a node """
1489     node_name = get_option(options, 'node')
1490
1491     path = get_option(options, 'path')
1492     clientoptions = get_option(options, "clientoptions")
1493     mds_sec = get_option(options, "mds_sec")
1494     oss_sec = get_option(options, "oss_sec")
1495     fs_name = get_option(options, 'filesystem')
1496
1497     lov_name = get_option(options, 'lov')
1498     ost_name = get_option(options, 'ost')
1499     mds_name = get_option(options, 'mds')
1500     if mds_name == '':
1501         mds_name = get_option(options, 'lmv')
1502         if mds_name == '':
1503             error("--add mtpt requires either --mds or --lmv.")
1504     if lov_name == '':
1505         if ost_name == '':
1506             error("--add mtpt requires --lov lov_name or --ost ost_name")
1507         else:
1508             warning("use default value for lov, due no --lov lov_name provided")
1509             lov_name = new_name("lov_default")
1510             add_default_lov(gen, lustre, mds_name, lov_name)
1511             ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
1512             if not ost_uuid:
1513                 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
1514             lov = findByName(lustre, lov_name, "lov")
1515             lov_add_obd(gen, lustre, lov, ost_uuid, options)
1516
1517     if fs_name == '':
1518         mgmt_name = get_option(options, 'mgmt')
1519         fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name)
1520     else:
1521         fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
1522
1523     name = new_name('MNT_'+ node_name)
1524
1525     ret = findByName(lustre, name, "mountpoint")
1526     if ret:
1527         # this can't happen, because new_name creates unique names
1528         error("MOUNTPOINT: ", name, " already exists.")
1529
1530     uuid = new_uuid(name)
1531     mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions, mds_sec, oss_sec)
1532     node = findByName(lustre, node_name, "node")
1533     if not node:
1534         error('node:',  node_name, "not found.")
1535     node_add_profile(gen, node, "mountpoint", uuid)
1536     lustre.appendChild(mtpt)
1537
1538 def commit_version(gen, lustre):
1539     update = findLastUpdate(lustre)
1540     if update:
1541         version = int(update.getAttribute("version")) + 1
1542     else:
1543         version = 1
1544
1545     new = gen.update(str(version))
1546     lustre.appendChild(new)
1547     
1548
1549 ############################################################
1550 # Command line processing
1551 #
1552 class OptionError (exceptions.Exception):
1553     def __init__(self, args):
1554         self.args = args
1555
1556 def get_option(options, tag):
1557     """Look for tag in options hash and return the value if set. If not
1558     set, then if return default it is set, otherwise exception."""
1559     if options.__getattr__(tag) != None:
1560         return options.__getattr__(tag)
1561     else:
1562         raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
1563
1564 def get_option_int(options, tag):
1565     """Return an integer option.  Raise exception if the value is not an int"""
1566     val = get_option(options, tag)
1567     try:
1568         n = int(val)
1569     except ValueError:
1570         raise OptionError("--%s <num> (value must be integer)" % (tag))
1571     return n
1572
1573 # simple class for profiling
1574 import time
1575 class chrono:
1576     def __init__(self):
1577         self._start = 0
1578     def start(self):
1579         self._stop = 0
1580         self._start = time.time()
1581     def stop(self, msg=''):
1582         self._stop = time.time()
1583         if msg:
1584             self.display(msg)
1585     def dur(self):
1586         return self._stop - self._start
1587     def display(self, msg):
1588         d = self.dur()
1589         str = '%s: %g secs' % (msg, d)
1590         print str
1591
1592 #################################################################
1593 # function cmdlinesplit used to split cmd line from batch file
1594 #
1595 def cmdlinesplit(cmdline):
1596
1597     double_quote  = re.compile(r'"(([^"\\]|\\.)*)"')
1598     single_quote  = re.compile(r"'(.*?)'")
1599     escaped = re.compile(r'\\(.)')
1600     esc_quote = re.compile(r'\\([\\"])')
1601     outside = re.compile(r"""([^\s\\'"]+)""") #" fucking emacs.
1602
1603     arg_list = []
1604     i = 0; arg = None
1605     while i < len(cmdline):
1606         c = cmdline[i]
1607         if c == '"':
1608             match = double_quote.match(cmdline, i)
1609             if not match:
1610                 print "Unmatched double quote:", cmdline
1611                 sys.exit(1)
1612             i = match.end()
1613             if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1614             else:           arg = arg + esc_quote.sub(r'\1', match.group(1))
1615
1616         elif c == "'":
1617             match = single_quote.match(cmdline, i)
1618             if not match:
1619                 print "Unmatched single quote:", cmdline
1620                 sys.exit(1)
1621             i = match.end()
1622             if arg is None: arg = match.group(1)
1623             else:           arg = arg + match.group(1)
1624
1625         elif c == "\\":
1626             match = escaped.match(cmdline, i)
1627             if not match:
1628                 print "Unmatched backslash", cmdline
1629                 sys.exit(1)
1630             i = match.end()
1631             if arg is None: arg = match.group(1)
1632             else:           arg = arg + match.group(1)
1633
1634         elif c in string.whitespace:
1635             if arg != None:
1636                 arg_list.append(str(arg))
1637             arg = None
1638             while i < len(cmdline) and cmdline[i] in string.whitespace:
1639                 i = i + 1
1640         else:
1641             match = outside.match(cmdline, i)
1642             assert match
1643             i = match.end()
1644             if arg is None: arg = match.group()
1645             else:           arg = arg + match.group()
1646
1647     if arg != None: arg_list.append(str(arg))
1648
1649     return arg_list
1650
1651 ############################################################
1652 # Main
1653 #
1654
1655 def add(devtype, gen, lustre, options):
1656     if devtype == 'net':
1657         add_net(gen, lustre, options)
1658     elif devtype == 'mtpt':
1659         add_mtpt(gen, lustre, options)
1660     elif devtype == 'mds':
1661         add_mds(gen, lustre, options)
1662     elif devtype == 'ost':
1663         add_ost(gen, lustre, options)
1664     elif devtype == 'lov':
1665         add_lov(gen, lustre, options)
1666     elif devtype == 'route':
1667         add_route(gen, lustre, options)
1668     elif devtype == 'node':
1669         add_node(gen, lustre, options)
1670     elif devtype == 'echo_client':
1671         add_echo_client(gen, lustre, options)
1672     elif devtype == 'cobd':
1673         add_cobd(gen, lustre, options)
1674     elif devtype == 'cmobd':
1675         add_cmobd(gen, lustre, options)
1676     elif devtype == 'mgmt':
1677         add_mgmt(gen, lustre, options)
1678     elif devtype == 'lmv':
1679         add_lmv(gen, lustre, options)
1680     else:
1681         error("unknown device type:", devtype)
1682
1683 def delete(devtype, gen, lustre, options):
1684     if devtype == 'ost':
1685         del_ost(gen, lustre, options)
1686     elif options.delete:
1687         error("delete not supported for device type:", devtype)
1688     elif options.deactivate:
1689         error("deactivate not supported for device type:", devtype)
1690     else:
1691         error("in delete(), but neither .delete nor .deactivate are set.  Tell CFS.")
1692
1693 def commit(gen, lustre):
1694     commit_version(gen, lustre)
1695
1696 def do_command(gen, lustre, options, args):
1697     if options.add:
1698         add(options.add, gen, lustre, options)
1699     elif options.delete:
1700         delete(options.delete, gen, lustre, options)
1701     elif options.deactivate:
1702         delete(options.deactivate, gen, lustre, options)
1703     elif options.commit:
1704         commit(gen, lustre)
1705     else:
1706         error("Missing command")
1707
1708 def main():
1709     cl = Lustre.Options("lmc", "", lmc_options)
1710     try:
1711         options, args = cl.parse(sys.argv[1:])
1712     except Lustre.OptionError, e:
1713         panic("lmc", e)
1714
1715     if len(args) > 0:
1716         panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1717
1718     if options.reference:
1719         reference()
1720         sys.exit(0)
1721
1722     outFile = '-'
1723
1724     if options.merge:
1725         outFile = options.merge
1726         if os.access(outFile, os.R_OK):
1727             doc = xml.dom.minidom.parse(outFile)
1728         else:
1729             doc = new_lustre(xml.dom.minidom)
1730     elif options.input:
1731         doc = xml.dom.minidom.parse(options.input)
1732     else:
1733         doc = new_lustre(xml.dom.minidom)
1734
1735     if options.output:
1736         outFile = options.output
1737
1738     lustre = doc.documentElement
1739     init_names(lustre)
1740     if lustre.tagName != "lustre":
1741         print "Existing config not valid."
1742         sys.exit(1)
1743
1744     gen = GenConfig(doc)
1745
1746     if options.batch:
1747         fp = open(options.batch)
1748         batchCommands = fp.readlines()
1749         fp.close()
1750         for cmd in batchCommands:
1751             try:
1752                 options, args = cl.parse(cmdlinesplit(cmd))
1753                 if options.merge or options.input or options.output:
1754                     print "The batchfile should not contain --merge, --input or --output."
1755                     sys.exit(1)
1756                 do_command(gen, lustre, options, args)
1757             except OptionError, e:
1758                 panic(cmd, e)
1759             except Lustre.OptionError, e:
1760                 panic(cmd, e)
1761     else:
1762         try:
1763             do_command(gen, lustre, options, args)
1764         except OptionError, e:
1765             panic(string.join(sys.argv),e)
1766         except Lustre.OptionError, e:
1767             panic("lmc", e)
1768
1769     if outFile == '-':
1770         printDoc(doc)
1771     else:
1772         printDoc(doc, open(outFile,"w"))
1773
1774 if __name__ == "__main__":
1775     main()