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