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:
parent
8c0c8e5333
commit
7140c2f710
@ -39,6 +39,9 @@
|
|||||||
#include "spdk/log.h"
|
#include "spdk/log.h"
|
||||||
#include "spdk/thread.h"
|
#include "spdk/thread.h"
|
||||||
#include "spdk/event.h"
|
#include "spdk/event.h"
|
||||||
|
#include "spdk/rpc.h"
|
||||||
|
#include "spdk/util.h"
|
||||||
|
#include "spdk/string.h"
|
||||||
|
|
||||||
#include "CUnit/Basic.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_init;
|
||||||
static uint32_t g_lcore_id_ut;
|
static uint32_t g_lcore_id_ut;
|
||||||
static uint32_t g_lcore_id_io;
|
static uint32_t g_lcore_id_io;
|
||||||
|
static bool g_wait_for_tests = false;
|
||||||
|
|
||||||
struct io_target {
|
struct io_target {
|
||||||
struct spdk_bdev *bdev;
|
struct spdk_bdev *bdev;
|
||||||
@ -71,6 +75,7 @@ struct bdevio_request {
|
|||||||
|
|
||||||
struct io_target *g_io_targets = NULL;
|
struct io_target *g_io_targets = NULL;
|
||||||
struct io_target *g_current_io_target = 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
|
static void
|
||||||
execute_spdk_function(spdk_event_fn fn, void *arg1, void *arg2)
|
execute_spdk_function(spdk_event_fn fn, void *arg1, void *arg2)
|
||||||
@ -938,18 +943,24 @@ static void
|
|||||||
__stop_init_thread(void *arg1, void *arg2)
|
__stop_init_thread(void *arg1, void *arg2)
|
||||||
{
|
{
|
||||||
unsigned num_failures = (unsigned)(uintptr_t)arg1;
|
unsigned num_failures = (unsigned)(uintptr_t)arg1;
|
||||||
|
struct spdk_jsonrpc_request *request = arg2;
|
||||||
|
|
||||||
bdevio_cleanup_targets();
|
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);
|
spdk_app_stop(num_failures);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stop_init_thread(unsigned num_failures)
|
stop_init_thread(unsigned num_failures, struct spdk_jsonrpc_request *request)
|
||||||
{
|
{
|
||||||
struct spdk_event *event;
|
struct spdk_event *event;
|
||||||
|
|
||||||
event = spdk_event_allocate(g_lcore_id_init, __stop_init_thread,
|
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);
|
spdk_event_call(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,6 +1043,7 @@ __setup_ut_on_single_target(struct io_target *target)
|
|||||||
static void
|
static void
|
||||||
__run_ut_thread(void *arg1, void *arg2)
|
__run_ut_thread(void *arg1, void *arg2)
|
||||||
{
|
{
|
||||||
|
struct spdk_jsonrpc_request *request = arg2;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct io_target *target;
|
struct io_target *target;
|
||||||
unsigned num_failures;
|
unsigned num_failures;
|
||||||
@ -1039,7 +1051,7 @@ __run_ut_thread(void *arg1, void *arg2)
|
|||||||
if (CU_initialize_registry() != CUE_SUCCESS) {
|
if (CU_initialize_registry() != CUE_SUCCESS) {
|
||||||
/* CUnit error, probably won't recover */
|
/* CUnit error, probably won't recover */
|
||||||
rc = CU_get_error();
|
rc = CU_get_error();
|
||||||
stop_init_thread(-rc);
|
stop_init_thread(-rc, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
target = g_io_targets;
|
target = g_io_targets;
|
||||||
@ -1047,7 +1059,7 @@ __run_ut_thread(void *arg1, void *arg2)
|
|||||||
rc = __setup_ut_on_single_target(target);
|
rc = __setup_ut_on_single_target(target);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
/* CUnit error, probably won't recover */
|
/* CUnit error, probably won't recover */
|
||||||
stop_init_thread(-rc);
|
stop_init_thread(-rc, request);
|
||||||
}
|
}
|
||||||
target = target->next;
|
target = target->next;
|
||||||
}
|
}
|
||||||
@ -1056,7 +1068,7 @@ __run_ut_thread(void *arg1, void *arg2)
|
|||||||
num_failures = CU_get_number_of_failures();
|
num_failures = CU_get_number_of_failures();
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
|
|
||||||
stop_init_thread(num_failures);
|
stop_init_thread(num_failures, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1078,6 +1090,11 @@ test_main(void *arg1)
|
|||||||
spdk_app_stop(-1);
|
spdk_app_stop(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_wait_for_tests) {
|
||||||
|
/* Do not perform any tests until RPC is received */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (bdevio_construct_targets() < 0) {
|
if (bdevio_construct_targets() < 0) {
|
||||||
spdk_app_stop(-1);
|
spdk_app_stop(-1);
|
||||||
return;
|
return;
|
||||||
@ -1090,14 +1107,104 @@ test_main(void *arg1)
|
|||||||
static void
|
static void
|
||||||
bdevio_usage(void)
|
bdevio_usage(void)
|
||||||
{
|
{
|
||||||
|
printf(" -w start bdevio app and wait for RPC to start the tests\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
bdevio_parse_arg(int ch, char *arg)
|
bdevio_parse_arg(int ch, char *arg)
|
||||||
{
|
{
|
||||||
|
switch (ch) {
|
||||||
|
case 'w':
|
||||||
|
g_wait_for_tests = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
return 0;
|
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
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -1108,7 +1215,7 @@ main(int argc, char **argv)
|
|||||||
opts.name = "bdevio";
|
opts.name = "bdevio";
|
||||||
opts.reactor_mask = "0x7";
|
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)) !=
|
bdevio_parse_arg, bdevio_usage)) !=
|
||||||
SPDK_APP_PARSE_ARGS_SUCCESS) {
|
SPDK_APP_PARSE_ARGS_SUCCESS) {
|
||||||
return rc;
|
return rc;
|
||||||
|
84
test/bdev/bdevio/tests.py
Executable file
84
test/bdev/bdevio/tests.py
Executable 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)
|
Loading…
Reference in New Issue
Block a user