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