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