Create Luns in an AMS2500 easily.
If you’re like me, you’ve got an AMS2000 series (2100, 2300, 2500) array, and you’re using HDP pools, since they’re SO MUCH EASIER than regular RAID groups.
If you’re also like me, you’re using SNM2[1] to manage this thing, and it is an amazingly slow GUI. But SNM2 has a CLI! So that’s better, right? Sorta… you have to log in with each command you want to run…
So I’ve written a python script which creates LU[2] of specified size. You can download my lu-maker.py here, or you can look at it below.
–Jason
[1] Storage Navigator Modular 2
[2] or LUNS (not presented to the hosts yet), or LDEV’s if you’re from USP land…
#!/usr/bin/python import os import os.path import subprocess import sys import time unit = 'tc3ams2500' user = 'my_username_you_silly_boy' passwd = 'my_password_you_silly_boy' def usage() : """print usage information and exit""" print "USAGE:" print "%s -p <pool#> size1 [size2 ... sizeN] [-p <pool#> size1 ...] ..." % sys.argv[0] print 'a size can be either:' print '"100" for 100gb or' print '"10x50" for creating ten different 50gb luns' sys.exit(-1) def whereis(program): """Validate if a program is in your $PATH variable""" for path in os.environ.get('PATH', '').split(':'): if os.path.exists(os.path.join(path, program)) and \ not os.path.isdir(os.path.join(path, program)): return os.path.join(path, program) return None def isint(arg) : """Validate if a string passed on the command line is an integer.""" try : int(arg) except ValueError : return False return True def validsize(arg) : """validate if an argument is a valid size argument. Either it must be an integer (indicating a size of one lun) or it must be in the form #x# or <number>x<number> which indicates a count of luns to be created of a specific size. For example: 10x50 would mean 10 luns of size 50gb should be created """ if isint(arg) : return True elif 'x' in arg : try : count, size = arg.split('x') except : return False if isint(count) and isint(size) : return True return False def parsesize(arg) : """once a size specification is validated, it can be more easily parsed. Run as a generator to yeild the right number of sizes """ if isint(arg) : yield arg else : count,size = arg.split('x') for i in range(int(count)) : yield size def runcmd(cmdstr, dashy=False) : """run command abstracts away the subprocess module, and the vagarities of the SNM2 CLI. Deals with the login prompt and the password prompt. if dashy is True, also respond the a third question with a 'y\n' to deal with 'are you sure' type prompts. """ sleeptime = .2 cmdlist = cmdstr.split() if whereis(cmdlist[0]) == None : print "%s is not found in $PATH" % cmdlist[0] sys.exit(-1) p = subprocess.Popen( cmdlist, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) time.sleep(sleeptime) p.stdin.write(user + '\n') time.sleep(sleeptime) p.stdin.write(passwd + '\n') if dashy : time.sleep(sleeptime) p.stdin.write('y\n') stdout, stderr = p.communicate() return stdout, stderr, p.returncode def parselus(output) : """Takes the output of auluref, and finds any gaps in the list of LU's, and also gives the next free lu number. First parse the output, then find the gaps, then add 1 to the top lu number in existance, and return the holelist, and the next free. """ lulist = [] holelist = [] last = 0 for line in (line for line in output.split(os.linesep)) : uline = line.strip() words = uline.split() if len(words) > 0 and isint(words[0]) : lu = int(words[0]) lulist.append(lu) #find the gaps in lulist: last = 0 holelist = [] for lu in lulist : while True : tlast = last last += 1 if tlast >= lu : break holelist.append(tlast) return holelist, lulist[-1] + 1 def genlunumbers() : """Creates a generator to yield the next available lu number. First tries to fill any holes, and then just adds to the top. Uses auluref as a datasource. """ cmdstr = "auluref -unit %s" % unit stdout, stderr, returncode = runcmd(cmdstr) holelist, next = parselus(stdout) for lu in holelist : yield str(lu) lu = next while True : yield str(lu) lu += 1 def getpools() : """uses audppool to list the available DP pools. Used to validate if the passed pool numbers are valid. NOTE: The format of the audppool command is a little crazy. Short version of what follows: if the first 10 chars of any line start with a number (if the 10 chars are run through .split(), it's a valid pool. """ cmdstr = "audppool -unit %s -refer" % unit stdout, stderr, returncode = runcmd(cmdstr) poollist = [] for line in (line for line in stdout.split(os.linesep)) : uline = line[:10] words = uline.split() if len(words) == 0 : continue if isint(words[0]) : poollist.append(words[0]) return poollist def makelun(pool, size, lu) : """Actually makes a given lun in a specified pool, with a given size. """ #auluadd -unit tc3ams2500 [ -lu lun ] -dppoolno pool -size size + 'g' cmdstr = "auluadd -unit %s -lu %s -dppoolno %s -size %s" % (unit, lu, pool, size + 'g') print "making LU number %s from pool %s of size %s" % (lu, pool, size) #stdout, stderr, returncode = runcmd(cmdstr, dashy=True) print "NOTE: program currently nutered. Uncomment above line (and \ncomment out this line) to activate.\n I would normally run this command: \n", cmdstr def main() : """ Main: calls getpool, parses the command line arguments (in a way I'm not fully happy with). The last three lines make the luns. """ poollist = getpools() lunlist = [] #parse cmdline, build list of pool,size pairs if sys.argv[1] != '-p' : print 'must specify -p <pool number> first' usage() if isint(sys.argv[2]) and sys.argv[2] in poollist : pool = sys.argv[2] else : print 'pool number %s must be a valid pool number' % sys.argv[2] print 'valid pools are %s' % poollist usage() if validsize(sys.argv[3]) : for size in parsesize(sys.argv[3]) : lunlist.append( (pool, size) ) else : print 'invalid size specification: %s' % sys.argv[3] usage() nextispool = False for arg in sys.argv[4:] : if nextispool : if isint(arg) and arg in poollist : pool = arg nextispool = False else : print 'pool number %s must be a valid pool number' % sys.argv[2] print 'valid pools are %s' % poollist usage() elif arg == '-p' : nextispool = True elif validsize(arg) : for size in parsesize(arg) : lunlist.append( (pool,size) ) else : 'unknown error with arg %s' % arg usage() lu = genlunumbers() for pool,size in lunlist : makelun(pool, size, lu.next()) if __name__ == '__main__' : main()