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