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