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