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