blob: 16f31bde89f4465d67ea66aca99b091c2827f788 [file] [log] [blame]
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import sys
import os
import subprocess
import cloudtool
import urllib2
from optparse import OptionParser, OptionGroup, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError
import xml.dom.minidom
NetAppServerIP=None
NetAppUserName=None
NetAppPassword=None
CloudStackSvrIP=None
CloudStackSvrPort=8096
cmds=["createvol","deletevol", "listvol", "createlun", "listlun", "destroylun", "assoclun", "disassoclun", "createpool", "modifypool", "destroypool", "listpools"]
header = "Volume Manager CLI, the available COMMANDS are:"
def cmd_help():
print header
print
print "createpool add a new pool to the system"
print "modifypool change the allocation algorithm for a pool"
print "destroypool destroy a pool"
print "listpools list all the pools"
print "createvol add volume to a storage server"
print "deletevol delete volume on a storage server"
print "listvol list volume on a storage server"
print "createlun create LUN on a storage server"
print "listlun list LUN on a storage server"
print "destroylun destroy LUN on a storage server"
print "assoclun assoc LUN on a storage server"
print "disassoclun disassoc LUN on a storage server"
print
print "\"cloudvoladm COMMAND --help\" for more information on a specific command"
print
print "Global Options:"
print "--cloudStackMgtSvrIP the IP address of CloudStack Management Server"
print
print "Config file is ~/.cloudvoladmrc, Config options including: "
print "cloudStackMgtSvrIP=Cloudstack Management Server Address, which can be overriden by --cloudStackMgtSvrIP. If neither is provided, localhost is used."
usage="Volume Manager CLI: add a new volume to a storage pool"
addvolParser= OptionParser(usage)
addvolParser.add_option("-i", metavar="server ip", dest="server_ip", help="The IP address of the storage server")
addvolParser.add_option("-u", metavar="username", dest="username", help="username to access the storage server with")
addvolParser.add_option("-w", metavar="password", dest="password", help="the password to access the storage server with")
addvolParser.add_option("-p", dest="pool_name", help="the name of the pool to allocate from")
addvolParser.add_option("-a", dest="aggregate_name", help="the name of aggregate")
addvolParser.add_option("-v", dest="vol_name", help="the name of volume")
addvolParser.add_option("-s", dest="size", help="size in GB eg.1")
optionalGroup = OptionGroup(addvolParser, "Optional")
optionalGroup.add_option("-r", dest="percentage", help="Percentage used for snapshot reserve")
optionalGroup.add_option("-S", dest="snapshots", help="Snapshot schedule in <weeks> <days> <hours>@<which-hours> <minutes>@<which-minutes> e.g. \"2 4 5@1,4 6@2,5\"")
addvolParser.add_option_group(optionalGroup)
usage="Volume Manager CLI: remove a volume from a pool"
delvolParser= OptionParser(usage)
delvolParser.add_option("-i", metavar="server ip", dest="server_ip", help="The IP address of the storage server")
delvolParser.add_option("-a", dest="aggregate_name", help="The name of aggregate")
delvolParser.add_option("-v", dest="vol_name", help="The name of volume")
usage="Volume Manager CLI: list all volumes known to exist in a pool"
listvolParser= OptionParser(usage)
listvolParser.add_option("-p", dest="pool_name", help="The name of the pool to list volumes from")
usage="Volume Manager CLI: create a LUN on a pool"
createlunParser = OptionParser(usage)
createlunParser.add_option("-p", dest="pool_name", help="The name of the pool to add the volume to")
createlunParser.add_option("-s", dest="size", help="The size in GB e.g. 100")
usage="Volume Manager CLI: list LUN on a pool"
listlunParser = OptionParser(usage)
listlunParser.add_option("-p", dest="pool_name", help="The pool name")
usage="Volume Manager CLI: destroy a LUN "
destroylunParser = OptionParser(usage)
destroylunParser.add_option("-l", dest="lun_name", help="The LUN name")
usage="Volume Manager CLI: Add a new pool to the system"
createPoolParser = OptionParser(usage)
createPoolParser.add_option("-p", dest="pool_name", help="The pool name")
createPoolParser.add_option("-A", dest="algorithm", help="roundrobin or leastfull")
usage="Volume Manager CLI: change the allocation algorithm for a pool"
modifyPoolParser = OptionParser(usage)
modifyPoolParser.add_option("-p", dest="pool_name", help="The pool name")
modifyPoolParser.add_option("-A", dest="algorithm", help="roundrobin or leastfull")
usage="Volume Manager CLI: destroy a pool"
destroyPoolParser = OptionParser(usage)
destroyPoolParser.add_option("-p", dest="pool_name", help="The pool name")
usage="Volume Manager CLI: list pools"
listPoolParser = OptionParser(usage)
usage="Volume Manager CLI: associate a LUN with a guest that uses the stated IQN as client"
assocLunParser = OptionParser(usage)
assocLunParser.add_option("-g", dest="guest_iqn", help="the guest IQN. By default, it reads from /etc/iscsi/initiatorname.iscsi")
assocLunParser.add_option("-l", dest="lun_name", help="The LUN name")
usage="Volume Manager CLI: disassociate a LUN with a guest that uses the stated IQN as client"
disassocLunParser = OptionParser(usage)
disassocLunParser.add_option("-g", dest="guest_iqn", help="the guest IQN. By default, it reads from /etc/iscsi/initiatorname.iscsi")
disassocLunParser.add_option("-l", dest="lun_name", help="The LUN name")
cmdParsers = {cmds[0]:addvolParser, cmds[1]:delvolParser, cmds[2]:listvolParser, cmds[3]:createlunParser, cmds[4]:listlunParser,
cmds[5]:destroylunParser, cmds[6]:assocLunParser, cmds[7]:disassocLunParser, cmds[8]:createPoolParser, cmds[9]:modifyPoolParser, cmds[10]:destroyPoolParser, cmds[11]:listPoolParser}
def validate_parameter(input, signature):
(options, args) = signature.parse_args([])
inputDict = input.__dict__
sigDict = options.__dict__
for k,v in sigDict.iteritems():
inputValue = inputDict[k]
if inputValue == None:
print "Volume Manager CLI: missing operand "
print
signature.parse_args(["--help"])
def help_callback(option, opt, value, parser):
argv = sys.argv[1:]
try:
argv.remove(opt)
except:
argv.remove("--h")
if len(argv) == 0:
cmd_help()
return
(options, args) = parser.parse_args(argv)
for cmd in cmds:
if cmd == args[0]:
cmdParsers[cmd].parse_args(["--help"])
def Help():
usage = "usage: %prog cmd[createpool|listpools|modifypool|destroypool|createvol|deletevol|listvol|createlun|listlun|destroylun|assoclun|disassoclun] arg1 arg2 [--help, -h]"
parser = OptionParser(usage=usage, add_help_option=False)
parser.add_option("-h", "--help", action="callback", callback=help_callback);
parser.add_option("-i", metavar="server ip", dest="server_ip", help="The IP address of the storage server")
parser.add_option("--cloudstackSvr", dest="cloudstackSvr", help="cloudStack Server IP")
parser.add_option("-u", metavar="username", dest="username", help="username to access the storage server with")
parser.add_option("-w", metavar="password", dest="password", help="the password to access the storage server with")
parser.add_option("-p", dest="pool_name", help="the name of the pool to allocate from")
parser.add_option("-v", dest="vol_name", help="the name of volume")
parser.add_option("-A", dest="algorithm", help="roundrobin or leastfull")
parser.add_option("-a", dest="aggregate_name", help="The name of aggregate")
parser.add_option("-o", dest="options", help="requested option string for the NFS export or attach")
parser.add_option("-S", dest="snapshots", help="Snapshot schedule e.g.2 4 5@1,4 6@2,5")
parser.add_option("-r", dest="percentage", help="Percentage used for snapshot reservation")
parser.add_option("-s", dest="size", help="size in GB eg.1")
parser.add_option("-t", dest="target_iqn", help="the target IQN")
parser.add_option("-g", dest="guest_iqn", help="the guest IQN")
parser.add_option("-l", dest="lun_name", help="the LUN name")
return parser
def httpErrorHandler(code, msg):
try:
errtext = xml.dom.minidom.parseString(msg)
if errtext.getElementsByTagName("errortext") is not None:
err = getText(errtext.getElementsByTagName("errortext")[0].childNodes).strip()
print err
except:
print "Internal Error %s"%msg
def getText(nodelist):
rc = []
for node in nodelist:
if node.nodeType == node.TEXT_NODE: rc.append(node.data)
return ''.join(rc)
def createvol(options):
args = []
if options.pool_name == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if options.aggregate_name == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if options.vol_name == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if options.snapshots != None:
args += ['--snapshotpolicy=' + options.snapshots]
if options.size == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if options.percentage != None:
args += ['--snapshotreservation=' + options.percentage]
if NetAppServerIP == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if NetAppUserName == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
if NetAppPassword == None:
print "Volume Manager CLI: missing operand "
print
addvolParser.parse_args(["--help"])
'''
snapshot = options.snapshots
tokens = snapshot.split(" ")
print tokens
pos = 0;
for token in tokens:
if pos == 0:
#week
try:
week = int(token)
if week < 0:
raise
except:
print "Pls input correct week"
sys.exit(1)
elif pos == 1:
try:
day = int(token)
if day < 0:
raise
except:
print "Pls input correct day"
sys.exit(1)
elif pos == 2:
try:
hours = token.split("@")
if int(hours[0]) < 0:
raise
hourlists = hours[1].split(",")
for hour in hourlists:
if int(hour) < 0 or int(hour) > 24:
raise
except:
print "Pls input correct hour"
sys.exit(1)
elif pos == 3:
try:
minutes = token.split("@")
if int(minutes[0]) < 0:
raise
minuteslist = minutes[1].split(",")
for minute in minuteslist:
if int(minute) < 0 or int(minute) > 60:
raise
except:
print "Pls input correct hour"
sys.exit(1)
'''
try:
output = cloudtool.main(['cloud-tool', 'createVolumeOnFiler', '--ipaddress=' + NetAppServerIP , '--aggregatename=' + options.aggregate_name,
'--poolname=' + options.pool_name, '--volumename=' + options.vol_name,
'--size=' + options.size,
'--username=' + NetAppUserName, '--password=' + NetAppPassword, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"] + args)
print "Successfully added volume"
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing createvol cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing createvol cmd failed: %s" % (err.reason)
sys.exit(1)
def deletevol(options):
validate_parameter(options, delvolParser)
try:
output = cloudtool.main(['cloud-tool', 'destroyVolumeOnFiler', '--ipaddress=' + NetAppServerIP, '--aggregatename=' + options.aggregate_name,
'--volumename=' + options.vol_name, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully deleted volume"
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing deletevol cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing deletevol cmd failed: %s" % (err.reason)
sys.exit(1)
def listvol(options):
validate_parameter(options, listvolParser)
try:
output = cloudtool.main(['cloud-tool', 'listVolumesOnFiler', '--poolname=' + options.pool_name, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"]).strip("\n")
xmlResult = xml.dom.minidom.parseString(output)
print "%-10s %-20s %-20s %-40s %-20s %-30s "%('Id', 'Address', 'Aggregate', 'Volume', 'Size(GB)', 'snapshotPolicy', )
for volume in xmlResult.getElementsByTagName("volume"):
aggregatename = getText(volume.getElementsByTagName('aggregatename')[0].childNodes).strip()
id = getText(volume.getElementsByTagName('id')[0].childNodes).strip()
volumeName = getText(volume.getElementsByTagName('volumename')[0].childNodes).strip()
snapshotPolicy = getText(volume.getElementsByTagName('snapshotpolicy')[0].childNodes).strip()
ipaddress = getText(volume.getElementsByTagName('ipaddress')[0].childNodes).strip()
volSize = getText(volume.getElementsByTagName('size')[0].childNodes).strip()
print "%-10s %-20s %-20s %-40s %-20s %-30s "%(id, ipaddress, aggregatename, volumeName, volSize, snapshotPolicy)
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing listvol cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing listvol cmd failed: %s" % (err.reason)
sys.exit(1)
def createlun(options):
validate_parameter(options, createlunParser)
try:
output = cloudtool.main(['cloud-tool', 'createLunOnFiler', '--name=' + options.pool_name,
'--size=' + options.size, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
xmlResult = xml.dom.minidom.parseString(output.strip("\n"))
path = getText(xmlResult.getElementsByTagName("path")[0].childNodes).strip()
iqn = getText(xmlResult.getElementsByTagName("iqn")[0].childNodes).strip()
ipAddr = getText(xmlResult.getElementsByTagName('ipaddress')[0].childNodes).strip()
print "%-30s %-30s %-50s "%('LUN Name', 'Address', 'Target IQN')
print "%-30s %-30s %-50s "%(path, ipAddr, iqn)
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing createlun cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing createlun cmd failed: %s" % (err.reason)
sys.exit(1)
def listlun(options):
validate_parameter(options, listlunParser)
args = ["--poolname=" + options.pool_name, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"]
try:
output = cloudtool.main(['cloud-tool', 'listLunsOnFiler'] + args).strip("\n")
xmlResult = xml.dom.minidom.parseString(output)
print "%-10s %-10s %-50s %-30s "%('LUN Id', 'Volume Id', 'Target IQN', 'LUN Name')
for volume in xmlResult.getElementsByTagName("lun"):
uuid = getText(volume.getElementsByTagName('id')[0].childNodes).strip()
path = getText(volume.getElementsByTagName('name')[0].childNodes).strip()
targetiqn = getText(volume.getElementsByTagName('iqn')[0].childNodes).strip()
volumeId = getText(volume.getElementsByTagName('volumeid')[0].childNodes).strip()
print "%-10s %-10s %-50s %-30s "%(uuid, volumeId, targetiqn, path)
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing listlun cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing listlun cmd failed: %s" % (err.reason)
sys.exit(1)
def destroylun(options):
validate_parameter(options, destroylunParser)
try:
output = cloudtool.main(['cloud-tool', 'destroyLunOnFiler', '--path=' + options.lun_name,
"--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully destroyed LUN"
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing destroylun cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing destroylun failed: %s" % (err.reason)
sys.exit(1)
def assoclun(options):
validate_parameter(options, assocLunParser)
try:
output = cloudtool.main(['cloud-tool', 'associateLun', '--name=' + options.lun_name,
'--iqn=' + options.guest_iqn, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
xmlResult = xml.dom.minidom.parseString(output.strip("\n"))
lunid = getText(xmlResult.getElementsByTagName("id")[0].childNodes).strip()
iqn = getText(xmlResult.getElementsByTagName("targetiqn")[0].childNodes).strip()
ipAddr = getText(xmlResult.getElementsByTagName('ipaddress')[0].childNodes).strip()
print "%-30s %-30s %-50s "%('LUN Id', 'Address', 'Target IQN')
print "%-30s %-30s %-50s" % (lunid, ipAddr, iqn)
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing assoclun cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing assoclun failed: %s" % (err.reason)
sys.exit(1)
def disassoclun(options):
validate_parameter(options, disassocLunParser)
try:
output = cloudtool.main(['cloud-tool', 'dissociateLun', '--path=' + options.lun_name,
'--iqn=' + options.guest_iqn, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully dissociated LUN"
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing disassoclun cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing disassoclun failed: %s" % (err.reason)
sys.exit(1)
def createpool(options):
validate_parameter(options, createPoolParser)
if not (options.algorithm == "roundrobin" or options.algorithm == "leastfull"):
print "Only roundrobin or leastfull algorithm is supported"
sys.exit(1)
try:
output = cloudtool.main(['cloud-tool', 'createPool', '--name=' + options.pool_name,
'--algorithm=' + options.algorithm, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully created pool"
except urllib2.HTTPError, err:
code = err.code
print "executing createpool cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, err.read())
sys.exit(1)
except urllib2.URLError, err:
print "executing createpool failed: %s" % (err.reason)
sys.exit(1)
def listpools(options):
try:
output = cloudtool.main(['cloud-tool', 'listPools',
"--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
output = output.strip("\n")
xmlResult = xml.dom.minidom.parseString(output)
print "%-10s %-40s %-10s" %('Id', 'Pool Name', 'Algorithm')
for volume in xmlResult.getElementsByTagName("pool"):
id = getText(volume.getElementsByTagName('id')[0].childNodes).strip()
poolname = getText(volume.getElementsByTagName('name')[0].childNodes).strip()
alg = getText(volume.getElementsByTagName('algorithm')[0].childNodes).strip()
print "%-10s %-40s %-10s"%(id, poolname, alg)
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing listpools cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing listpools failed, due to: %s" % (err.reason)
sys.exit(1)
def modifypool(options):
validate_parameter(options, modifyPoolParser)
try:
output = cloudtool.main(['cloud-tool', 'modifyPool', '--poolname=' + options.pool_name,
'--algorithm=' + options.algorithm, "--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully modified pool"
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing modifypool cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing modifypool failed, due to: %s" % (err.reason)
sys.exit(1)
def destroypool(options):
validate_parameter(options, destroyPoolParser)
try:
output = cloudtool.main(['cloud-tool', 'deletePool', '--poolname=' + options.pool_name,
"--server=" + CloudStackSvrIP + ":" + str(CloudStackSvrPort), "--stripxml=false"])
print "Successfully destroyed pool: " + options.pool_name
except urllib2.HTTPError, err:
code = err.code
msg = err.read()
print "executing destroypool cmd failed, http returning error code: %s" % (code)
httpErrorHandler(code, msg)
sys.exit(1)
except urllib2.URLError, err:
print "executing destroypool failed, due to: %s" % (err.reason)
sys.exit(1)
def loadCfgFile():
options = dict()
try:
cfgFile = open(os.environ['HOME'] + "/.cloudvoladmrc")
for line in cfgFile:
option = line.split("=")
if option[0] == "cloudStackMgtSvrIP":
options["cloudStackMgtSvrIP"] = option[1].strip("\n")
except:
return None
return options
def getGuestIQN():
try:
initialFile = open("/etc/iscsi/initiatorname.iscsi")
for line in initialFile:
iqn = line.split("=")
if iqn[0] == "InitiatorName":
return iqn[1].strip("\n")
except:
return None
return None
if __name__ == '__main__':
parser = Help()
(options, args) = parser.parse_args()
globalCfg = loadCfgFile()
NetAppServerIP= options.server_ip
NetAppUserName = options.username
NetAppPassword = options.password
CloudStackSvrIP = options.cloudstackSvr
if CloudStackSvrIP == None:
if globalCfg != None and "cloudStackMgtSvrIP" in globalCfg:
CloudStackSvrIP = globalCfg["cloudStackMgtSvrIP"]
if CloudStackSvrIP == None:
CloudStackSvrIP = "127.0.0.1"
if options.guest_iqn == None:
GuestIQN = getGuestIQN()
options.__dict__["guest_iqn"] = GuestIQN
if len(args) == 0:
sys.exit(1)
cmd = args[0]
if cmd == "createvol":
createvol(options)
elif cmd == "deletevol":
deletevol(options)
elif cmd == "listvol":
listvol(options)
elif cmd == "createlun":
createlun(options)
elif cmd == "listlun":
listlun(options)
elif cmd == "destroylun":
destroylun(options)
elif cmd == "assoclun":
assoclun(options)
elif cmd == "disassoclun":
disassoclun(options)
elif cmd == "createpool":
createpool(options)
elif cmd == "modifypool":
modifypool(options)
elif cmd == "destroypool":
destroypool(options)
elif cmd == "listpools":
listpools(options)
else:
print "Unrecoginzied command"
cmd_help()
sys.exit(1)