d18f8ad221
There were three main issues in the code from refstack repository that
were preventing refstack jobs from working:
1) Improper handling of sessions in the database API.
2) PEP8 job was failing because of outdated version of flake8.
3) The new version of cryptography library doesn't support signer() and
verifier() functions.
Issue #1 was solved by using the get_session() function as a context
manager instead of using session.begin() as a context manager. Using
session.begin() as a context manager does not ensure that the session
will be closed at the end of the context (see "Opening and Closing
a Session" and "Framing out a begin / commit / rollback block"
here [1]).
Issue #2 was solved by updating the libraries in
test-requirements.txt file. This change also forces flake8 to ignore
some pep8 errors (similar to the ones ignored in tempest project).
Issue #3 was solved by using the sign() and verify() functions instead
of verifier() and signer() functions [2].
Related Taiga issues:
- https://tree.taiga.io/project/openstack-interop-working-group/issue/77
- https://tree.taiga.io/project/openstack-interop-working-group/issue/79
[1] https://docs.sqlalchemy.org/en/14/orm/session_basics.html
[2] e71c0df301
Change-Id: If98670475b371d1ece7c877a0eea3158f6c1b3f5
288 lines
11 KiB
Python
Executable File
288 lines
11 KiB
Python
Executable File
#!/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 argparse
|
|
import datetime
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
from collections import namedtuple
|
|
import jwt
|
|
import requests
|
|
|
|
|
|
def generate_token(keyfile, _id):
|
|
# get private key in string format
|
|
with open(keyfile) as pemfile:
|
|
secret = pemfile.read().rstrip()
|
|
exp = datetime.timedelta(seconds=100500)
|
|
payload =\
|
|
{
|
|
"user_openid": _id,
|
|
"exp": datetime.datetime.now() + exp
|
|
}
|
|
token = jwt.encode(payload, secret, algorithm="RS256")
|
|
with open("./token", "w+") as tokenfile:
|
|
tokenfile.write(str(token))
|
|
print("token stored in the current working directory.")
|
|
|
|
|
|
def testAuth(link, tokenfile):
|
|
auth_test_url = link.split("/v1")[0] + "/#/profile"
|
|
with open(tokenfile) as tokenfile:
|
|
token = tokenfile.read().strip()
|
|
headers = {"Authorization": "Bearer " + token}
|
|
response = requests.get(auth_test_url, headers)
|
|
if response.status_code == 200:
|
|
return True, token
|
|
else:
|
|
print("token auth failed. status code = %d" % (response.status_code))
|
|
print("auth failure detail: %s" % (response.text))
|
|
return False, None
|
|
|
|
|
|
def getData(entry):
|
|
guidelines = ["2015.03", "2015.04", "2015.05", "2015.07", "2016.01",
|
|
"2016.08", "2017.01", "2017.09"]
|
|
# NOTE: Storage is an alias of Object
|
|
components = ["platform", "compute", "storage", "object"]
|
|
if len(entry) < 10:
|
|
return None, None, None
|
|
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 guideline not in guidelines:
|
|
guideline = None
|
|
if target not in components:
|
|
target = None
|
|
elif target == "storage":
|
|
target = "object"
|
|
return testId, guideline, target
|
|
|
|
|
|
def linkChk(link, token):
|
|
"""Check existence of and access to api result link"""
|
|
print("checking result with a test ID of: %s" % (link.split("/")[-1]))
|
|
if not link:
|
|
return False
|
|
try:
|
|
if " " in link:
|
|
return False
|
|
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: %d" %
|
|
(response.status_code))
|
|
return False
|
|
elif response.status_code == 400:
|
|
print("Malformed Request. link response code: %d" %
|
|
(response.status_code))
|
|
return False
|
|
else:
|
|
print("Link check response_status_code = %d" %
|
|
(response.status_code))
|
|
print("Link check response detail: %s" % (response.text))
|
|
return False
|
|
except requests.exceptions as err:
|
|
print(err)
|
|
return 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=%s" % (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:
|
|
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:
|
|
return False
|
|
except Exception as ex:
|
|
print(
|
|
"Exception raised while ensuring verification status update: %s" %
|
|
str(ex))
|
|
return False
|
|
|
|
|
|
def main():
|
|
linect = 0
|
|
parser = argparse.ArgumentParser(
|
|
"Update the internal RefStack db using a csv file")
|
|
# token handling options- we either need the path of a working token,
|
|
# or the data to generate a new token
|
|
token_flags = parser.add_mutually_exclusive_group(required=True)
|
|
token_flags.add_argument("--tokenfile", type=str, action="store",
|
|
help=("Absolute path to a json web token to "
|
|
"use to auth to the RefStack API"))
|
|
token_flags.add_argument("--generate", nargs=2,
|
|
metavar=("ssh-key", "openstack-id"),
|
|
help=("data needed to create a new auth token "
|
|
"ssh - key should be an absolute path to "
|
|
"a rsa ssh key. openstack - id indicates "
|
|
"an openstackid url to use for auth. "
|
|
"example: "
|
|
"https://openstackid.org/<your id >"))
|
|
# non token-related flags
|
|
parser.add_argument("--file", "-f", metavar="f", type=str, action="store",
|
|
required=True,
|
|
help="csv source for the data to use in updates")
|
|
parser.add_argument(
|
|
"--endpoint", "-e", metavar="e",
|
|
type=str, action="store", required=True,
|
|
help="the base URL of the endpoint. ex: http://examplerefstack.com/v1")
|
|
parser.add_argument("--record", "-r", metavar="r", type=str,
|
|
action="store", default="verification_results.csv",
|
|
help=("name of file to output update & verification "
|
|
"run record data into"))
|
|
args = parser.parse_args()
|
|
infile = args.file
|
|
record = args.record
|
|
endpoint = args.endpoint
|
|
if args.generate:
|
|
keypath = args.generate[0]
|
|
_id = args.generate[1]
|
|
generate_token(keypath, _id)
|
|
tokenfile = "./token"
|
|
else:
|
|
tokenfile = args.tokenfile
|
|
auth_success, token = testAuth(endpoint, tokenfile)
|
|
if not auth_success:
|
|
print(("Please enter either a valid token or an openstackid and the "
|
|
"absolute path to an rsa ssh key."))
|
|
sys.exit(1)
|
|
with open(infile) as f:
|
|
for line in f:
|
|
linect = linect + 1
|
|
entry = line.split(",")
|
|
testId, guideline, target = getData(entry)
|
|
if None in (testId, guideline, target):
|
|
print(("entry found at line %d cannot be updated and "
|
|
"verified: entry incomplete.\n") % (linect))
|
|
else:
|
|
apiLink = os.path.join(endpoint, "results", testId)
|
|
testResult = linkChk(apiLink, token)
|
|
if testResult:
|
|
if testResult.get("verification_status"):
|
|
print("Result has been verified.\n")
|
|
else:
|
|
print(
|
|
"Result link is valid. Updating result with ID %s"
|
|
% (testId))
|
|
success = updateResult(apiLink, target, guideline,
|
|
token, record)
|
|
if not success:
|
|
print(("update of the results with the ID %s "
|
|
"failed. please recheck your spreadsheet "
|
|
"and try again" % (testId)))
|
|
else:
|
|
print(("the test result: % s cannot be updated or "
|
|
"verified due to a broken result link." % (testId)))
|
|
|
|
|
|
main()
|