| #!/usr/bin/python |
| # -*- coding: utf-8 -*- |
| # 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. |
| |
| try: |
| import base64 |
| import hashlib |
| import hmac |
| import json |
| import requests |
| import sys |
| import time |
| import urllib |
| import urllib2 |
| from datetime import datetime, timedelta |
| from urllib2 import HTTPError, URLError |
| |
| except ImportError, e: |
| print "Import error in %s : %s" % (__name__, e) |
| import sys |
| sys.exit() |
| |
| |
| def logger_debug(logger, message): |
| if logger is not None: |
| 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'] |
| 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, |
| credentials, protocol, path, expires): |
| response = None |
| error = None |
| |
| if protocol != 'http' and protocol != 'https': |
| error = "Protocol must be 'http' or 'https'" |
| return None, error |
| |
| if args is None: |
| args = {} |
| |
| args["command"] = command |
| args["response"] = "json" |
| args["signatureversion"] = "3" |
| expirationtime = datetime.utcnow() + timedelta(seconds=int(expires)) |
| args["expires"] = expirationtime.strftime('%Y-%m-%dT%H:%M:%S+0000') |
| |
| # 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()) |
| |
| request_url = "&".join(["=".join([r[0], urllib.quote_plus(str(r[1]))]) |
| for r in request]) |
| hashStr = "&".join(["=".join([r[0].lower(), |
| str.lower(urllib.quote_plus(str(r[1]))).replace("+", |
| "%20")]) for r in request]) |
| |
| sig = urllib.quote_plus(base64.encodestring(hmac.new(secretkey, hashStr, |
| hashlib.sha1).digest()).strip()) |
| request_url += "&signature=%s" % sig |
| request_url = "%s://%s:%s%s?%s" % (protocol, host, port, path, request_url) |
| |
| try: |
| logger_debug(logger, "Request sent: %s" % request_url) |
| connection = urllib2.urlopen(request_url) |
| response = connection.read() |
| except HTTPError, e: |
| error = "%s: %s" % (e.msg, e.info().getheader('X-Description')) |
| except URLError, e: |
| error = e.reason |
| |
| logger_debug(logger, "Response received: %s" % response) |
| if error is not None: |
| logger_debug(logger, "Error: %s" % (error)) |
| return response, error |
| |
| return response, error |
| |
| |
| def monkeyrequest(command, args, isasync, asyncblock, logger, host, port, |
| credentials, timeout, protocol, path, expires): |
| 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, credentials, protocol, path, expires) |
| |
| logger_debug(logger, "======== END Request ========\n") |
| |
| if error is not None: |
| return response, error |
| |
| def process_json(response): |
| try: |
| response = json.loads(str(response)) |
| except ValueError, e: |
| logger_debug(logger, "Error processing json: %s" % e) |
| |
| return response |
| |
| response = process_json(response) |
| if response is None: |
| return response, error |
| |
| isasync = isasync and (asyncblock == "true") |
| responsekey = filter(lambda x: 'response' in x, response.keys())[0] |
| |
| if isasync and 'jobid' in response[responsekey]: |
| jobid = response[responsekey]['jobid'] |
| command = "queryAsyncJobResult" |
| request = {'jobid': jobid} |
| timeout = int(timeout) |
| pollperiod = 2 |
| progress = 1 |
| while timeout > 0: |
| print '\r' + '.' * progress, |
| sys.stdout.flush() |
| time.sleep(pollperiod) |
| timeout = timeout - pollperiod |
| progress += 1 |
| logger_debug(logger, "Job %s to timeout in %ds" % (jobid, timeout)) |
| response, error = make_request(command, request, logger, |
| host, port, credentials, |
| protocol, path, expires) |
| if error is not None: |
| return response, error |
| |
| response = process_json(response) |
| responsekeys = filter(lambda x: 'response' in x, response.keys()) |
| |
| if len(responsekeys) < 1: |
| continue |
| |
| result = response[responsekeys[0]] |
| jobstatus = result['jobstatus'] |
| if jobstatus == 2: |
| jobresult = result["jobresult"] |
| error = "\rAsync job %s failed\nError %s, %s" % ( |
| jobid, jobresult["errorcode"], jobresult["errortext"]) |
| return response, error |
| elif jobstatus == 1: |
| print "\r" + " " * progress, |
| return response, error |
| else: |
| logger_debug(logger, "We should not arrive here!") |
| sys.stdout.flush() |
| |
| error = "Error: Async query timeout occurred for jobid %s" % jobid |
| |
| return response, error |