Whamcloud - gitweb
- tagging RC_CURRENT
[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|scimac
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   --dev path
88   --fstype extN|ext3
89   --size size
90   --nspath
91   --journal_size size
92   --inode_size size
93
94 --add lov
95   --lov lov_name
96   --mds mds_name
97   --stripe_sz num
98   --stripe_cnt num
99   --stripe_pattern num
100
101 --add ost
102   --node node_name
103   --ost ost_name 
104   --lov lov_name 
105   --dev path
106   --size size
107   --fstype extN|ext3
108   --journal_size size
109   --inode_size size
110   --obdtype obdecho|obdfilter
111   --ostuuid uuid
112  
113 --add mtpt  - Mountpoint
114   --node node_name
115   --path /mnt/point
116   --mds mds_name
117   --ost ost_name OR --lov lov_name
118
119 --add route
120   --node nodename
121   --gw nid
122   --tgt nid
123   --lo nid
124   --hi nid
125
126 --add echo_client
127   --node nodename
128
129 --add mgmt  - Management/monitoring service
130   --node node_name
131   --mgmt mgmt_service_name
132 """
133
134 PARAM = Lustre.Options.PARAM
135 lmc_options = [
136     # lmc input/output options
137     ('reference', "Print short reference for commands."), 
138     ('verbose,v', "Print system commands as they are run."),
139     ('merge,m', "Append to the specified config file.", PARAM),
140     ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM),
141     ('input,i', "", PARAM),
142     ('batch', "Used to execute lmc commands in batch mode.", PARAM),
143
144     # commands
145     ('add', "", PARAM),
146     
147     # node options
148     ('node', "Add a new node in the cluster configuration.", PARAM),
149     ('timeout', "Set timeout to initiate recovery.", PARAM),
150     ('upcall', "Set both lustre and portals upcall scripts.", PARAM),
151     ('lustre_upcall', "Set location of lustre upcall script.", PARAM),
152     ('portals_upcall', "Set location of portals upcall script.", PARAM),
153     ('ptldebug', "Set the portals debug level",  PARAM),
154     ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log",  PARAM),
155
156     # network 
157     ('nettype', "Specify the network type. This can be tcp/elan/gm/scimac.", PARAM),
158     ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM),
159     ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"),
160     ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT),
161     ('irq_affinity', "Optional argument.", PARAM, 0),
162     ('hostaddr', "", PARAM,""),
163     ('cluster_id', "Specify the cluster ID", PARAM, "0"),
164
165     # routes
166     ('route', "Add a new route for the cluster.", PARAM),
167     ('router', "Optional flag to mark a node as router."),
168     ('gw', "Specify the nid of the gateway for a route.", PARAM),
169     ('gateway_cluster_id', "", PARAM, "0"),
170     ('target_cluster_id', "", PARAM, "0"),
171     ('lo', "For a range route, this is the low value nid.", PARAM),
172     ('hi', "For a range route, this is a hi value nid.", PARAM,""),
173
174     # servers: mds and ost
175     ('mds', "Specify MDS name.", PARAM),
176     ('ost', "Specify the OST name.", PARAM,""),
177     ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"),
178     ('failover', "Enable failover support on OSTs or MDS?"),
179     ('group', "", PARAM),
180     ('dev', "Path of the device on local system.", PARAM,""),
181     ('size', "Specify the size of the device if needed.", PARAM,"0"),
182     ('journal_size', "Specify new journal size for underlying ext3 file system.", PARAM,"0"),
183     ('inode_size', "Specify new inode size for underlying ext3 file system.", PARAM,"0"),
184     ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"),
185     ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""),
186     ('ostuuid', "", PARAM,""),
187     ('nspath', "Local mount point of server namespace.", PARAM,""),
188     ('format', ""),
189
190     # clients: mountpoint and echo
191     ('echo_client', "", PARAM),
192     ('path', "Specify the mountpoint for Lustre.", PARAM),
193     ('filesystem', "Lustre filesystem name", PARAM,""),
194
195     # lov
196     ('lov', "Specify LOV name.", PARAM,""),
197     ('stripe_sz', "Specify the stripe size in bytes.", PARAM),
198     ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0),
199     ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0),
200
201     # cobd
202     ('real_obd', "", PARAM),
203     ('cache_obd', "", PARAM),
204
205     ('mgmt', "Specify management/monitoring service name.", PARAM, ""),
206     ]
207
208 def error(*args):
209     msg = string.join(map(str,args))
210     raise OptionError("Error: " +  msg)
211
212 def panic(cmd, msg):
213     print "! " + cmd
214     print msg
215     sys.exit(1)
216
217     
218 def warning(*args):
219     msg = string.join(map(str,args))
220     print "Warning: ", msg
221     
222 #
223 # manage names and uuids
224 # need to initialize this by walking tree to ensure
225 # no duplicate names or uuids are created.
226 # this are just place holders for now.
227 # consider changing this to be like OBD-dev-host
228 def new_name(base):
229     ctr = 2
230     ret = base
231     while names.has_key(ret):
232         ret = "%s_%d" % (base, ctr)
233         ctr = 1 + ctr
234     names[ret] = 1
235     return ret
236
237 def new_uuid(name):
238     ctr = 2
239     ret = "%s_UUID" % (name)
240     if len(ret) > UUID_MAX_LENGTH:
241         ret = ret[-UUID_MAX_LENGTH:]
242     while uuids.has_key(ret):
243         ret = "%s_UUID_%d" % (name, ctr)
244         ctr = 1 + ctr
245         if len(ret) > UUID_MAX_LENGTH:
246             ret = ret[-UUID_MAX_LENGTH:]
247     uuids[ret] = 1
248     return ret
249
250
251 ldlm_name = 'ldlm'
252 ldlm_uuid = 'ldlm_UUID'
253
254 def new_lustre(dom):
255     """Create a new empty lustre document"""
256     # adding ldlm here is a bit of a hack, but one is enough.
257     str = """<lustre version="%s">
258     <ldlm name="%s" uuid="%s"/>
259     </lustre>""" % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid)
260     return dom.parseString(str)
261
262 names = {}
263 uuids = {}
264
265 def init_names(doc):
266     """initialize auto-name generation tables"""
267     global names, uuids
268     # get all elements that contain a name attribute
269     for n in doc.childNodes:
270         if n.nodeType == n.ELEMENT_NODE:
271             if getName(n):
272                 names[getName(n)] = 1
273                 uuids[getUUID(n)] = 1
274             init_names(n)
275
276 def get_format_flag(options):
277     if options.format:
278         return 'yes'
279     return 'no'
280
281 ############################################################
282 # Build config objects using DOM
283 #
284 class GenConfig:
285     doc = None
286     dom = None
287     def __init__(self, doc):
288         self.doc = doc
289
290     def ref(self, type, uuid):
291         """ generate <[type]_ref uuidref="[uuid]"/> """
292         tag = "%s_ref" % (type)
293         ref = self.doc.createElement(tag)
294         ref.setAttribute("uuidref", uuid)
295         return ref
296     
297     def newService(self, tag, name, uuid):
298         """ create a new  service elmement, which requires name and uuid attributes """
299         new = self.doc.createElement(tag)
300         new.setAttribute("uuid", uuid);
301         new.setAttribute("name", name);
302         return new
303     
304     def addText(self, node, str):
305         txt = self.doc.createTextNode(str)
306         node.appendChild(txt)
307
308     def addElement(self, node, tag, str=None):
309         """ create a new element and add it as a child to node. If str is passed,
310             a text node is created for the new element"""
311         new = self.doc.createElement(tag)
312         if str:
313             self.addText(new, str)
314         node.appendChild(new)
315         return new
316
317     def network(self, name, uuid, nid, cluster_id, net, hostaddr="",
318                 port=0, tcpbuf=0, irq_aff=0):
319         """create <network> node"""
320         network = self.newService("network", name, uuid)
321         network.setAttribute("nettype", net);
322         self.addElement(network, "nid", nid)
323         self.addElement(network, "clusterid", cluster_id)
324         if hostaddr:
325             self.addElement(network, "hostaddr", hostaddr)
326         if port:
327             self.addElement(network, "port", "%d" %(port))
328         if tcpbuf:
329             self.addElement(network, "sendmem", "%d" %(tcpbuf))
330             self.addElement(network, "recvmem", "%d" %(tcpbuf))
331         if irq_aff:
332             self.addElement(network, "irqaffinity", "%d" %(irq_aff))
333             
334         return network
335
336     def routetbl(self, name, uuid):
337         """create <routetbl> node"""
338         rtbl = self.newService("routetbl", name, uuid)
339         return rtbl
340         
341     def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi):
342         """ create one entry for the route table """
343         ref = self.doc.createElement('route')
344         ref.setAttribute("type", gw_net_type)
345         ref.setAttribute("gw", gw)
346         ref.setAttribute("gwclusterid", gw_cluster_id)
347         ref.setAttribute("tgtclusterid", tgt_cluster_id)
348         ref.setAttribute("lo", lo)
349         if hi:
350             ref.setAttribute("hi", hi)
351         return ref
352     
353     def profile(self, name, uuid):
354         """ create a host """
355         profile = self.newService("profile", name, uuid)
356         return profile
357
358     def node(self, name, uuid, prof_uuid):
359         """ create a host """
360         node = self.newService("node", name, uuid)
361         node.appendChild(self.ref("profile", prof_uuid))
362         return node
363
364     def ldlm(self, name, uuid):
365         """ create a ldlm """
366         ldlm = self.newService("ldlm", name, uuid)
367         return ldlm
368
369     def osd(self, name, uuid, fs, osdtype, devname, format, ost_uuid,
370             node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", mkfsoptions=""):
371         osd = self.newService("osd", name, uuid)
372         osd.setAttribute('osdtype', osdtype)
373         osd.appendChild(self.ref("target", ost_uuid))
374         osd.appendChild(self.ref("node", node_uuid))
375         if fs:
376             self.addElement(osd, "fstype", fs)
377         if devname:
378             dev = self.addElement(osd, "devpath", devname)
379             self.addElement(osd, "autoformat", format)
380             if dev_size:
381                 self.addElement(osd, "devsize", "%s" % (dev_size))
382             if journal_size:
383                 self.addElement(osd, "journalsize", "%s" % (journal_size))
384             if inode_size:
385                 self.addElement(osd, "inodesize", "%s" % (inode_size))
386             if mkfsoptions:
387                 self.addElement(osd, "mkfsoptions", mkfsoptions)
388         if nspath:
389             self.addElement(osd, "nspath", nspath)
390         return osd
391
392     def cobd(self, name, uuid, real_uuid, cache_uuid):
393         cobd = self.newService("cobd", name, uuid)
394         cobd.appendChild(self.ref("realobd",real_uuid))
395         cobd.appendChild(self.ref("cacheobd",cache_uuid))
396         return cobd
397
398     def ost(self, name, uuid, osd_uuid, group=""):
399         ost = self.newService("ost", name, uuid)
400         ost.appendChild(self.ref("active", osd_uuid))
401         if group:
402             self.addElement(ost, "group", group)
403         return ost
404
405     def oss(self, name, uuid):
406         oss = self.newService("oss", name, uuid)
407         return oss
408
409     def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern):
410         lov = self.newService("lov", name, uuid)
411         lov.appendChild(self.ref("mds", mds_uuid))
412         lov.setAttribute("stripesize", str(stripe_sz))
413         lov.setAttribute("stripecount", str(stripe_cnt))
414         lov.setAttribute("stripepattern", str(pattern))
415         return lov
416
417     def lovconfig(self, name, uuid, lov_uuid):
418         lovconfig = self.newService("lovconfig", name, uuid)
419         lovconfig.appendChild(self.ref("lov", lov_uuid))
420         return lovconfig
421
422     def mds(self, name, uuid, mdd_uuid, group=""):
423         mds = self.newService("mds", name, uuid)
424         mds.appendChild(self.ref("active",mdd_uuid))
425         if group:
426             self.addElement(mds, "group", group)
427         return mds
428
429     def mdsdev(self, name, uuid, fs, devname, format, node_uuid,
430                mds_uuid, dev_size=0, journal_size=0, inode_size=256,
431                nspath="", mkfsoptions=""):
432         mdd = self.newService("mdsdev", name, uuid)
433         self.addElement(mdd, "fstype", fs)
434         dev = self.addElement(mdd, "devpath", devname)
435         self.addElement(mdd, "autoformat", format)
436         if dev_size:
437                 self.addElement(mdd, "devsize", "%s" % (dev_size))
438         if journal_size:
439             self.addElement(mdd, "journalsize", "%s" % (journal_size))
440         if inode_size:
441             self.addElement(mdd, "inodesize", "%s" % (inode_size))
442         if nspath:
443             self.addElement(mdd, "nspath", nspath)
444         if mkfsoptions:
445             self.addElement(mdd, "mkfsoptions", mkfsoptions)
446         mdd.appendChild(self.ref("node", node_uuid))
447         mdd.appendChild(self.ref("target", mds_uuid))
448         return mdd
449
450     def mgmt(self, mgmt_name, mgmt_uuid, node_uuid):
451         mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid)
452         mgmt.appendChild(self.ref("node", node_uuid))
453         # Placeholder until mgmt-service failover.
454         mgmt.appendChild(self.ref("active", mgmt_uuid))
455         return mgmt
456
457     def mountpoint(self, name, uuid, fs_uuid, path):
458         mtpt = self.newService("mountpoint", name, uuid)
459         mtpt.appendChild(self.ref("filesystem", fs_uuid))
460         self.addElement(mtpt, "path", path)
461         return mtpt
462
463     def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid):
464         fs = self.newService("filesystem", name, uuid)
465         fs.appendChild(self.ref("mds", mds_uuid))
466         fs.appendChild(self.ref("obd", obd_uuid))
467         if mgmt_uuid:
468             fs.appendChild(self.ref("mgmt", mgmt_uuid))
469         return fs
470         
471     def echo_client(self, name, uuid, osc_uuid):
472         ec = self.newService("echoclient", name, uuid)
473         ec.appendChild(self.ref("obd", osc_uuid))
474         return ec
475
476 ############################################################
477 # Utilities to query a DOM tree
478 # Using this functions we can treat use config information
479 # directly as a database.
480 def getName(n):
481     return n.getAttribute('name')
482
483 def getUUID(node):
484     return node.getAttribute('uuid')
485
486
487 def findByName(lustre, name, tag = ""):
488     for n in lustre.childNodes:
489         if n.nodeType == n.ELEMENT_NODE:
490             if tag and n.nodeName != tag:
491                 continue
492             if getName(n) == name:
493                 return n
494             else:
495                 n = findByName(n, name)
496                 if n: return n
497     return None
498
499
500 def lookup(node, uuid):
501     for n in node.childNodes:
502         if n.nodeType == n.ELEMENT_NODE:
503             if getUUID(n) == uuid:
504                 return n
505             else:
506                 n = lookup(n, uuid)
507                 if n: return n
508     return None
509
510
511 def name2uuid(lustre, name, tag="",  fatal=1):
512     ret = findByName(lustre, name, tag)
513     if not ret:
514         if fatal:
515             error('name2uuid:', '"'+name+'"', tag, 'element not found.')
516         else:
517             return ""
518     return getUUID(ret)
519     
520 def lookup_filesystem(lustre, mds_uuid, ost_uuid):
521     for n in lustre.childNodes:
522         if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem':
523             if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid):
524                 return getUUID(n)
525     return None
526
527 # XXX: assumes only one network element per node. will fix this
528 # as soon as support for routers is added
529 def get_net_uuid(lustre, node_name):
530     """ get a network uuid for a node_name """
531     node = findByName(lustre, node_name, "node")
532     if not node:
533         error ('get_net_uuid:', '"'+node_name+'"', "node element not found.")
534     net = node.getElementsByTagName('network')
535     if net:
536         return getUUID(net[0])
537     return None
538
539
540 def lov_add_obd(gen, lov, osc_uuid):
541     lov.appendChild(gen.ref("obd", osc_uuid))
542                             
543 def ref_exists(profile, uuid):
544     elist = profile.childNodes
545     for e in elist:
546         if e.nodeType == e.ELEMENT_NODE:
547             ref = e.getAttribute('uuidref')
548             if ref == uuid:
549                 return 1
550     return 0
551         
552 # ensure that uuid is not already in the profile
553 # return true if uuid is added
554 def node_add_profile(gen, node, ref, uuid):
555     refname = "%s_ref" % "profile"
556     ret = node.getElementsByTagName(refname)
557     if not ret:
558         error('node has no profile ref:', node)
559     prof_uuid = ret[0].getAttribute('uuidref')
560     profile = lookup(node.parentNode, prof_uuid)
561     if not profile:
562         error("no profile found:", prof_uuid)
563     if ref_exists(profile, uuid):
564         return 0
565     profile.appendChild(gen.ref(ref, uuid))
566     return 1
567     
568 def get_attr(dom_node, attr, default=""):
569     v = dom_node.getAttribute(attr)
570     if v:
571         return v
572     return default
573
574 ############################################################
575 # Top level commands
576 #
577 def set_node_options(gen, node, options):
578     if options.router:
579         node.setAttribute('router', '1')
580     if options.timeout:
581         gen.addElement(node, "timeout", get_option(options, 'timeout'))
582     if options.upcall:
583         default_upcall =  get_option(options, 'upcall')
584     else:
585         default_upcall = ''
586     if default_upcall or options.lustre_upcall:
587         if options.lustre_upcall:
588             gen.addElement(node, 'lustreUpcall', options.lustre_upcall)
589         else: 
590             gen.addElement(node, 'lustreUpcall', default_upcall)
591     if default_upcall or options.portals_upcall:
592         if options.portals_upcall:
593             gen.addElement(node, 'portalsUpcall', options.portals_upcall)
594         else:
595             gen.addElement(node, 'portalsUpcall', default_upcall)
596     if options.ptldebug:
597         gen.addElement(node, "ptldebug", get_option(options, 'ptldebug'))
598     if options.subsystem:
599         gen.addElement(node, "subsystem", get_option(options, 'subsystem'))
600     return node
601
602 def do_add_node(gen, lustre,  options, node_name):
603     uuid = new_uuid(node_name)
604     prof_name = new_name("PROFILE_" + node_name)
605     prof_uuid = new_uuid(prof_name)
606     profile = gen.profile(prof_name, prof_uuid)
607     node = gen.node(node_name, uuid, prof_uuid)
608     lustre.appendChild(node)
609     lustre.appendChild(profile)
610
611     node_add_profile(gen, node, 'ldlm', ldlm_uuid)
612     set_node_options(gen, node, options)
613     return node
614
615     
616 def add_node(gen, lustre, options):
617     """ create a node with a network config """
618
619     node_name = get_option(options, 'node')
620     ret = findByName(lustre, node_name, "node")
621     if ret:
622         print "Node:", node_name, "exists."
623         return
624     do_add_node(gen, lustre, options, node_name)
625
626
627 def add_net(gen, lustre, options):
628     """ create a node with a network config """
629
630     node_name = get_option(options, 'node')
631     nid = get_option(options, 'nid')
632     cluster_id = get_option(options, 'cluster_id')
633     hostaddr = get_option(options, 'hostaddr')
634     net_type = get_option(options, 'nettype')
635
636     if net_type in ('tcp',):
637         port = get_option_int(options, 'port')
638         tcpbuf = get_option_int(options, 'tcpbuf')
639         irq_aff = get_option_int(options, 'irq_affinity')
640     elif net_type in ('elan', 'gm', 'scimac'):
641         port = 0
642         tcpbuf = 0
643         irq_aff = 0
644     else:
645         print "Unknown net_type: ", net_type
646         sys.exit(2)
647
648     ret = findByName(lustre, node_name, "node")
649     if not ret:
650         node = do_add_node(gen, lustre, options, node_name)
651     else:
652         node = ret
653         set_node_options(gen, node, options)
654
655     net_name = new_name('NET_'+ node_name +'_'+ net_type)
656     net_uuid = new_uuid(net_name)
657     node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type,
658                                  hostaddr, port, tcpbuf, irq_aff))
659     node_add_profile(gen, node, "network", net_uuid)
660
661
662 def add_route(gen, lustre, options):
663     """ create a node with a network config """
664
665     node_name = get_option(options, 'node')
666     gw_net_type = get_option(options, 'nettype')
667     gw = get_option(options, 'gw')
668     gw_cluster_id = get_option(options, 'gateway_cluster_id')
669     tgt_cluster_id = get_option(options, 'target_cluster_id')
670     lo = get_option(options, 'lo')
671     hi = get_option(options, 'hi')
672     if not hi:
673         hi = lo
674
675     node = findByName(lustre, node_name, "node")
676     if not node:
677         error (node_name, " not found.")
678     
679     rlist = node.getElementsByTagName('routetbl')
680     if len(rlist) > 0:
681         rtbl = rlist[0]
682     else:
683         rtbl_name = new_name("RTBL_" + node_name)
684         rtbl_uuid = new_uuid(rtbl_name)
685         rtbl = gen.routetbl(rtbl_name, rtbl_uuid)
686         node.appendChild(rtbl)
687         node_add_profile(gen, node, "routetbl", rtbl_uuid)
688     rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id,
689                                lo, hi))
690
691
692 def add_mds(gen, lustre, options):
693     node_name = get_option(options, 'node')
694     mds_name = get_option(options, 'mds')
695     mdd_name = new_name("MDD_" + mds_name +"_" + node_name)
696     mdd_uuid = new_uuid(mdd_name)
697
698     mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0)
699     if not mds_uuid:
700         mds_uuid = new_uuid(mds_name)
701         mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group)
702         lustre.appendChild(mds)
703     else:
704         mds = lookup(lustre, mds_uuid)
705     if options.failover:
706         mds.setAttribute('failover', "1")
707
708     devname = get_option(options, 'dev')
709     size = get_option(options, 'size')
710     fstype = get_option(options, 'fstype')
711     journal_size = get_option(options, 'journal_size')
712     inode_size = get_option(options, 'inode_size')
713     nspath = get_option(options, 'nspath')
714     mkfsoptions = get_option(options, 'mkfsoptions')
715
716     node_uuid = name2uuid(lustre, node_name, 'node')
717
718     node = findByName(lustre, node_name, "node")
719     node_add_profile(gen, node, "mdsdev", mdd_uuid)
720     net_uuid = get_net_uuid(lustre, node_name)
721     if not net_uuid:
722         error("NODE: ", node_name, "not found")
723
724     mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname,
725                      get_format_flag(options), node_uuid, mds_uuid,
726                      size, journal_size, inode_size, nspath, mkfsoptions)
727     lustre.appendChild(mdd)
728                    
729
730 def add_mgmt(gen, lustre, options):
731     node_name = get_option(options, 'node')
732     node_uuid = name2uuid(lustre, node_name, 'node')
733     mgmt_name = get_option(options, 'mgmt')
734     if not mgmt_name:
735         mgmt_name = new_name('MGMT_' + node_name)
736     mgmt_uuid = name2uuid(lustre, mgmt_name, 'mgmt', fatal=0)
737     if not mgmt_uuid:
738         mgmt_uuid = new_uuid(mgmt_name)
739         mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid)
740         lustre.appendChild(mgmt)
741     else:
742         mgmt = lookup(lustre, mgmt_uuid)
743
744     node = findByName(lustre, node_name, "node")
745     node_add_profile(gen, node, 'mgmt', mgmt_uuid)
746
747 def add_ost(gen, lustre, options):
748     node_name = get_option(options, 'node')
749     lovname = get_option(options, 'lov')
750     osdtype = get_option(options, 'osdtype')
751
752     node_uuid = name2uuid(lustre, node_name, 'node')
753
754     if osdtype == 'obdecho':
755         fstype = ''
756         devname = ''
757         size = 0
758         fstype = ''
759         journal_size = ''
760         inode_size = ''
761         mkfsoptions = ''
762     else:
763         devname = get_option(options, 'dev') # can be unset for bluearcs
764         size = get_option(options, 'size')
765         fstype = get_option(options, 'fstype')
766         journal_size = get_option(options, 'journal_size')
767         inode_size = get_option(options, 'inode_size')
768         mkfsoptions = get_option(options, 'mkfsoptions')
769         
770     nspath = get_option(options, 'nspath')
771
772     ostname = get_option(options, 'ost')
773     if not ostname:
774         ostname = new_name('OST_'+ node_name)
775
776     osdname = new_name("OSD_" + ostname + "_" + node_name)
777     osd_uuid = new_uuid(osdname)
778
779     ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0)
780     if not ost_uuid:
781         ost_uuid = get_option(options, 'ostuuid')
782         if ost_uuid:
783             if lookup(lustre, ost_uuid):
784                 error("Duplicate OST UUID:", ost_uuid)
785         else:
786             ost_uuid = new_uuid(ostname)
787
788         ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group)
789         lustre.appendChild(ost)
790         if lovname:
791             lov = findByName(lustre, lovname, "lov")
792             if not lov:
793                 error('add_ost:', '"'+lovname+'"', "lov element not found.")
794             lov_add_obd(gen, lov, ost_uuid)
795     else:
796         ost = lookup(lustre, ost_uuid)
797
798     if options.failover:
799         ost.setAttribute('failover', "1")
800     
801
802     osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname,
803                   get_format_flag(options), ost_uuid, node_uuid, size,
804                   journal_size, inode_size, nspath, mkfsoptions)
805
806     node = findByName(lustre, node_name, "node")
807
808 ##     if node_add_profile(gen, node, 'oss', oss_uuid):
809 ##         ossname = 'OSS'
810 ##         oss_uuid = new_uuid(ossname)
811 ##         oss = gen.oss(ossname, oss_uuid)
812 ##         lustre.appendChild(oss)
813
814     node_add_profile(gen, node, 'osd', osd_uuid)
815     lustre.appendChild(osd)
816
817                    
818 def add_cobd(gen, lustre, options):
819     node_name = get_option(options, 'node')
820     name = new_name('COBD_' + node_name)
821     uuid = new_uuid(name)
822
823     real_name = get_option(options, 'real_obd')
824     cache_name = get_option(options, 'cache_obd')
825     
826     real_uuid = name2uuid(lustre, real_name, tag='obd')
827     cache_uuid = name2uuid(lustre, cache_name, tag='obd')
828
829     node = findByName(lustre, node_name, "node")
830     node_add_profile(gen, node, "cobd", uuid)
831     cobd = gen.cobd(name, uuid, real_uuid, cache_uuid)
832     lustre.appendChild(cobd)
833
834
835 def add_echo_client(gen, lustre, options):
836     """ add an echo client to the profile for this node. """
837     node_name = get_option(options, 'node')
838     lov_name = get_option(options, 'ost')
839
840     node = findByName(lustre, node_name, 'node')
841
842     echoname = new_name('ECHO_'+ node_name)
843     echo_uuid = new_uuid(echoname)
844     node_add_profile(gen, node, 'echoclient', echo_uuid)
845
846     lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0)
847     if not lov_uuid:
848         lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1)
849
850     echo = gen.echo_client(echoname, echo_uuid, lov_uuid)
851     lustre.appendChild(echo)
852
853
854 def add_lov(gen, lustre, options):
855     """ create a lov """
856
857     lov_orig = get_option(options, 'lov')
858     name = new_name(lov_orig)
859     if name != lov_orig:
860         warning("name:", lov_orig, "already used. using:", name)
861
862     mds_name = get_option(options, 'mds')
863     stripe_sz = get_option_int(options, 'stripe_sz')
864     stripe_cnt = get_option_int(options, 'stripe_cnt')
865     pattern = get_option_int(options, 'stripe_pattern')
866     uuid = new_uuid(name)
867
868     ret = findByName(lustre, name, "lov")
869     if ret:
870         error("LOV: ", name, " already exists.")
871
872     mds_uuid = name2uuid(lustre, mds_name, 'mds')
873     lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
874     lustre.appendChild(lov)
875     
876     # add an lovconfig entry to the active mdsdev profile
877     lovconfig_name = new_name('LVCFG_' + name)
878     lovconfig_uuid = new_uuid(lovconfig_name)
879     mds = findByName(lustre, mds_name, "mds")
880     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
881     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
882     lustre.appendChild(lovconfig)
883
884 def add_default_lov(gen, lustre, mds_name, lov_name):
885     """ create a default lov """
886                                                                                                                                                
887     stripe_sz = DEFAULT_STRIPE_SZ
888     stripe_cnt = DEFAULT_STRIPE_CNT
889     pattern = DEFAULT_STRIPE_PATTERN
890     uuid = new_uuid(lov_name)
891                                                                                                                                                
892     ret = findByName(lustre, lov_name, "lov")
893     if ret:
894         error("LOV: ", lov_name, " already exists.")
895                                                                                                                                                
896     mds_uuid = name2uuid(lustre, mds_name, 'mds')
897     lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern)
898     lustre.appendChild(lov)
899                                                                                                                                                
900     # add an lovconfig entry to the active mdsdev profile
901     lovconfig_name = new_name('LVCFG_' + lov_name)
902     lovconfig_uuid = new_uuid(lovconfig_name)
903     mds = findByName(lustre, mds_name)
904     mds.appendChild(gen.ref("lovconfig", lovconfig_uuid))
905     lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid)
906     lustre.appendChild(lovconfig)
907
908 def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid):
909     fs_name = new_name("FS_fsname")
910     fs_uuid = new_uuid(fs_name)
911     mds = lookup(lustre, mds_uuid)
912     mds.appendChild(gen.ref("filesystem", fs_uuid))
913     fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid)
914     lustre.appendChild(fs)
915     return fs_uuid
916
917 def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name):
918     mds_uuid = name2uuid(lustre, mds_name, tag='mds')
919     obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0)
920     if mgmt_name:
921         mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1)
922     else:
923         mgmt_uuid = ''
924     fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid)
925     if not fs_uuid:
926         fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid)
927     return fs_uuid
928     
929 def add_mtpt(gen, lustre, options):
930     """ create mtpt on a node """
931     node_name = get_option(options, 'node')
932
933     path = get_option(options, 'path')
934     fs_name = get_option(options, 'filesystem')
935
936     lov_name = get_option(options, 'lov')
937     ost_name = get_option(options, 'ost')
938     mds_name = get_option(options, 'mds')
939     if lov_name == '':
940         if ost_name == '':
941             error("--add mtpt requires --lov lov_name or --ost ost_name")
942         else:
943             warning("use default value for lov, due no --lov lov_name provided")
944             lov_name = new_name("lov_default")
945             add_default_lov(gen, lustre, mds_name, lov_name)
946             ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0)
947             if not ost_uuid:
948                 error('add_mtpt:', '"'+ost_name+'"', "ost element not found.")
949             lov = findByName(lustre, lov_name, "lov")
950             lov_add_obd(gen, lov, ost_uuid)
951
952     if fs_name == '':
953         mgmt_name = get_option(options, 'mgmt')
954         fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name)
955     else:
956         fs_uuid = name2uuid(lustre, fs_name, tag='filesystem')
957
958     name = new_name('MNT_'+ node_name)
959
960     ret = findByName(lustre, name, "mountpoint")
961     if ret:
962         # this can't happen, because new_name creates unique names
963         error("MOUNTPOINT: ", name, " already exists.")
964
965     uuid = new_uuid(name)
966     mtpt = gen.mountpoint(name, uuid, fs_uuid, path)
967     node = findByName(lustre, node_name, "node")
968     if not node:
969         error('node:',  node_name, "not found.")
970     node_add_profile(gen, node, "mountpoint", uuid)
971     lustre.appendChild(mtpt)
972
973 ############################################################
974 # Command line processing
975 #
976 class OptionError (exceptions.Exception):
977     def __init__(self, args):
978         self.args = args
979
980 def get_option(options, tag):
981     """Look for tag in options hash and return the value if set. If not
982     set, then if return default it is set, otherwise exception."""
983     if options.__getattr__(tag) != None:
984         return options.__getattr__(tag)
985     else:
986         raise OptionError("--add %s requires --%s <value>" % (options.add, tag))
987
988 def get_option_int(options, tag):
989     """Return an integer option.  Raise exception if the value is not an int"""
990     val = get_option(options, tag)
991     try:
992         n = int(val)
993     except ValueError:
994         raise OptionError("--%s <num> (value must be integer)" % (tag))        
995     return n
996
997 # simple class for profiling
998 import time
999 class chrono:
1000     def __init__(self):
1001         self._start = 0
1002     def start(self):
1003         self._stop = 0
1004         self._start = time.time()
1005     def stop(self, msg=''):
1006         self._stop = time.time()
1007         if msg:
1008             self.display(msg)
1009     def dur(self):
1010         return self._stop - self._start
1011     def display(self, msg):
1012         d = self.dur()
1013         str = '%s: %g secs' % (msg, d)
1014         print str
1015
1016 #################################################################
1017 # function cmdlinesplit used to split cmd line from batch file
1018 #
1019 def cmdlinesplit(cmdline):
1020                                                                                                                                                
1021     double_quote  = re.compile(r'"(([^"\\]|\\.)*)"')
1022     single_quote  = re.compile(r"'(.*?)'")
1023     escaped = re.compile(r'\\(.)')
1024     esc_quote = re.compile(r'\\([\\"])')
1025     outside = re.compile(r"""([^\s\\'"]+)""")
1026                                                                                                                                                
1027     arg_list = []
1028     i = 0; arg = None
1029     while i < len(cmdline):
1030         c = cmdline[i]
1031         if c == '"':
1032             match = double_quote.match(cmdline, i)
1033             if not match:
1034                 print "Unmatched double quote:", cmdline
1035                 sys.exit(1)
1036             i = match.end()
1037             if arg is None: arg = esc_quote.sub(r'\1', match.group(1))
1038             else:           arg = arg + esc_quote.sub(r'\1', match.group(1))
1039                                                                                                                                                
1040         elif c == "'":
1041             match = single_quote.match(cmdline, i)
1042             if not match:
1043                 print "Unmatched single quote:", cmdline
1044                 sys.exit(1)
1045             i = match.end()
1046             if arg is None: arg = match.group(1)
1047             else:           arg = arg + match.group(1)
1048                                                                                                                                                
1049         elif c == "\\":
1050             match = escaped.match(cmdline, i)
1051             if not match:
1052                 print "Unmatched backslash", cmdline
1053                 sys.exit(1)
1054             i = match.end()
1055             if arg is None: arg = match.group(1)
1056             else:           arg = arg + match.group(1)
1057                                                                                                                                                
1058         elif c in string.whitespace:
1059             if arg != None:
1060                 arg_list.append(str(arg))
1061             arg = None
1062             while i < len(cmdline) and cmdline[i] in string.whitespace:
1063                 i = i + 1
1064         else:
1065             match = outside.match(cmdline, i)
1066             assert match
1067             i = match.end()
1068             if arg is None: arg = match.group()
1069             else:           arg = arg + match.group()
1070                                                                                                                                                
1071     if arg != None: arg_list.append(str(arg))
1072                                                                                                                                                
1073     return arg_list
1074
1075 ############################################################
1076 # Main
1077 #
1078
1079 def add(devtype, gen, lustre, options):
1080     if devtype == 'net':
1081         add_net(gen, lustre, options)
1082     elif devtype == 'mtpt':
1083         add_mtpt(gen, lustre, options)
1084     elif devtype == 'mds':
1085         add_mds(gen, lustre, options)
1086     elif devtype == 'ost':
1087         add_ost(gen, lustre, options)
1088     elif devtype == 'lov':
1089         add_lov(gen, lustre, options)
1090     elif devtype == 'route':
1091         add_route(gen, lustre, options)
1092     elif devtype == 'node':
1093         add_node(gen, lustre, options)
1094     elif devtype == 'echo_client':
1095         add_echo_client(gen, lustre, options)
1096     elif devtype == 'cobd':
1097         add_cobd(gen, lustre, options)
1098     elif devtype == 'mgmt':
1099         add_mgmt(gen, lustre, options)
1100     else:
1101         error("unknown device type:", devtype)
1102     
1103 def do_command(gen, lustre, options, args):
1104     if options.add:
1105         add(options.add, gen, lustre, options)
1106     else:
1107         error("Missing command")
1108
1109 def main():
1110     cl = Lustre.Options("lmc", "", lmc_options)
1111     try:
1112         options, args = cl.parse(sys.argv[1:])
1113     except Lustre.OptionError, e:
1114         panic("lmc", e)
1115
1116     if len(args) > 0:
1117         panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args))
1118
1119     if options.reference:
1120         reference()
1121         sys.exit(0)
1122
1123     outFile = '-'
1124
1125     if options.merge:
1126         outFile = options.merge
1127         if os.access(outFile, os.R_OK):
1128             doc = xml.dom.minidom.parse(outFile)
1129         else:
1130             doc = new_lustre(xml.dom.minidom)
1131     elif options.input:
1132         doc = xml.dom.minidom.parse(options.input)
1133     else:
1134         doc = new_lustre(xml.dom.minidom)
1135
1136     if options.output:
1137         outFile = options.output
1138
1139     lustre = doc.documentElement
1140     init_names(lustre)
1141     if lustre.tagName != "lustre":
1142         print "Existing config not valid."
1143         sys.exit(1)
1144
1145     gen = GenConfig(doc)
1146
1147     if options.batch:
1148         fp = open(options.batch)
1149         batchCommands = fp.readlines()
1150         fp.close()
1151         for cmd in batchCommands:
1152             try:
1153                 options, args = cl.parse(cmdlinesplit(cmd))
1154                 if options.merge or options.input or options.output:
1155                         print "The batchfile should not contain --merge, --input or --output."
1156                         sys.exit(1)
1157                 do_command(gen, lustre, options, args)
1158             except OptionError, e:
1159                 panic(cmd, e)
1160             except Lustre.OptionError, e:
1161                 panic(cmd, e)
1162     else:
1163         try:
1164             do_command(gen, lustre, options, args)
1165         except OptionError, e:
1166             panic(string.join(sys.argv),e)
1167         except Lustre.OptionError, e:
1168             panic("lmc", e)
1169
1170     if outFile == '-':
1171         printDoc(doc)
1172     else:
1173         printDoc(doc, open(outFile,"w"))
1174
1175 if __name__ == "__main__":
1176     main()