Skip to content

Create Luns in an AMS2500 easily.

by jprice on March 10th, 2011

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()

From → Geeky musings

No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS