mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Removed cloud-cli folder and contents, as it is not maintained or used anymore.
This commit is contained in:
parent
bdaa60fd6c
commit
7f2bbcbfff
@ -1,28 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
|
||||||
|
|
||||||
import cloudtool
|
|
||||||
|
|
||||||
ret = cloudtool.main()
|
|
||||||
if ret: sys.exit(ret)
|
|
||||||
@ -1,607 +0,0 @@
|
|||||||
#!/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)
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
Created on Aug 2, 2010
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
import os,pkgutil
|
|
||||||
|
|
||||||
def get_all_apis():
|
|
||||||
apis = []
|
|
||||||
for x in pkgutil.walk_packages([os.path.dirname(__file__)]):
|
|
||||||
loader = x[0].find_module(x[1])
|
|
||||||
try: module = loader.load_module("cloudapis." + x[1])
|
|
||||||
except ImportError: continue
|
|
||||||
apis.append(module)
|
|
||||||
return apis
|
|
||||||
|
|
||||||
def lookup_api(api_name):
|
|
||||||
api = None
|
|
||||||
matchingapi = [ x for x in get_all_apis() if api_name.replace("-","_") == x.__name__.split(".")[-1] ]
|
|
||||||
if not matchingapi: api = None
|
|
||||||
else: api = matchingapi[0]
|
|
||||||
if api: api = getattr(api,"implementor")
|
|
||||||
return api
|
|
||||||
|
|
||||||
@ -1,198 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''Implements the CloudStack API'''
|
|
||||||
|
|
||||||
|
|
||||||
from cloudtool.utils import describe
|
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
import os
|
|
||||||
import xml.dom.minidom
|
|
||||||
import re
|
|
||||||
import base64
|
|
||||||
import hmac
|
|
||||||
import hashlib
|
|
||||||
import httplib
|
|
||||||
|
|
||||||
class CloudAPI:
|
|
||||||
|
|
||||||
@describe("server", "Management Server host name or address")
|
|
||||||
@describe("apikey", "Management Server apiKey")
|
|
||||||
@describe("securitykey", "Management Server securityKey")
|
|
||||||
@describe("responseformat", "Response format: xml or json")
|
|
||||||
@describe("stripxml", "True if xml tags have to be stripped in the output, false otherwise")
|
|
||||||
def __init__(self,
|
|
||||||
server="127.0.0.1:8096",
|
|
||||||
responseformat="xml",
|
|
||||||
stripxml="true",
|
|
||||||
apiKey=None,
|
|
||||||
securityKey=None
|
|
||||||
):
|
|
||||||
self.__dict__.update(locals())
|
|
||||||
|
|
||||||
def _make_request_with_keys(self,command,requests={}):
|
|
||||||
requests["command"] = command
|
|
||||||
requests["apiKey"] = self.apiKey
|
|
||||||
requests["response"] = "xml"
|
|
||||||
requests = zip(requests.keys(), requests.values())
|
|
||||||
requests.sort(key=lambda x: str.lower(x[0]))
|
|
||||||
|
|
||||||
requestUrl = "&".join(["=".join([request[0], urllib.quote_plus(str(request[1]))]) for request in requests])
|
|
||||||
hashStr = "&".join(["=".join([str.lower(request[0]), urllib.quote_plus(str.lower(str(request[1])))]) for request in requests])
|
|
||||||
|
|
||||||
sig = urllib.quote_plus(base64.encodestring(hmac.new(self.securityKey, hashStr, hashlib.sha1).digest()).strip())
|
|
||||||
|
|
||||||
requestUrl += "&signature=%s"%sig
|
|
||||||
return requestUrl
|
|
||||||
|
|
||||||
|
|
||||||
def _make_request_with_auth(self, command, requests):
|
|
||||||
self.connection = httplib.HTTPConnection("%s"%(self.server))
|
|
||||||
requests["command"] = command
|
|
||||||
requests["apiKey"] = self.apiKey
|
|
||||||
requests["response"] = self.responseformat
|
|
||||||
requests = zip(requests.keys(), requests.values())
|
|
||||||
requests.sort(key=lambda x: str.lower(x[0]))
|
|
||||||
|
|
||||||
requestUrl = "&".join(["=".join([request[0], urllib.quote(str(request[1],""))]) for request in requests])
|
|
||||||
hashStr = "&".join(["=".join([str.lower(request[0]), urllib.quote(str.lower(str(request[1])),"")]) for request in requests])
|
|
||||||
|
|
||||||
sig = urllib.quote_plus(base64.encodestring(hmac.new(self.securityKey, str.lower(hashStr), hashlib.sha1).digest()).strip())
|
|
||||||
|
|
||||||
requestUrl += "&signature=%s"%sig
|
|
||||||
|
|
||||||
self.connection.request("GET", "/client/api?%s"%requestUrl)
|
|
||||||
return self.connection.getresponse().read()
|
|
||||||
|
|
||||||
def _make_request(self,command,parameters=None):
|
|
||||||
|
|
||||||
'''Command is a string, parameters is a dictionary'''
|
|
||||||
if ":" in self.server:
|
|
||||||
host,port = self.server.split(":")
|
|
||||||
port = int(port)
|
|
||||||
else:
|
|
||||||
host = self.server
|
|
||||||
port = 8096
|
|
||||||
|
|
||||||
url = "http://" + self.server + "/client/api?"
|
|
||||||
|
|
||||||
if not parameters: parameters = {}
|
|
||||||
if self.apiKey is not None and self.securityKey is not None:
|
|
||||||
return self._make_request_with_auth(command, parameters)
|
|
||||||
else:
|
|
||||||
parameters["command"] = command
|
|
||||||
parameters["response"] = self.responseformat
|
|
||||||
querystring = urllib.urlencode(parameters)
|
|
||||||
|
|
||||||
url += querystring
|
|
||||||
|
|
||||||
f = urllib2.urlopen(url)
|
|
||||||
data = f.read()
|
|
||||||
if self.stripxml == "true":
|
|
||||||
data=re.sub("<\?.*\?>", "\n", data);
|
|
||||||
data=re.sub("</[a-z]*>", "\n", data);
|
|
||||||
data=data.replace(">", "=");
|
|
||||||
data=data.replace("=<", "\n");
|
|
||||||
data=data.replace("\n<", "\n");
|
|
||||||
data=re.sub("\n.*cloud-stack-version=.*", "", data);
|
|
||||||
data=data.replace("\n\n\n", "\n");
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def load_dynamic_methods():
|
|
||||||
'''creates smart function objects for every method in the commands.xml file'''
|
|
||||||
|
|
||||||
def getText(nodelist):
|
|
||||||
rc = []
|
|
||||||
for node in nodelist:
|
|
||||||
if node.nodeType == node.TEXT_NODE: rc.append(node.data)
|
|
||||||
return ''.join(rc)
|
|
||||||
|
|
||||||
# FIXME figure out installation and packaging
|
|
||||||
xmlfile = os.path.join("/etc/cloud/cli/","commands.xml")
|
|
||||||
dom = xml.dom.minidom.parse(xmlfile)
|
|
||||||
|
|
||||||
for cmd in dom.getElementsByTagName("command"):
|
|
||||||
name = getText(cmd.getElementsByTagName('name')[0].childNodes).strip()
|
|
||||||
assert name
|
|
||||||
|
|
||||||
description = getText(cmd.getElementsByTagName('description')[0].childNodes).strip()
|
|
||||||
if description:
|
|
||||||
description = '"""%s"""' % description
|
|
||||||
else: description = ''
|
|
||||||
arguments = []
|
|
||||||
options = []
|
|
||||||
descriptions = []
|
|
||||||
|
|
||||||
for param in cmd.getElementsByTagName("request")[0].getElementsByTagName("arg"):
|
|
||||||
argname = getText(param.getElementsByTagName('name')[0].childNodes).strip()
|
|
||||||
assert argname
|
|
||||||
|
|
||||||
required = getText(param.getElementsByTagName('required')[0].childNodes).strip()
|
|
||||||
if required == 'true': required = True
|
|
||||||
elif required == 'false': required = False
|
|
||||||
else: raise AssertionError, "Not reached"
|
|
||||||
if required: arguments.append(argname)
|
|
||||||
options.append(argname)
|
|
||||||
|
|
||||||
#import ipdb; ipdb.set_trace()
|
|
||||||
requestDescription = param.getElementsByTagName('description')
|
|
||||||
if requestDescription:
|
|
||||||
descriptionParam = getText(requestDescription[0].childNodes)
|
|
||||||
else:
|
|
||||||
descriptionParam = ''
|
|
||||||
if descriptionParam: descriptions.append( (argname,descriptionParam) )
|
|
||||||
|
|
||||||
funcparams = ["self"] + [ "%s=None"%o for o in options ]
|
|
||||||
funcparams = ", ".join(funcparams)
|
|
||||||
|
|
||||||
code = """
|
|
||||||
def %s(%s):
|
|
||||||
%s
|
|
||||||
parms = dict(locals())
|
|
||||||
del parms["self"]
|
|
||||||
for arg in %r:
|
|
||||||
if locals()[arg] is None:
|
|
||||||
raise TypeError, "%%s is a required option"%%arg
|
|
||||||
for k,v in parms.items():
|
|
||||||
if v is None: del parms[k]
|
|
||||||
output = self._make_request("%s",parms)
|
|
||||||
return output
|
|
||||||
"""%(name,funcparams,description,arguments,name)
|
|
||||||
|
|
||||||
namespace = {}
|
|
||||||
exec code.strip() in namespace
|
|
||||||
|
|
||||||
func = namespace[name]
|
|
||||||
for argname,description in descriptions:
|
|
||||||
func = describe(argname,description)(func)
|
|
||||||
|
|
||||||
yield (name,func)
|
|
||||||
|
|
||||||
|
|
||||||
for name,meth in load_dynamic_methods():
|
|
||||||
setattr(CloudAPI, name, meth)
|
|
||||||
|
|
||||||
implementor = CloudAPI
|
|
||||||
|
|
||||||
del name,meth,describe,load_dynamic_methods
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
Created on Aug 2, 2010
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import cloudapis as apis
|
|
||||||
import cloudtool.utils as utils
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
|
||||||
|
|
||||||
#import ipdb; ipdb.set_trace()
|
|
||||||
if argv == None:
|
|
||||||
argv = sys.argv
|
|
||||||
|
|
||||||
prelim_args = [ x for x in argv[0:] if not x.startswith('-') ]
|
|
||||||
parser = utils.get_parser()
|
|
||||||
|
|
||||||
api = __import__("cloudapis")
|
|
||||||
apis = getattr(api, "implementor")
|
|
||||||
if len(prelim_args) == 1:
|
|
||||||
commandlist = utils.get_command_list(apis)
|
|
||||||
parser.error("you need to specify a command name as the first argument\n\nCommands supported by the %s API:\n"%prelim_args[0] + "\n".join(commandlist))
|
|
||||||
|
|
||||||
command = utils.lookup_command_in_api(apis,prelim_args[1])
|
|
||||||
if not command: parser.error("command %r not supported by the %s API"%(prelim_args[1],prelim_args[0]))
|
|
||||||
|
|
||||||
argv = argv[1:]
|
|
||||||
if len(argv) == 1:
|
|
||||||
argv.append("--help")
|
|
||||||
|
|
||||||
parser = utils.get_parser(apis.__init__,command)
|
|
||||||
opts,args,api_optionsdict,cmd_optionsdict = parser.parse_args(argv)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
api = apis(**api_optionsdict)
|
|
||||||
except utils.OptParseError,e:
|
|
||||||
parser.error(str(e))
|
|
||||||
|
|
||||||
command = utils.lookup_command_in_api(api,args[0])
|
|
||||||
|
|
||||||
# we now discard the first two arguments as those necessarily are the api and command names
|
|
||||||
args = args[2:]
|
|
||||||
|
|
||||||
try: return command(*args,**cmd_optionsdict)
|
|
||||||
except TypeError,e: parser.error(str(e))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main(argv)
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
Created on Aug 2, 2010
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import inspect
|
|
||||||
from optparse import OptionParser, OptParseError, BadOptionError, OptionError, OptionConflictError, OptionValueError
|
|
||||||
import cloudapis as apis
|
|
||||||
|
|
||||||
|
|
||||||
def describe(name,desc):
|
|
||||||
def inner(decoratee):
|
|
||||||
if not hasattr(decoratee,"descriptions"): decoratee.descriptions = {}
|
|
||||||
decoratee.descriptions[name] = desc
|
|
||||||
return decoratee
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
def error(msg):
|
|
||||||
sys.stderr.write(msg)
|
|
||||||
sys.stderr.write("\n")
|
|
||||||
|
|
||||||
|
|
||||||
class MyOptionParser(OptionParser):
|
|
||||||
def error(self, msg):
|
|
||||||
error("%s: %s\n" % (self.get_prog_name(),msg))
|
|
||||||
self.print_usage(sys.stderr)
|
|
||||||
self.exit(os.EX_USAGE)
|
|
||||||
|
|
||||||
def parse_args(self,*args,**kwargs):
|
|
||||||
options,arguments = OptionParser.parse_args(self,*args,**kwargs)
|
|
||||||
|
|
||||||
def prune_options(options,alist):
|
|
||||||
"""Given 'options' -- a list of arguments to OptionParser.add_option,
|
|
||||||
and a set of optparse Values, return a dictionary of only those values
|
|
||||||
that apply exclusively to 'options'"""
|
|
||||||
return dict( [ (k,getattr(options,k)) for k in dir(options) if k in alist ] )
|
|
||||||
|
|
||||||
api_options = prune_options(options,self.api_dests)
|
|
||||||
cmd_options = prune_options(options,self.cmd_dests)
|
|
||||||
|
|
||||||
return options,arguments,api_options,cmd_options
|
|
||||||
|
|
||||||
|
|
||||||
def get_parser(api_callable=None,cmd_callable=None): # this should probably be the __init__ method of myoptionparser
|
|
||||||
|
|
||||||
def getdefaulttag(default):
|
|
||||||
if default is not None: return " [Default: %default]"
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def get_arguments_and_options(callable):
|
|
||||||
"""Infers and returns arguments and options based on a callable's signature.
|
|
||||||
Cooperates with decorator @describe"""
|
|
||||||
try:
|
|
||||||
funcargs = inspect.getargspec(callable).args
|
|
||||||
defaults = inspect.getargspec(callable).defaults
|
|
||||||
except:
|
|
||||||
funcargs = inspect.getargspec(callable)[0]
|
|
||||||
defaults = inspect.getargspec(callable)[3]
|
|
||||||
if not defaults: defaults = []
|
|
||||||
args = funcargs[1:len(funcargs)-len(defaults)] # this assumes self, so assumes methods
|
|
||||||
opts = funcargs[len(funcargs)-len(defaults):]
|
|
||||||
try: descriptions = callable.descriptions
|
|
||||||
except AttributeError: descriptions = {}
|
|
||||||
arguments = [ (argname, descriptions.get(argname,'') ) for argname in args ]
|
|
||||||
options = [ [
|
|
||||||
("--%s"%argname.replace("_","-"),),
|
|
||||||
{
|
|
||||||
"dest":argname,
|
|
||||||
"help":descriptions.get(argname,'') + getdefaulttag(default),
|
|
||||||
"default":default,
|
|
||||||
}
|
|
||||||
] for argname,default in zip(opts,defaults) ]
|
|
||||||
return arguments,options
|
|
||||||
|
|
||||||
basic_usage = "usage: %prog [options...] "
|
|
||||||
|
|
||||||
api_name = "<api>"
|
|
||||||
cmd_name = "<command>"
|
|
||||||
description = "%prog is a command-line tool to access several cloud APIs."
|
|
||||||
arguments = ''
|
|
||||||
argexp = ""
|
|
||||||
|
|
||||||
if api_callable:
|
|
||||||
api_name = api_callable.__module__.split(".")[-1].replace("_","-")
|
|
||||||
api_arguments,api_options = get_arguments_and_options(api_callable)
|
|
||||||
assert len(api_arguments) is 0 # no mandatory arguments for class initializers
|
|
||||||
|
|
||||||
if cmd_callable:
|
|
||||||
cmd_name = cmd_callable.func_name.replace("_","-")
|
|
||||||
cmd_arguments,cmd_options = get_arguments_and_options(cmd_callable)
|
|
||||||
if cmd_arguments:
|
|
||||||
arguments = " " + " ".join( [ s[0].upper() for s in cmd_arguments ] )
|
|
||||||
argexp = "\n\nArguments:\n" + "\n".join ( " %s\n %s"%(s.upper(),u) for s,u in cmd_arguments )
|
|
||||||
description = cmd_callable.__doc__
|
|
||||||
|
|
||||||
api_command = "%s %s"%(api_name,cmd_name)
|
|
||||||
|
|
||||||
if description: description = "\n\n" + description
|
|
||||||
else: description = ''
|
|
||||||
|
|
||||||
usage = basic_usage + api_command + arguments + description + argexp
|
|
||||||
|
|
||||||
parser = MyOptionParser(usage=usage, add_help_option=False)
|
|
||||||
|
|
||||||
parser.add_option('--help', action="help")
|
|
||||||
|
|
||||||
group = parser.add_option_group("General options")
|
|
||||||
group.add_option('-v', '--verbose', dest="verbose", help="Print extra output")
|
|
||||||
|
|
||||||
parser.api_dests = []
|
|
||||||
if api_callable and api_options:
|
|
||||||
group = parser.add_option_group("Options for the %s API"%api_name)
|
|
||||||
for a in api_options:
|
|
||||||
group.add_option(a[0][0],**a[1])
|
|
||||||
parser.api_dests.append(a[1]["dest"])
|
|
||||||
|
|
||||||
parser.cmd_dests = []
|
|
||||||
if cmd_callable and cmd_options:
|
|
||||||
group = parser.add_option_group("Options for the %s command"%cmd_name)
|
|
||||||
for a in cmd_options:
|
|
||||||
group.add_option(a[0][0],**a[1])
|
|
||||||
parser.cmd_dests.append(a[1]["dest"])
|
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
||||||
def lookup_command_in_api(api,command_name):
|
|
||||||
command = getattr(api,command_name.replace("-","_"),None)
|
|
||||||
return command
|
|
||||||
|
|
||||||
def get_api_list(api):
|
|
||||||
apilist = []
|
|
||||||
for cmd_name in dir(api):
|
|
||||||
cmd = getattr(api,cmd_name)
|
|
||||||
if callable(cmd) and not cmd_name.startswith("_"):
|
|
||||||
apilist.append(cmd_name)
|
|
||||||
return apilist
|
|
||||||
|
|
||||||
def get_command_list(api):
|
|
||||||
cmds = []
|
|
||||||
for cmd_name in dir(api):
|
|
||||||
cmd = getattr(api,cmd_name)
|
|
||||||
if callable(cmd) and not cmd_name.startswith("_"):
|
|
||||||
if cmd.__doc__:docstring = cmd.__doc__
|
|
||||||
else:docstring = ''
|
|
||||||
cmds.append( " %s" % (cmd_name.replace('_','-')) )
|
|
||||||
return cmds
|
|
||||||
Loading…
x
Reference in New Issue
Block a user