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