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