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