You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ch...@apache.org on 2013/10/16 05:17:53 UTC

git commit: Add support for using username and password to cloudmonkey. The interactive CLI user can call 'login' and 'logout' to start and terminate a session. If the apikey and secretkey are not supplied in the config, then the username and password ar

Updated Branches:
  refs/heads/username_password_support [created] 61901f20d


Add support for using username and password to cloudmonkey.
The interactive CLI user can call 'login' and 'logout' to start and terminate a session.
If the apikey and secretkey are not supplied in the config, then the username and password are used.
If the session has expired, the CLI will automatically login and obtain a session


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

Branch: refs/heads/username_password_support
Commit: 61901f20d23383589582620779ad55034b08c0a0
Parents: 34665b3
Author: Chiradeep Vittal <ch...@apache.org>
Authored: Tue Oct 15 19:21:26 2013 -0700
Committer: Chiradeep Vittal <ch...@apache.org>
Committed: Tue Oct 15 19:21:26 2013 -0700

----------------------------------------------------------------------
 cloudmonkey/cloudmonkey.py |  28 ++++++++++-
 cloudmonkey/config.py      |   2 +
 cloudmonkey/requester.py   | 108 +++++++++++++++++++++++++++++++++++++---
 3 files changed, 131 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack-cloudmonkey/blob/61901f20/cloudmonkey/cloudmonkey.py
----------------------------------------------------------------------
diff --git a/cloudmonkey/cloudmonkey.py b/cloudmonkey/cloudmonkey.py
index b465bec..53ca066 100644
--- a/cloudmonkey/cloudmonkey.py
+++ b/cloudmonkey/cloudmonkey.py
@@ -36,6 +36,8 @@ try:
     from prettytable import PrettyTable
     from printer import monkeyprint
     from requester import monkeyrequest
+    from requester import login
+    from requester import logout
 except ImportError, e:
     print("Import error in %s : %s" % (__name__, e))
     import sys
@@ -73,6 +75,8 @@ class CloudMonkeyShell(cmd.Cmd, object):
         self.config_file = cfile
         self.config_options = read_config(self.get_attr, self.set_attr,
                                           self.config_file)
+        self.credentials = {'apikey':self.apikey, 'secretkey': self.secretkey, 
+                            'username': self.username, 'password': self.password}
         self.loadcache()
         self.prompt = self.prompt.strip() + " "  # Cosmetic fix for prompt
 
@@ -90,6 +94,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
             logger.debug("Error: Unable to read history. " + str(e))
         atexit.register(readline.write_history_file, self.history_file)
 
+
     def get_attr(self, field):
         return getattr(self, field)
 
@@ -253,7 +258,7 @@ class CloudMonkeyShell(cmd.Cmd, object):
         response, error = monkeyrequest(command, args, isasync,
                                         self.asyncblock, logger,
                                         self.host, self.port,
-                                        self.apikey, self.secretkey,
+                                        self.credentials,
                                         self.timeout, self.protocol, self.path)
         if error is not None:
             self.monkeyprint(error)
@@ -414,6 +419,25 @@ class CloudMonkeyShell(cmd.Cmd, object):
         return [s[offs:] for s in self.config_options
                 if s.startswith(mline)]
 
+    def do_login(self, args):
+        """
+        Login using stored credentials. Starts a session to be reused for subsequent api calls
+        """
+        url = "%s://%s:%s%s" % (self.protocol, self.host, self.port, self.path)
+        session, sessionkey = login(url, self.username, self.password)
+        self.credentials['session'] = session
+        self.credentials['sessionkey'] = sessionkey
+
+    def do_logout(self, args):
+        """
+        Logout of session started with login with username and password
+        """
+        url = "%s://%s:%s%s" % (self.protocol, self.host, self.port, self.path)
+        logout(url, self.credentials['session'])
+        self.credentials['session'] = None
+        self.credentials['sessionkey'] = None
+
+
     def pipe_runner(self, args):
         if args.find(' |') > -1:
             pname = self.program_name
@@ -529,7 +553,9 @@ def main():
         print __description__, "(%s)" % __projecturl__
         sys.exit(0)
 
+
     shell = CloudMonkeyShell(sys.argv[0], options.cfile)
+
     if len(args) > 0:
         shell.onecmd(' '.join(args))
     else:

http://git-wip-us.apache.org/repos/asf/cloudstack-cloudmonkey/blob/61901f20/cloudmonkey/config.py
----------------------------------------------------------------------
diff --git a/cloudmonkey/config.py b/cloudmonkey/config.py
index 2f91608..ae56bfa 100644
--- a/cloudmonkey/config.py
+++ b/cloudmonkey/config.py
@@ -66,6 +66,8 @@ config_fields['server']['timeout'] = '3600'
 # user
 config_fields['user']['apikey'] = ''
 config_fields['user']['secretkey'] = ''
+config_fields['user']['username'] = ''
+config_fields['user']['password'] = ''
 
 
 def write_config(get_attr, config_file, first_time=False):

http://git-wip-us.apache.org/repos/asf/cloudstack-cloudmonkey/blob/61901f20/cloudmonkey/requester.py
----------------------------------------------------------------------
diff --git a/cloudmonkey/requester.py b/cloudmonkey/requester.py
index b06e1fc..93400e1 100644
--- a/cloudmonkey/requester.py
+++ b/cloudmonkey/requester.py
@@ -26,6 +26,7 @@ try:
     import os
     import pdb
     import re
+    import requests
     import shlex
     import sys
     import time
@@ -45,8 +46,95 @@ def logger_debug(logger, message):
         logger.debug(message)
 
 
+def login(url, username, password):
+    """
+    Login and obtain a session to be used for subsequent API calls
+    Wrong username/password leads to HTTP error code 531
+    """
+    args = {}
+
+    args["command"] = 'login'
+    args["username"] = username
+    args["password"] = password
+    args["domain"] = "/"
+    args["response"] = "json"
+
+    sessionkey = ''
+    session = requests.Session()
+
+    resp = session.post(url, params=args)
+    if resp.status_code == 200:
+        sessionkey = resp.json()['loginresponse']['sessionkey']
+        userid = resp.json()['loginresponse']['userid']
+    elif resp.status_code == 531:
+        print "Error authenticating at %s, with username: %s, and password: %s" % (url, username, password)
+        session = None
+        sessionkey = None
+    else:
+        resp.raise_for_status()
+
+
+    return session, sessionkey
+
+
+def logout(url, session):
+    if session is None:
+        return
+    session.get(url, params={'command': 'logout'})
+
+def make_request_with_password(command, args, logger, url, credentials):
+
+    error = None
+    username = credentials['username']
+    password = credentials['password']
+
+    if not (username and password):
+        error = "Username and password cannot be empty"
+        result = None
+        return result, error
+
+    tries = 0
+    retry = True
+
+    while tries < 2 and retry:
+        sessionkey = credentials.get('sessionkey')
+        session = credentials.get('session')
+        tries += 1
+    
+        #obtain a valid session if not supplied
+        if not (session and sessionkey):
+            session, sessionkey = login(url, username, password)
+            if not (session and sessionkey):
+                return None, 'Error authenticating'
+            credentials['session'] = session
+            credentials['sessionkey'] = sessionkey
+
+        args['sessionkey'] = sessionkey
+    
+        #make the api call
+        resp = session.get(url, params=args)
+        result = resp.text
+        logger_debug(logger, "Response received: %s" % resp.text)
+
+        if resp.status_code == 200: #success
+            retry = False
+            break
+        if resp.status_code == 401: #sessionkey is wrong
+            credentials['session'] = None
+            credentials['sessionkey'] = None
+            continue
+
+        if resp.status_code != 200 and resp.status_code != 401:
+            error = "%s: %s" % (str(resp.status_code), resp.headers.get('X-Description'))
+            result = None
+            retry = False
+            
+
+    return result, error
+
+
 def make_request(command, args, logger, host, port,
-                 apikey, secretkey, protocol, path):
+                 credentials, protocol, path):
     response = None
     error = None
 
@@ -58,8 +146,16 @@ def make_request(command, args, logger, host, port,
         args = {}
 
     args["command"] = command
-    args["apiKey"] = apikey
     args["response"] = "json"
+
+    #try to use the apikey/secretkey method by default
+    #if not present, use the username/password method
+    if not credentials['apikey']:
+        url = "%s://%s:%s%s" % (protocol, host, port, path)
+        return make_request_with_password(command, args, logger, url, credentials)
+
+    args['apikey'] = credentials['apikey']
+    secretkey = credentials['secretkey']
     request = zip(args.keys(), args.values())
     request.sort(key=lambda x: x[0].lower())
 
@@ -92,13 +188,13 @@ def make_request(command, args, logger, host, port,
 
 
 def monkeyrequest(command, args, isasync, asyncblock, logger, host, port,
-                  apikey, secretkey, timeout, protocol, path):
+                  credentials, timeout, protocol, path):
     response = None
     error = None
     logger_debug(logger, "======== START Request ========")
     logger_debug(logger, "Requesting command=%s, args=%s" % (command, args))
-    response, error = make_request(command, args, logger, host, port,
-                                   apikey, secretkey, protocol, path)
+    response, error = make_request(command, args, logger, host, port, credentials, protocol, path)
+
     logger_debug(logger, "======== END Request ========\n")
 
     if error is not None:
@@ -135,7 +231,7 @@ def monkeyrequest(command, args, isasync, asyncblock, logger, host, port,
             progress += 1
             logger_debug(logger, "Job %s to timeout in %ds" % (jobid, timeout))
             response, error = make_request(command, request, logger,
-                                           host, port, apikey, secretkey,
+                                           host, port, credentials,
                                            protocol, path)
             if error is not None:
                 return response, error


Re: git commit: Add support for using username and password to cloudmonkey

Posted by Shanker Balan <sh...@shapeblue.com>.
On 16-Oct-2013, at 8:47 am, chiradeep@apache.org wrote:

> Updated Branches:
>  refs/heads/username_password_support [created] 61901f20d
>
>
> Add support for using username and password to cloudmonkey.
> The interactive CLI user can call 'login' and 'logout' to start and terminate a session.
> If the apikey and secretkey are not supplied in the config, then the username and password are used.
> If the session has expired, the CLI will automatically login and obtain a session


Thanks. Solves a TON of automation problems. :)


--
@shankerbalan

M: +91 98860 60539 | O: +91 (80) 67935867
shanker.balan@shapeblue.com | www.shapeblue.com | Twitter:@shapeblue
ShapeBlue Services India LLP, 22nd floor, Unit 2201A, World Trade Centre, Bangalore - 560 055

CloudStack Bootcamp Training on 27/28 November, Bangalore
http://www.shapeblue.com/cloudstack-training/




This email and any attachments to it may be confidential and are intended solely for the use of the individual to whom it is addressed. Any views or opinions expressed are solely those of the author and do not necessarily represent those of Shape Blue Ltd or related companies. If you are not the intended recipient of this email, you must neither take any action based upon its contents, nor copy or show it to anyone. Please contact the sender if you believe you have received this email in error. Shape Blue Ltd is a company incorporated in England & Wales. ShapeBlue Services India LLP is a company incorporated in India and is operated under license from Shape Blue Ltd. Shape Blue Brasil Consultoria Ltda is a company incorporated in Brasil and is operated under license from Shape Blue Ltd. ShapeBlue is a registered trademark.