cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r...@apache.org
Subject git commit: updated refs/heads/master to fa41bc7
Date Sun, 17 Jan 2016 21:09:33 GMT
Repository: cloudstack
Updated Branches:
  refs/heads/master 262be758b -> fa41bc769


Revert "Merge pull request #1228 from borisroman/CLOUDSTACK-9149"

This reverts commit c459dfe62c57c72ced99ddb0c7d8a9547a380305, reversing
changes made to 2afb739f0656d14e5c298bf469f797fff6da221b.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/fa41bc76
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/fa41bc76
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/fa41bc76

Branch: refs/heads/master
Commit: fa41bc769c442287076d28a667a2f3967bae4ed1
Parents: 262be75
Author: Remi Bergsma <github@remi.nl>
Authored: Sun Jan 17 22:06:50 2016 +0100
Committer: Remi Bergsma <github@remi.nl>
Committed: Sun Jan 17 22:06:50 2016 +0100

----------------------------------------------------------------------
 cloud-cli/bindir/cloud-tool     |  28 ++
 cloud-cli/bindir/cloudvoladm    | 607 +++++++++++++++++++++++++++++++++++
 cloud-cli/cloudapis/__init__.py |  43 +++
 cloud-cli/cloudapis/cloud.py    | 198 ++++++++++++
 cloud-cli/cloudtool/__init__.py |  71 ++++
 cloud-cli/cloudtool/utils.py    | 169 ++++++++++
 6 files changed, 1116 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/bindir/cloud-tool
----------------------------------------------------------------------
diff --git a/cloud-cli/bindir/cloud-tool b/cloud-cli/bindir/cloud-tool
new file mode 100755
index 0000000..4fcc834
--- /dev/null
+++ b/cloud-cli/bindir/cloud-tool
@@ -0,0 +1,28 @@
+#!/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)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/bindir/cloudvoladm
----------------------------------------------------------------------
diff --git a/cloud-cli/bindir/cloudvoladm b/cloud-cli/bindir/cloudvoladm
new file mode 100755
index 0000000..16f31bd
--- /dev/null
+++ b/cloud-cli/bindir/cloudvoladm
@@ -0,0 +1,607 @@
+#!/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)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/cloudapis/__init__.py
----------------------------------------------------------------------
diff --git a/cloud-cli/cloudapis/__init__.py b/cloud-cli/cloudapis/__init__.py
new file mode 100644
index 0000000..f23b2ce
--- /dev/null
+++ b/cloud-cli/cloudapis/__init__.py
@@ -0,0 +1,43 @@
+# 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
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/cloudapis/cloud.py
----------------------------------------------------------------------
diff --git a/cloud-cli/cloudapis/cloud.py b/cloud-cli/cloudapis/cloud.py
new file mode 100644
index 0000000..a0e8880
--- /dev/null
+++ b/cloud-cli/cloudapis/cloud.py
@@ -0,0 +1,198 @@
+# 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
+
+

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/cloudtool/__init__.py
----------------------------------------------------------------------
diff --git a/cloud-cli/cloudtool/__init__.py b/cloud-cli/cloudtool/__init__.py
new file mode 100644
index 0000000..c4b479e
--- /dev/null
+++ b/cloud-cli/cloudtool/__init__.py
@@ -0,0 +1,71 @@
+# 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)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa41bc76/cloud-cli/cloudtool/utils.py
----------------------------------------------------------------------
diff --git a/cloud-cli/cloudtool/utils.py b/cloud-cli/cloudtool/utils.py
new file mode 100644
index 0000000..e123434
--- /dev/null
+++ b/cloud-cli/cloudtool/utils.py
@@ -0,0 +1,169 @@
+# 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


Mime
View raw message