3 # Copyright (C) 2002 Cluster File Systems, Inc.
4 # Author: Robert Read <rread@clusterfs.com>
6 # This file is part of Lustre, http://www.lustre.org.
8 # Lustre is free software; you can redistribute it and/or
9 # modify it under the terms of version 2 of the GNU General Public
10 # License as published by the Free Software Foundation.
12 # Lustre is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Lustre; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 # lconf - lustre configuration tool
23 # lconf is the main driver script for starting and stopping
24 # lustre filesystem services.
26 # Based in part on the XML obdctl modifications done by Brian Behlendorf
29 import string, os, stat
31 import xml.dom.minidom
34 raise RuntimeError, 'This feature not implmemented yet.'
37 raise RuntimeError, msg
40 # Maximum number of devices to search for.
41 # (the /dev/loop* nodes need to be created beforehand)
42 MAX_LOOP_DEVICES = 256
46 print """usage: lconf --ldap server | config.xml
48 config.xml Lustre configuration in xml format.
49 --ldap server LDAP server with lustre config database
52 --reformat Reformat all devices (will confirm)
53 --dev="lustre src" Base directory of lustre sources. Used to search
55 --portals=src Portals source
56 --makeldiff Translate xml source to LDIFF
57 --cleanup Cleans up config. (Shutdown)
60 (SCRIPT STILL UNDER DEVELOPMENT, MOST FUNCTIONALITY UNIMPLEMENTED)
63 # ============================================================
64 # Various system-level functions
65 # (ideally moved to their own module)
67 # Run a command and return the output and status.
68 # stderr is sent to /dev/null, could use popen3 to
69 # save it if necessary
71 cmd = string.join(map(str,args))
73 f = os.popen(cmd + ' 2> /dev/null')
82 # is the path a block device?
89 return stat.S_ISBLK(s[stat.ST_MODE])
91 # build fs according to type
93 def mkfs(fstype, dev):
96 elif (fstype == 'extN'):
99 print 'unsupported fs type: ', fstype
100 if not is_block(dev):
104 run (mkfs, force, dev)
106 # some systems use /dev/loopN, some /dev/loop/N
110 if not os.access(loop + str(0), os.R_OK):
112 if not os.access(loop + str(0), os.R_OK):
113 panic ("can't access loop devices")
116 # find loop device assigned to thefile
119 for n in xrange(0, MAX_LOOP_DEVICES):
121 if os.access(dev, os.R_OK):
122 (out, stat) = run('losetup', dev)
124 m = re.search(r'\((.*)\)', out[0])
125 if m and file == m.group(1):
131 # create file if necessary and assign the first free loop device
132 def init_loop(file, size, fstype):
133 dev = find_loop(file)
135 print 'WARNING file:', file, 'already mapped to', dev
137 if not os.access(file, os.R_OK | os.W_OK):
138 run("dd if=/dev/zero bs=1k count=0 seek=%d of=%s" %(size, file))
140 # find next free loop
141 for n in xrange(0, MAX_LOOP_DEVICES):
143 if os.access(dev, os.R_OK):
144 (out, stat) = run('losetup', dev)
146 run('losetup', dev, file)
149 print "out of loop devices"
151 print "out of loop devices"
154 # undo loop assignment
155 def clean_loop(file):
156 dev = find_loop(file)
158 run('losetup -d', dev)
160 # ============================================================
161 # Functions to prepare the various objects
163 def prepare_ldlm(node):
166 def prepare_network(node):
167 print 'prepare network'
169 # need to check /proc/mounts and /etc/mtab before
170 # formatting anything.
171 # FIXME: check if device is already formatted.
172 def prepare_obd(obd):
173 obdname = obd.getAttribute('name')
174 (dev, size, fstype, format) = getDeviceInfo(obd)
175 print "OBD: ", dev, size, fstype, format
176 ## if not is_block(dev):
177 ## dev = init_loop(dev, size, fstype)
178 ## if (format == 'yes'):
181 def prepare_ost(node):
184 def prepare_mds(node):
187 def prepare_osc(node):
190 def prepare_mdc(node):
193 def prepare_mountpoint(node):
196 # ============================================================
199 # extract device attributes for an obd
200 def getDeviceInfo(obd):
201 dev = obd.getElementsByTagName('device')[0]
204 size = int(dev.getAttribute('size'))
208 fstype = getText(obd, 'fstype')
209 format = getText(obd, 'autoformat')
210 return (dev.firstChild.data, size, fstype, format)
212 # Get the text content from the first matching child
213 def getText(node, tag):
214 node = node.getElementsByTagName(tag)[0]
216 return node.firstChild.data
218 # Recusively search for a particular node by uuid
219 def getByUUID(node, uuid):
221 for n in node.childNodes:
222 if n.nodeType == n.ELEMENT_NODE:
223 if getUUID(n) == uuid:
227 # Recusively search for a particular node by name
228 def getByName(node, name):
229 for n in node.childNodes:
230 # this service_id check is ugly. need some other way to
231 # differentiate between definitions and references
232 if n.nodeType == n.ELEMENT_NODE and n.nodeName != 'service_id':
233 if getName(n) == name:
236 n = getByName(n, name)
241 # Get name attribute of node
243 return node.getAttribute('name')
245 # Get name attribute of node
247 return node.getAttribute('uuid')
249 # the tag name is the service type
250 # fixme: this should do some checks to make sure the node is a service
251 def getServiceType(node):
255 # determine what "level" a particular node is at.
256 # the order of iniitailization is based on level. objects
257 # are assigned a level based on type:
258 # net,devices:1, obd, mdd:2 mds,ost:3 osc,mdc:4 mounts:5
259 def getServiceLevel(node):
260 type = getServiceType(node)
261 if type in ('network', 'device', 'ldlm'):
263 elif type in ('obd', 'mdd'):
265 elif type in ('mds','ost'):
267 elif type in ('mdc','osc'):
269 elif type in ('mountpoint',):
274 # return list of services in a profile. list is a list of tuples
276 def getServices(lustreNode, profileNode):
278 for n in profileNode.childNodes:
279 if n.nodeType == n.ELEMENT_NODE:
280 servNode = getByName(lustreNode, getName(n))
282 panic('service not found: ' + servNode)
283 level = getServiceLevel(servNode)
284 list.append((level, servNode))
288 def getProfile(lustreNode, profile):
289 profList = lustreNode.getElementsByTagName('profile')
290 for prof in profList:
291 if getName(prof) == profile:
297 def startService(node):
298 type = getServiceType(node)
299 print 'Starting service:', type, getName(node), getUUID(node)
300 # there must be a more dynamic way of doing this...
303 elif type == 'network':
304 prepare_network(node)
315 elif type == 'mountpoint':
316 prepare_mountpoint(node)
319 # Prepare the system to run lustre using a particular profile
320 # in a the configuration.
321 # * load & the modules
322 # * setup networking for the current node
323 # * make sure partitions are in place and prepared
324 # * initialize devices with lctl
325 # Levels is important, and needs to be enforced.
326 def startProfile(lustreNode, profile):
327 profileNode = getProfile(lustreNode, profile)
329 print "profile:", profile, "not found."
331 services = getServices(lustreNode, profileNode)
335 #obdlist = lustreNode.getElementsByTagName("obd")
340 # Initialize or shutdown lustre according to a configuration file
341 # * prepare the system for lustre
342 # * configure devices with lctl
343 # Shutdown does steps in reverse
346 dom = xml.dom.minidom.parse(sys.argv[1])
347 startProfile(dom.childNodes[0], 'local-profile')
350 # try a different traceback style. (dare ya to try this in java)
351 def my_traceback(file=None):
352 """Print the list of tuples as returned by extract_tb() or
353 extract_stack() as a formatted stack trace to the given file."""
355 (t,v, tb) = sys.exc_info()
356 list = traceback.extract_tb(tb)
359 for filename, lineno, name, line in list:
361 print '%s:%04d %-14s %s' % (filename,lineno, name, line.strip())
363 print '%s:%04d %s' % (filename,lineno, name)
364 print '%s: %s' % (t, v)
366 if __name__ == "__main__":