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