test/bdevio: add perform_tests RPC

New RPC added specifically to the bdevio app.
It will launch all tests on all targets by default.
When optional parameter for bdev name provided is supplied
tests will be ran against just that bdev.
After finishing, the application will await another RPC.

stop_init_thread() and __run_ut_thread now pass json request.

Added separate test.py to not pollute rpc.py with custom RPC
just for the bdevio application.

Added '-w' argument to the bdevio app, so that instead
of performing the tests immidietly - it awaits RPC.
After a lot of changes to blockdev.sh, we might end up
removing this argument and just use it as default.

Change-Id: I82e52352bdf8082c1712caa223ad5ab78aa4e7fa
Signed-off-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/455200
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Paul Luse <paul.e.luse@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Tomasz Zawadzki 2019-05-23 03:53:44 -04:00 committed by Jim Harris
parent 8c0c8e5333
commit 7140c2f710
2 changed files with 197 additions and 6 deletions

View File

@ -39,6 +39,9 @@
#include "spdk/log.h"
#include "spdk/thread.h"
#include "spdk/event.h"
#include "spdk/rpc.h"
#include "spdk/util.h"
#include "spdk/string.h"
#include "CUnit/Basic.h"
@ -52,6 +55,7 @@ pthread_cond_t g_test_cond;
static uint32_t g_lcore_id_init;
static uint32_t g_lcore_id_ut;
static uint32_t g_lcore_id_io;
static bool g_wait_for_tests = false;
struct io_target {
struct spdk_bdev *bdev;
@ -71,6 +75,7 @@ struct bdevio_request {
struct io_target *g_io_targets = NULL;
struct io_target *g_current_io_target = NULL;
static void rpc_perform_tests_cb(unsigned num_failures, struct spdk_jsonrpc_request *request);
static void
execute_spdk_function(spdk_event_fn fn, void *arg1, void *arg2)
@ -938,18 +943,24 @@ static void
__stop_init_thread(void *arg1, void *arg2)
{
unsigned num_failures = (unsigned)(uintptr_t)arg1;
struct spdk_jsonrpc_request *request = arg2;
bdevio_cleanup_targets();
if (g_wait_for_tests) {
/* Do not stop the app yet, wait for another RPC */
rpc_perform_tests_cb(num_failures, request);
return;
}
spdk_app_stop(num_failures);
}
static void
stop_init_thread(unsigned num_failures)
stop_init_thread(unsigned num_failures, struct spdk_jsonrpc_request *request)
{
struct spdk_event *event;
event = spdk_event_allocate(g_lcore_id_init, __stop_init_thread,
(void *)(uintptr_t)num_failures, NULL);
(void *)(uintptr_t)num_failures, request);
spdk_event_call(event);
}
@ -1032,6 +1043,7 @@ __setup_ut_on_single_target(struct io_target *target)
static void
__run_ut_thread(void *arg1, void *arg2)
{
struct spdk_jsonrpc_request *request = arg2;
int rc = 0;
struct io_target *target;
unsigned num_failures;
@ -1039,7 +1051,7 @@ __run_ut_thread(void *arg1, void *arg2)
if (CU_initialize_registry() != CUE_SUCCESS) {
/* CUnit error, probably won't recover */
rc = CU_get_error();
stop_init_thread(-rc);
stop_init_thread(-rc, request);
}
target = g_io_targets;
@ -1047,7 +1059,7 @@ __run_ut_thread(void *arg1, void *arg2)
rc = __setup_ut_on_single_target(target);
if (rc < 0) {
/* CUnit error, probably won't recover */
stop_init_thread(-rc);
stop_init_thread(-rc, request);
}
target = target->next;
}
@ -1056,7 +1068,7 @@ __run_ut_thread(void *arg1, void *arg2)
num_failures = CU_get_number_of_failures();
CU_cleanup_registry();
stop_init_thread(num_failures);
stop_init_thread(num_failures, request);
}
static void
@ -1078,6 +1090,11 @@ test_main(void *arg1)
spdk_app_stop(-1);
}
if (g_wait_for_tests) {
/* Do not perform any tests until RPC is received */
return;
}
if (bdevio_construct_targets() < 0) {
spdk_app_stop(-1);
return;
@ -1090,14 +1107,104 @@ test_main(void *arg1)
static void
bdevio_usage(void)
{
printf(" -w start bdevio app and wait for RPC to start the tests\n");
}
static int
bdevio_parse_arg(int ch, char *arg)
{
switch (ch) {
case 'w':
g_wait_for_tests = true;
break;
default:
return -EINVAL;
}
return 0;
}
struct rpc_perform_tests {
char *name;
};
static void
free_rpc_perform_tests(struct rpc_perform_tests *r)
{
free(r->name);
}
static const struct spdk_json_object_decoder rpc_perform_tests_decoders[] = {
{"name", offsetof(struct rpc_perform_tests, name), spdk_json_decode_string, true},
};
static void
rpc_perform_tests_cb(unsigned num_failures, struct spdk_jsonrpc_request *request)
{
struct spdk_json_write_ctx *w;
w = spdk_jsonrpc_begin_result(request);
if (w == NULL) {
return;
}
spdk_json_write_uint32(w, num_failures);
spdk_jsonrpc_end_result(request, w);
}
static void
rpc_perform_tests(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
struct rpc_perform_tests req = {NULL};
struct spdk_event *event;
struct spdk_bdev *bdev;
int rc;
if (params && spdk_json_decode_object(params, rpc_perform_tests_decoders,
SPDK_COUNTOF(rpc_perform_tests_decoders),
&req)) {
SPDK_ERRLOG("spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
goto invalid;
}
if (req.name) {
bdev = spdk_bdev_get_by_name(req.name);
if (bdev == NULL) {
SPDK_ERRLOG("Bdev '%s' does not exist\n", req.name);
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"Bdev '%s' does not exist: %s",
req.name, spdk_strerror(ENODEV));
goto invalid;
}
rc = bdevio_construct_target(bdev);
if (rc < 0) {
SPDK_ERRLOG("Could not construct target for bdev '%s'\n", spdk_bdev_get_name(bdev));
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"Could not construct target for bdev '%s': %s",
spdk_bdev_get_name(bdev), spdk_strerror(-rc));
goto invalid;
}
} else {
rc = bdevio_construct_targets();
if (rc < 0) {
SPDK_ERRLOG("Could not construct targets for all bdevs\n");
spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"Could not construct targets for all bdevs: %s",
spdk_strerror(-rc));
goto invalid;
}
}
free_rpc_perform_tests(&req);
event = spdk_event_allocate(g_lcore_id_ut, __run_ut_thread, NULL, request);
spdk_event_call(event);
return;
invalid:
free_rpc_perform_tests(&req);
}
SPDK_RPC_REGISTER("perform_tests", rpc_perform_tests, SPDK_RPC_RUNTIME)
int
main(int argc, char **argv)
{
@ -1108,7 +1215,7 @@ main(int argc, char **argv)
opts.name = "bdevio";
opts.reactor_mask = "0x7";
if ((rc = spdk_app_parse_args(argc, argv, &opts, "", NULL,
if ((rc = spdk_app_parse_args(argc, argv, &opts, "w", NULL,
bdevio_parse_arg, bdevio_usage)) !=
SPDK_APP_PARSE_ARGS_SUCCESS) {
return rc;

84
test/bdev/bdevio/tests.py Executable file
View File

@ -0,0 +1,84 @@
#!/usr/bin/env python3
from rpc.client import print_dict, JSONRPCException
import logging
import argparse
import rpc
import sys
import shlex
try:
from shlex import quote
except ImportError:
from pipes import quote
def print_array(a):
print(" ".join((quote(v) for v in a)))
def perform_tests_func(client, name=None):
"""
Args:
name: bdev name to perform bdevio tests on (optional; if omitted, test all bdevs)
Returns:
Number of failures in tests. 0 means no errors found.
"""
params = {}
if name:
params['name'] = name
return client.call('perform_tests', params)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='SPDK RPC command line interface. NOTE: spdk/scripts/ is expected in PYTHONPATH')
parser.add_argument('-s', dest='server_addr',
help='RPC domain socket path or IP address', default='/var/tmp/spdk.sock')
parser.add_argument('-p', dest='port',
help='RPC port number (if server_addr is IP address)',
default=5260, type=int)
parser.add_argument('-t', dest='timeout',
help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0',
default=60.0, type=float)
parser.add_argument('-v', dest='verbose', action='store_const', const="INFO",
help='Set verbose mode to INFO', default="ERROR")
parser.add_argument('--verbose', dest='verbose', choices=['DEBUG', 'INFO', 'ERROR'],
help="""Set verbose level. """)
subparsers = parser.add_subparsers(help='RPC methods')
def perform_tests(args):
print_dict(perform_tests_func(args.client, name=args.name))
p = subparsers.add_parser('perform_tests', help='Perform all bdevio tests on select bdev')
p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1")
p.set_defaults(func=perform_tests)
def call_rpc_func(args):
try:
args.func(args)
except JSONRPCException as ex:
print(ex.message)
exit(1)
def execute_script(parser, client, fd):
for rpc_call in map(str.rstrip, fd):
if not rpc_call.strip():
continue
args = parser.parse_args(shlex.split(rpc_call))
args.client = client
call_rpc_func(args)
args = parser.parse_args()
args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout, log_level=getattr(logging, args.verbose.upper()))
if hasattr(args, 'func'):
call_rpc_func(args)
elif sys.stdin.isatty():
# No arguments and no data piped through stdin
parser.print_help()
exit(1)
else:
execute_script(parser, args.client, sys.stdin)