diff --git a/test/cpp/qps/data_script.sh b/test/cpp/qps/data_script.sh new file mode 100644 index 00000000000..51d840f8d80 --- /dev/null +++ b/test/cpp/qps/data_script.sh @@ -0,0 +1,7 @@ +while [ true ] +do + python run_auth_test.py ../../../bins/opt/async_streaming_ping_pong_test sidrakesh@google.com + python run_auth_test.py ../../../bins/opt/async_unary_ping_pong_test sidrakesh@google.com + python run_auth_test.py ../../../bins/opt/sync_streaming_ping_pong_test sidrakesh@google.com + python run_auth_test.py ../../../bins/opt/sync_unary_ping_pong_test sidrakesh@google.com +done \ No newline at end of file diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc index 83860ab9cee..91fe9524cb4 100644 --- a/test/cpp/qps/report.cc +++ b/test/cpp/qps/report.cc @@ -176,8 +176,10 @@ void UserDatabaseReporter::ReportTimes(const ScenarioResult& result) const { } void UserDatabaseReporter::SendData() const { + //send data to performance database int userDataState = userDataClient.sendData(access_token_, test_name_, sys_info_); + //check state of data sending switch(userDataState) { case 1: gpr_log(GPR_INFO, "Data sent to user database successfully"); diff --git a/test/cpp/qps/run_auth_test.py b/test/cpp/qps/run_auth_test.py index 06b064cad0c..c327a505b6f 100755 --- a/test/cpp/qps/run_auth_test.py +++ b/test/cpp/qps/run_auth_test.py @@ -1,3 +1,33 @@ +# +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# #!/usr/bin/python import os @@ -8,6 +38,7 @@ import urllib import json import time import subprocess +import fnmatch CLIENT_ID = '1018396037782-tv81fshn76nemr24uuhuginceb9hni2m.apps.googleusercontent.com' CLIENT_SECRET = '_HGHXg4DAA59r4w4x8p6ARzD' @@ -17,6 +48,7 @@ AUTH_TOKEN_LINK = 'https://www.googleapis.com/oauth2/v3/token' GOOGLE_ACCOUNTS_LINK = 'https://accounts.google.com/o/oauth2/device/code' USER_INFO_LINK = 'https://www.googleapis.com/oauth2/v1/userinfo' +# Fetches JSON reply object, given a url and parameters def fetchJSON(url, paramDict): if len(paramDict) == 0: req = urllib2.Request(url) @@ -33,6 +65,7 @@ def fetchJSON(url, paramDict): return result +# Fetch user info; used to check if access token is valid def getUserInfo(accessToken): url = USER_INFO_LINK + '?access_token=' + accessToken paramDict = {} @@ -41,6 +74,7 @@ def getUserInfo(accessToken): return data +# Returns true if stored access token is valid def isAccessTokenValid(accessToken): data = getUserInfo(accessToken); @@ -49,32 +83,47 @@ def isAccessTokenValid(accessToken): else: return False +# Returns user id given a working access token def getUserId(accessToken): data = getUserInfo(accessToken) email = data['email'] - email = email.split('@')[0].lower() - userId = re.sub('[.]', '', email) + userId = getUserIdFromEmail(email) return userId +# Extracts a unique user id from an email address +def getUserIdFromEmail(email): + email = email.split('@')[0].lower() # take username and convert to lower case + userId = re.sub('[.]', '', email) # remove periods + + return userId + +# Use an existing access token def useAccessToken(userTokFile): with open(userTokFile, "r") as data_file: - data = json.load(data_file) + data = json.load(data_file) # load JSON data from file accessToken = data["access_token"] + # If access token has gone stale, refresh it if not isAccessTokenValid(accessToken): return refreshAccessToken(data["refresh_token"], userTokFile) return accessToken +# refresh stale access token def refreshAccessToken(refreshToken, userTokFile): + # Parameters for request paramDict = {'refresh_token':refreshToken, 'client_id':CLIENT_ID, 'client_secret':CLIENT_SECRET, 'grant_type':'refresh_token'} + # Fetch reply to request JSONBody = fetchJSON(AUTH_TOKEN_LINK, paramDict) data = json.loads(JSONBody) + if not 'access_token' in data: + # Refresh token has gone stale, re-authentication required return reauthenticate() else: + # write fresh access token to tokens file tokenData = {} with open(userTokFile, "r") as data_file: @@ -83,61 +132,108 @@ def refreshAccessToken(refreshToken, userTokFile): with open(userTokFile, "w") as data_file: tokenData['access_token'] = data['access_token'] json.dump(tokenData, data_file) - + + # return fresh access token return data['access_token'] def reauthenticate(): + # Request parameters paramDict = {'client_id':CLIENT_ID, 'scope':'email profile'} JSONBody = fetchJSON(GOOGLE_ACCOUNTS_LINK, paramDict) data = json.loads(JSONBody) print 'User authorization required\n' - print 'Please use the following code in you browser: ', data['user_code'] - print 'Verification URL: ', data['verification_url'] + print 'Please use the following code in you browser: ', data['user_code'] # Code to be entered by user in browser + print 'Verification URL: ', data['verification_url'] # Authentication link print '\nAwaiting user authorization. May take a few more seconds after authorizing...\n' authData = {} while not 'access_token' in authData: + # Request parameters authDict = {'client_id':CLIENT_ID, 'client_secret':CLIENT_SECRET, 'code':data['device_code'], 'grant_type':GRANT_TYPE} JSONBody = fetchJSON(AUTH_TOKEN_LINK, authDict) authData = json.loads(JSONBody) + # If server pinged too quickly, will get slowdown message; need to wait for specified interval time.sleep(data['interval']) + # File to write tokens newUserTokFile = ACCESS_TOKENS_DIR + '/' + getUserId(authData['access_token']) + # Write tokens to file with open(newUserTokFile, "w") as data_file: json.dump(authData, data_file) + # return working access token return authData['access_token'] -def main(): - if not os.path.exists(ACCESS_TOKENS_DIR): - os.makedirs(ACCESS_TOKENS_DIR) - - if len(sys.argv) > 2: - email = sys.argv[2] - else: - email = raw_input('Enter your e-mail id: ') - - email = email.split('@')[0].lower() - userId = re.sub('[.]', '', email) +# Fetch a working access token given user entered email id; authntication may be required +def getAccessToken(email): + # Get unique user id from email address + userId = getUserIdFromEmail(email) + # Token file userTokFile = ACCESS_TOKENS_DIR + '/' + userId accessToken = '' if os.path.exists(userTokFile): + # File containing access token exists; unless refresh token has expired, user authentication will not be required accessToken = useAccessToken(userTokFile) else: + # User authentication required accessToken = reauthenticate() - testName = sys.argv[1].split('/')[-1] - sysInfo = os.popen('lscpu').readlines() + return accessToken + +# If user has not entered full path to test, recursively searches for given test in parent folders +def findTestPath(test): + # If user entered full path to test, return it + if(os.path.isfile(test)): + return test + + testName = test.split('/')[-1] # Extract just test name + testPath = '' + + # Search for test + for root, dirnames, filenames in os.walk('../../../'): + for fileName in fnmatch.filter(filenames, '*'+testName): + testPath = os.path.join(root, fileName) + + return testPath + +def main(): + # If tokens directory does not exist, creates it + if not os.path.exists(ACCESS_TOKENS_DIR): + os.makedirs(ACCESS_TOKENS_DIR) + + if len(sys.argv) > 1: + test = sys.argv[1] + else: + test = raw_input('Enter the test path/name: ') + + if len(sys.argv) > 2: + email = sys.argv[2] + else: + email = raw_input('Enter your e-mail id: ') + + try: + # Fetch working access token + accessToken = getAccessToken(email) + except Exception, e: + print 'Error in authentication' try: - subprocess.call([sys.argv[1], '--access_token='+accessToken, '--test_name='+testName, '--sys_info='+str(sysInfo).strip('[]')]) + testPath = findTestPath(test) # Get path to test + testName = testPath.split('/')[-1] # Get test name + + # Fetch system information + sysInfo = os.popen('lscpu').readlines() + + # Run the test + subprocess.call([testPath, '--access_token='+accessToken, '--test_name='+testName, '--sys_info='+str(sysInfo).strip('[]')]) except OSError: - print 'Could not execute the test, please check test path' + print 'Could not execute the test, please check test name' + if __name__ == "__main__": main() \ No newline at end of file diff --git a/test/cpp/qps/user_data_client.cc b/test/cpp/qps/user_data_client.cc index f56b79ee4a6..a8ced7559d7 100644 --- a/test/cpp/qps/user_data_client.cc +++ b/test/cpp/qps/user_data_client.cc @@ -36,19 +36,23 @@ namespace grpc { namespace testing { +//sets the client and server config information void UserDataClient::setConfigs(const ClientConfig& clientConfig, const ServerConfig& serverConfig) { clientConfig_ = clientConfig; serverConfig_ = serverConfig; } +//sets the QPS void UserDataClient::setQPS(double QPS) { QPS_ = QPS; } +//sets the QPS per core void UserDataClient::setQPSPerCore(double QPSPerCore) { QPSPerCore_ = QPSPerCore; } +//sets the 50th, 90th, 95th, 99th and 99.9th percentile latency void UserDataClient::setLatencies(double percentileLatency50, double percentileLatency90, double percentileLatency95, double percentileLatency99, double percentileLatency99Point9) { percentileLatency50_ = percentileLatency50; @@ -58,6 +62,7 @@ void UserDataClient::setLatencies(double percentileLatency50, double percentileL percentileLatency99Point9_ = percentileLatency99Point9; } +//sets the server and client, user and system times void UserDataClient::setTimes(double serverSystemTime, double serverUserTime, double clientSystemTime, double clientUserTime) { serverSystemTime_ = serverSystemTime; @@ -66,18 +71,23 @@ void UserDataClient::setTimes(double serverSystemTime, double serverUserTime, clientUserTime_ = clientUserTime; } +//sends the data to the performancew database server int UserDataClient::sendData(std::string access_token, std::string test_name, std::string sys_info) { - + //Data record request object SingleUserRecordRequest singleUserRecordRequest; + + //setting access token, name of the test and the system information singleUserRecordRequest.set_access_token(access_token); singleUserRecordRequest.set_test_name(test_name); singleUserRecordRequest.set_sys_info(sys_info); + //setting configs *(singleUserRecordRequest.mutable_client_config()) = clientConfig_; *(singleUserRecordRequest.mutable_server_config()) = serverConfig_; Metrics* metrics = singleUserRecordRequest.mutable_metrics(); + //setting metrcs in data record request if(QPS_ != DBL_MIN) metrics->set_qps(QPS_); if(QPSPerCore_ != DBL_MIN) metrics->set_qps_per_core(QPSPerCore_); if(percentileLatency50_ != DBL_MIN) metrics->set_perc_lat_50(percentileLatency50_); @@ -95,9 +105,9 @@ int UserDataClient::sendData(std::string access_token, std::string test_name, st Status status = stub_->RecordSingleClientData(&context, singleUserRecordRequest, &singleUserRecordReply); if (status.IsOk()) { - return 1; + return 1; //data sent to database successfully } else { - return -1; + return -1; //error in data sending } } } //testing diff --git a/test/cpp/qps/user_data_client.h b/test/cpp/qps/user_data_client.h index c99b7e9b3a3..c2e07ef5cd9 100644 --- a/test/cpp/qps/user_data_client.h +++ b/test/cpp/qps/user_data_client.h @@ -49,6 +49,7 @@ namespace grpc{ namespace testing { +//Manages data sending to performance database server class UserDataClient { public: UserDataClient(std::shared_ptr channel) @@ -56,18 +57,24 @@ public: ~UserDataClient() {} + //sets the client and server config information void setConfigs(const ClientConfig& clientConfig, const ServerConfig& serverConfig); + //sets the QPS void setQPS(double QPS); + //sets the QPS per core void setQPSPerCore(double QPSPerCore); + //sets the 50th, 90th, 95th, 99th and 99.9th percentile latency void setLatencies(double percentileLatency50, double percentileLatency90, double percentileLatency95, double percentileLatency99, double percentileLatency99Point9); + //sets the server and client, user and system times void setTimes(double serverSystemTime, double serverUserTime, double clientSystemTime, double clientUserTime); + //sends the data to the performancew database server int sendData(std::string access_token, std::string test_name, std::string sys_info); private: