From 0ad61438742a519959378a7b026f3d4aa2529b4e Mon Sep 17 00:00:00 2001 From: Megan Guiney Date: Thu, 31 Aug 2017 19:43:04 -0700 Subject: [PATCH] refstack result update script bugfix fixed codeflow and RESTful API call errors. Change-Id: I8cfe07b972588fda453dec4ef3651e500badc986 --- tools/update-rs-db.py | 223 +++++++++++++++++++++++++++-------------- tools/update-rs-db.rst | 86 ++++++++++------ 2 files changed, 207 insertions(+), 102 deletions(-) diff --git a/tools/update-rs-db.py b/tools/update-rs-db.py index e88a7b07..ecb083aa 100755 --- a/tools/update-rs-db.py +++ b/tools/update-rs-db.py @@ -1,48 +1,76 @@ -#!/usr/bin/python +#!/usr/bin/env python +# Copyright (c) 2017 OpenStack Foundation +# All Rights Reserved. +# +# Licensed 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. + +""" +Test result update & verify script for the local refstack database. +""" + +import os import argparse import datetime import requests -import os +from collections import namedtuple import json def getData(entry): - guidelines = ["2015.03", "2015.04", "2015.05", "2015.07", "2016.01", - "2016.08", "2017.01"] - components = ["Platform", "Compute", "Storage"] + """Extract and reformat data from a product data csv""" + guidelines = ['2015.03', '2015.04', '2015.05', '2015.07', '2016.01', + '2016.08', '2017.01', '2017.09'] + components = ['platform', 'compute', 'storage', 'object'] if len(entry) < 10: return None, None, None - if entry[9] != "" and entry[9] != " ": - refstackLink = entry[9] - testId = refstackLink.split("/")[-1] + refstackLink = entry[9].strip() + guideline = entry[4].strip() + target = entry[5].lower().strip() + if refstackLink: + testId = refstackLink.split('/')[-1] else: refstackLink = None testId = None - if entry[4] != "" and entry[4] != " " and entry[4] in guidelines: - guideline = entry[4] - else: + if guideline not in guidelines: guideline = None - if entry[5] != "" and entry[5] != " " and entry[5] in components: - target = entry[5].lower() - if target == "storage": - target = "object" - else: + if target not in components: target = None + elif target == "storage": + target = "object" return testId, guideline, target def linkChk(link, token): - print("checking result with a test ID of: " + link.split("/")[-1]) + """Check existence of and access to api result link""" + print("now checking result: " + link) if not link: return False try: if " " in link: return False - response = requests.get( - link, headers={'Authorization': 'Bearer ' + token}) + headers = {'Authorization': 'Bearer ' + token} + response = requests.get(link, headers) if response.status_code == 200: return json.loads(response.text) + elif response.status_code == 401 or response.status_code == 403: + print("Authentication Failed. link check response code: " + + str(response.status_code)) + return False + elif response.status_code == 400: + print("Malformed Request. link response code: " + + str(response.status_code)) + return False else: print("Link check response_status_code=" + str(response.status_code)) @@ -52,50 +80,98 @@ def linkChk(link, token): return False -def updateResult(apiLink, target, guideline, token, results_log): - success = True - with open(results_log, 'a') as logfile: - logfile.write(str(datetime.datetime.now()) + ",") - response = requests.post(apiLink + '/meta/shared', headers={ - 'Authorization': 'Bearer ' + token}, data='true') - if response.status_code != 201: - print("Update shared status response_status_code=" + - str(response.status_code)) - logfile.write(apiLink + ",0,") - success = False +def updateField(header, apiLink, raw_data): + """Update a given metadata field""" + valid_keytype = ['shared', 'guideline', 'target'] + keytype = raw_data.type + keyval = raw_data.value + if keytype not in valid_keytype or not keyval: + updresult = "%s keypair does not exist" % (keytype) + return updresult, False + link = apiLink.strip() + '/meta/' + keytype + response = requests.post(link, data=keyval, headers=header) + if response.status_code != 201: + print('update response status code=%d' % + response.status_code) + print('update response text=' + response.text) + updresult = ("%s field update failed. reason: %s" % + (keytype, response.text.replace(',', ' '))) + return updresult, False + else: + updresult = "%s field update successful," % (keytype) + return updresult, True + + +def updateResult(apiLink, target, guideline, token, record): + """Update metadata for result and verify if all updates are a success""" + MetadataField = namedtuple('MetadataField', ['type', 'value']) + success = [] + header = {'Authorization': 'Bearer ' + token} + with open(record, 'a') as r: + r.write(str(datetime.datetime.now()) + "," + apiLink + ",") + # update the shared field + data = MetadataField('shared', 'true') + shared_result, shared_status = updateField(header, apiLink, data) + r.write(shared_result) + success.append(shared_status) + # update the target field + data = MetadataField('target', target) + target_result, target_status = updateField(header, apiLink, data) + r.write(target_result) + success.append(target_status) + # update the guideline field + data = MetadataField('guideline', guideline + '.json') + gl_result, gl_status = updateField(header, apiLink, data) + r.write(gl_result) + success.append(gl_status) + if not all(success): + r.write('unable to verify.\n') + return False + # if there were no update failures, we can verify the result + # this is the operation most likely to fail, so extra checks are + # in order + print('Test Result updated successfully. Attempting verification.') + try: + response = requests.put(apiLink, + json={'verification_status': 1}, + headers=header) + except Exception as ex: + print('Exception raised while verifying test result: %s' % + (str(ex))) + r.write('verification failed: %s\n' % (str(ex))) + return False + updated = verification_chk(apiLink, header) + if response.status_code not in (200, 201): + print('verification failure status code=%d' % + response.status_code) + print('verification failure detail=%s' % + response.text) + r.write('verification unsuccessful: detail: %s\n' % + (response.text)) + return False + elif not updated: + print("verification_status field failed to update") + r.write('verification status update failed. detail: %s\n' % + (response.text)) + return False else: - logfile.write(apiLink + ",1,") - if ".json" not in guideline: - guideline = str(guideline) + ".json" - response = requests.post(apiLink + '/meta/guideline', headers={ - 'Authorization': 'Bearer ' + token}, data=guideline) - if response.status_code != 201: - print("Update guideline response_status_code=" + - str(response.status_code)) - logfile.write(guideline + ",0,") - success = False + print('Test result verified!\n') + r.write('Test result successfully verified\n') + return True + + +def verification_chk(link, header): + try: + response = requests.get(link, header) + status = int(response.json()['verification_status']) + if status == 1: + return True else: - logfile.write(guideline + ",1,") - response = requests.post(apiLink + '/meta/target', headers={ - 'Authorization': 'Bearer ' + token}, data=target) - if response.status_code != 201: - print("Update target response_status_code=" + - str(response.status_code)) - logfile.write(target + ",0,") - success = False - else: - logfile.write(target + ",1,") - if success: - print("test result updated. Verifying.") - response = requests.put(apiLink, headers={ - 'Authorization': 'Bearer ' + token}, - json={'verification_status': 1}) - if response.status_code != 201: - success = False - else: - print("Test result verified.") - logfile.write(str(int(success)) + '\n') - return success + return False + except Exception as ex: + print('Exception raised while ensuring update of ' + + 'verification status: ' + str(ex)) + return False def main(): @@ -111,12 +187,13 @@ def main(): help="the base URL of the endpoint. ex: http://examplerefstack.com/v1") parser.add_argument("--token", "-t", metavar="t", type=str, action="store", required=True, help="API auth token") - parser.add_argument("--logfile", "-l", metavar="l", type=str, + parser.add_argument("--record", "-r", metavar="r", type=str, action="store", default="verification_results.csv", - help="name of logfile to output data into") + help="name of file to output update & verification " + + " run record data into") result = parser.parse_args() infile = result.file - logfile = result.logfile + record = result.record endpoint = result.endpoint token = result.token with open(infile) as f: @@ -124,30 +201,28 @@ def main(): linect = linect + 1 entry = line.split(",") testId, guideline, target = getData(entry) - if testId is None or guideline is None or target is None: + if None in (testId, guideline, target): print( "entry found at line " + str(linect) + - " cannot be updated and verified: entry incomplete.") + " cannot be updated and verified: entry incomplete.\n") else: apiLink = os.path.join(endpoint, 'results', testId) testResult = linkChk(apiLink, token) if testResult: if testResult.get('verification_status'): - print( - "Result has already been verified; nothing to do.") + print("Result has been verified.\n") else: print( - "Result link is valid. Updating result with ID " + - testId) + "Result link is valid. Updating...") success = updateResult(apiLink, target, guideline, - token, logfile) + token, record) if not success: print("update of the results with the ID " + testId + " failed. please recheck your " + - "spreadsheet and try again") + "spreadsheet and try again\n") else: - print("the test result " + testId + - " cannot be verified due to a broken result link.") + print("the test result " + testId + " cannot be " + + "verified due to a link verification failure\n") main() diff --git a/tools/update-rs-db.rst b/tools/update-rs-db.rst index 8dc040a6..bd5dc567 100644 --- a/tools/update-rs-db.rst +++ b/tools/update-rs-db.rst @@ -7,29 +7,51 @@ successful in the usage of the script update-rs-db.py. The script can be run using the following formatting: "./update-rs-db.py --file /tmp/datasource.csv --endpoint -http://example.com:8000/v1 --token " +http://example.com:8000/v1 --token ". In order to +successfully update and verify results, you will need admin rights +for the refstack server in question. Instructions on how to get +these for your local install can be found at https://github.com/openstack/refstack/blob/master/doc/source/refstack.rst#optional-configure-foundation-organization-and-group This script updates RefStack tests as verified given a specific spreadsheet. The columns in this spreadsheet are, in this order: - - Company Name - - Product Name - - Type (Distribution, Public, or Private) - - Region - - Guideline - - Component (Compute, Platform, or Object) - - Reported Release - - Passed Release - - Federated identity (yes/no) - - Refstack Link - - Zendesk Link - - Marketplace Link - - License Date - - Update Product (yes/no) - - Contacts - - Notes - - License Link - - Active (1 or 0) - - Public (1 or 0) + + * Company Name + + * Product Name + + * Type (Distribution, Public, or Private) + + * Region + + * Guideline + + * Component (Compute, Platform, or Object) + + * Reported Release + + * Passed Release + + * Federated identity (yes/no) + + * Refstack Link + + * Zendesk Link + + * Marketplace Link + + * License Date + + * Update Product (yes/no) + + * Contacts + + * Notes + + * License Link + + * Active (1 or 0) + + * Public (1 or 0) The data is pulled from a csv file. The default csv name is toadd.csv, but using the -f flag, we can use csv of a different filename. @@ -60,11 +82,19 @@ The status of each of these steps will be output to "verification_status.csv" by default. A '1' will denote that the resource was successfully updated while a '0' will denote that the resource was not successfully updated. The order of fields of this file are as follows: -- Date modified -- API link -- Shared update status -- Guideline -- Guideline update success status -- Target -- Target update success status -- Verification update success status + + * Date modified + + * API link + + * Shared update status + + * Guideline + + * Guideline update success status + + * Target + + * Target update success statu + + * Verification update success status