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