From 2982a74df9b190417a5bf28034e0229d7c1c1214 Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Wed, 7 Feb 2018 08:22:00 +0900 Subject: [PATCH] iscsi&rpc: Add new initiator information to an existing initiator group Adding new initiator to an existing initiator group to allow login will be usual. This patch support the following JSON-RPC commands: - add_initiators_to_initiator_group - delete_initiators_from_initiator_group Both initiator's name and netmask are optional but already added name or netmask cannot be added. Test code is added too. Change-Id: Ic101210b9d00c2b36e37ece23fcba8cfe8e44eb8 Signed-off-by: Shuhei Matsumoto Reviewed-on: https://review.gerrithub.io/398361 Tested-by: SPDK Automated Test System Reviewed-by: Jim Harris Reviewed-by: Daniel Verkamp --- lib/iscsi/init_grp.c | 134 +++++++++++++ lib/iscsi/init_grp.h | 6 + lib/iscsi/iscsi_rpc.c | 85 +++++++++ scripts/rpc.py | 20 ++ scripts/rpc/iscsi.py | 28 +++ test/iscsi_tgt/rpc_config/rpc_config.py | 37 ++++ test/unit/lib/iscsi/init_grp.c/init_grp_ut.c | 188 +++++++++++++++++++ 7 files changed, 498 insertions(+) diff --git a/lib/iscsi/init_grp.c b/lib/iscsi/init_grp.c index f1914983f..c08cbf590 100644 --- a/lib/iscsi/init_grp.c +++ b/lib/iscsi/init_grp.c @@ -167,6 +167,31 @@ spdk_iscsi_init_grp_delete_all_initiators(struct spdk_iscsi_init_grp *ig) } } +static int +spdk_iscsi_init_grp_delete_initiators(struct spdk_iscsi_init_grp *ig, int num_inames, char **inames) +{ + int i; + int rc; + + for (i = 0; i < num_inames; i++) { + rc = spdk_iscsi_init_grp_delete_initiator(ig, inames[i]); + if (rc < 0) { + goto cleanup; + } + } + return 0; + +cleanup: + for (; i > 0; --i) { + rc = spdk_iscsi_init_grp_add_initiator(ig, inames[i - 1]); + if (rc != 0) { + spdk_iscsi_init_grp_delete_all_initiators(ig); + break; + } + } + return -1; +} + static struct spdk_iscsi_initiator_netmask * spdk_iscsi_init_grp_find_netmask(struct spdk_iscsi_init_grp *ig, const char *mask) { @@ -275,6 +300,30 @@ spdk_iscsi_init_grp_delete_all_netmasks(struct spdk_iscsi_init_grp *ig) } } +static int +spdk_iscsi_init_grp_delete_netmasks(struct spdk_iscsi_init_grp *ig, int num_imasks, char **imasks) +{ + int i; + int rc; + + for (i = 0; i < num_imasks; i++) { + rc = spdk_iscsi_init_grp_delete_netmask(ig, imasks[i]); + if (rc != 0) { + goto cleanup; + } + } + return 0; + +cleanup: + for (; i > 0; --i) { + rc = spdk_iscsi_init_grp_add_netmask(ig, imasks[i - 1]); + if (rc != 0) { + spdk_iscsi_init_grp_delete_all_netmasks(ig); + break; + } + } + return -1; +} /* Read spdk iscsi target's config file and create initiator group */ static int @@ -461,6 +510,91 @@ cleanup: return rc; } +int +spdk_iscsi_init_grp_add_initiators_from_initiator_list(int tag, + int num_initiator_names, + char **initiator_names, + int num_initiator_masks, + char **initiator_masks) +{ + int rc = -1; + struct spdk_iscsi_init_grp *ig; + + SPDK_DEBUGLOG(SPDK_LOG_ISCSI, + "add initiator to initiator group: tag=%d, #initiators=%d, #masks=%d\n", + tag, num_initiator_names, num_initiator_masks); + + pthread_mutex_lock(&g_spdk_iscsi.mutex); + ig = spdk_iscsi_init_grp_find_by_tag(tag); + if (!ig) { + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + SPDK_ERRLOG("initiator group (%d) is not found\n", tag); + return rc; + } + + rc = spdk_iscsi_init_grp_add_initiators(ig, num_initiator_names, + initiator_names); + if (rc < 0) { + SPDK_ERRLOG("add initiator name error\n"); + goto error; + } + + rc = spdk_iscsi_init_grp_add_netmasks(ig, num_initiator_masks, + initiator_masks); + if (rc < 0) { + SPDK_ERRLOG("add initiator netmask error\n"); + spdk_iscsi_init_grp_delete_initiators(ig, num_initiator_names, + initiator_names); + } + +error: + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + return rc; +} + +int +spdk_iscsi_init_grp_delete_initiators_from_initiator_list(int tag, + int num_initiator_names, + char **initiator_names, + int num_initiator_masks, + char **initiator_masks) +{ + int rc = -1; + struct spdk_iscsi_init_grp *ig; + + SPDK_DEBUGLOG(SPDK_LOG_ISCSI, + "delete initiator from initiator group: tag=%d, #initiators=%d, #masks=%d\n", + tag, num_initiator_names, num_initiator_masks); + + pthread_mutex_lock(&g_spdk_iscsi.mutex); + ig = spdk_iscsi_init_grp_find_by_tag(tag); + if (!ig) { + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + SPDK_ERRLOG("initiator group (%d) is not found\n", tag); + return rc; + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, num_initiator_names, + initiator_names); + if (rc < 0) { + SPDK_ERRLOG("delete initiator name error\n"); + goto error; + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, num_initiator_masks, + initiator_masks); + if (rc < 0) { + SPDK_ERRLOG("delete initiator netmask error\n"); + spdk_iscsi_init_grp_add_initiators(ig, num_initiator_names, + initiator_names); + goto error; + } + +error: + pthread_mutex_unlock(&g_spdk_iscsi.mutex); + return rc; +} + void spdk_iscsi_init_grp_destroy(struct spdk_iscsi_init_grp *ig) { diff --git a/lib/iscsi/init_grp.h b/lib/iscsi/init_grp.h index c340c78b6..871918764 100644 --- a/lib/iscsi/init_grp.h +++ b/lib/iscsi/init_grp.h @@ -61,6 +61,12 @@ struct spdk_iscsi_init_grp { int spdk_iscsi_init_grp_create_from_initiator_list(int tag, int num_initiator_names, char **initiator_names, int num_initiator_masks, char **initiator_masks); +int spdk_iscsi_init_grp_add_initiators_from_initiator_list(int tag, + int num_initiator_names, char **initiator_names, + int num_initiator_masks, char **initiator_masks); +int spdk_iscsi_init_grp_delete_initiators_from_initiator_list(int tag, + int num_initiator_names, char **initiator_names, + int num_initiator_masks, char **initiator_masks); int spdk_iscsi_init_grp_register(struct spdk_iscsi_init_grp *ig); struct spdk_iscsi_init_grp *spdk_iscsi_init_grp_unregister(int tag); struct spdk_iscsi_init_grp *spdk_iscsi_init_grp_find_by_tag(int tag); diff --git a/lib/iscsi/iscsi_rpc.c b/lib/iscsi/iscsi_rpc.c index 60475dde0..39ecbeebc 100644 --- a/lib/iscsi/iscsi_rpc.c +++ b/lib/iscsi/iscsi_rpc.c @@ -206,6 +206,91 @@ invalid: } SPDK_RPC_REGISTER("add_initiator_group", spdk_rpc_add_initiator_group) +static const struct spdk_json_object_decoder rpc_add_or_delete_initiators_decoders[] = { + {"tag", offsetof(struct rpc_initiator_group, tag), spdk_json_decode_int32}, + {"initiators", offsetof(struct rpc_initiator_group, initiator_list), decode_rpc_initiator_list, true}, + {"netmasks", offsetof(struct rpc_initiator_group, netmask_list), decode_rpc_netmask_list, true}, +}; + +static void +spdk_rpc_add_initiators_to_initiator_group(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_initiator_group req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_add_or_delete_initiators_decoders, + SPDK_COUNTOF(rpc_add_or_delete_initiators_decoders), &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + goto invalid; + } + + if (spdk_iscsi_init_grp_add_initiators_from_initiator_list(req.tag, + req.initiator_list.num_initiators, + req.initiator_list.initiators, + req.netmask_list.num_netmasks, + req.netmask_list.netmasks)) { + SPDK_ERRLOG("add_initiators_from_initiator_list failed\n"); + goto invalid; + } + + free_rpc_initiator_group(&req); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_initiator_group(&req); +} +SPDK_RPC_REGISTER("add_initiators_to_initiator_group", spdk_rpc_add_initiators_to_initiator_group) + +static void +spdk_rpc_delete_initiators_from_initiator_group(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_initiator_group req = {}; + struct spdk_json_write_ctx *w; + + if (spdk_json_decode_object(params, rpc_add_or_delete_initiators_decoders, + SPDK_COUNTOF(rpc_add_or_delete_initiators_decoders), &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + goto invalid; + } + + if (spdk_iscsi_init_grp_delete_initiators_from_initiator_list(req.tag, + req.initiator_list.num_initiators, + req.initiator_list.initiators, + req.netmask_list.num_netmasks, + req.netmask_list.netmasks)) { + SPDK_ERRLOG("delete_initiators_from_initiator_list failed\n"); + goto invalid; + } + + free_rpc_initiator_group(&req); + + w = spdk_jsonrpc_begin_result(request); + if (w == NULL) { + return; + } + + spdk_json_write_bool(w, true); + spdk_jsonrpc_end_result(request, w); + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters"); + free_rpc_initiator_group(&req); +} +SPDK_RPC_REGISTER("delete_initiators_from_initiator_group", + spdk_rpc_delete_initiators_from_initiator_group) + struct rpc_delete_initiator_group { int32_t tag; }; diff --git a/scripts/rpc.py b/scripts/rpc.py index 4f0454ca8..19ecadad8 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -192,6 +192,26 @@ if __name__ == "__main__": Example: '255.255.0.0 255.248.0.0' etc""") p.set_defaults(func=rpc.iscsi.add_initiator_group) + p = subparsers.add_parser('add_initiators_to_initiator_group', + help='Add initiators to an existing initiator group') + p.add_argument( + 'tag', help='Initiator group tag (unique, integer > 0)', type=int) + p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses, + enclosed in quotes. This parameter can be omitted. Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False) + p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes. + This parameter can be omitted. Example: '255.255.0.0 255.248.0.0' etc""", required=False) + p.set_defaults(func=rpc.iscsi.add_initiators_to_initiator_group) + + p = subparsers.add_parser('delete_initiators_from_initiator_group', + help='Delete initiators from an existing initiator group') + p.add_argument( + 'tag', help='Initiator group tag (unique, integer > 0)', type=int) + p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses, + enclosed in quotes. This parameter can be omitted. Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False) + p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes. + This parameter can be omitted. Example: '255.255.0.0 255.248.0.0' etc""", required=False) + p.set_defaults(func=rpc.iscsi.delete_initiators_from_initiator_group) + p = subparsers.add_parser('delete_target_node', help='Delete a target node') p.add_argument('target_node_name', diff --git a/scripts/rpc/iscsi.py b/scripts/rpc/iscsi.py index 5bedcc215..a45cf0f5c 100755 --- a/scripts/rpc/iscsi.py +++ b/scripts/rpc/iscsi.py @@ -120,6 +120,34 @@ def add_initiator_group(args): args.client.call('add_initiator_group', params) +def add_initiators_to_initiator_group(args): + initiators = [] + netmasks = [] + if args.initiator_list: + for i in args.initiator_list.strip().split(' '): + initiators.append(i) + if args.netmask_list: + for n in args.netmask_list.strip().split(' '): + netmasks.append(n) + + params = {'tag': args.tag, 'initiators': initiators, 'netmasks': netmasks} + args.client.call('add_initiators_to_initiator_group', params) + + +def delete_initiators_from_initiator_group(args): + initiators = [] + netmasks = [] + if args.initiator_list: + for i in args.initiator_list.strip().split(' '): + initiators.append(i) + if args.netmask_list: + for n in args.netmask_list.strip().split(' '): + netmasks.append(n) + + params = {'tag': args.tag, 'initiators': initiators, 'netmasks': netmasks} + args.client.call('delete_initiators_from_initiator_group', params) + + def delete_target_node(args): params = {'name': args.target_node_name} args.client.call('delete_target_node', params) diff --git a/test/iscsi_tgt/rpc_config/rpc_config.py b/test/iscsi_tgt/rpc_config/rpc_config.py index d27cc51fd..580ab8718 100755 --- a/test/iscsi_tgt/rpc_config/rpc_config.py +++ b/test/iscsi_tgt/rpc_config/rpc_config.py @@ -259,6 +259,43 @@ def verify_initiator_groups_rpc_methods(rpc_py, rpc_param): verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) + for idx, value in enumerate(rpc_param['netmask']): + tag = idx + 1 + rpc.delete_initiators_from_initiator_group(tag, '-n', rpc_param['initiator_name'], '-m', value) + + output = rpc.get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + for idx, value in enumerate(jsonvalues): + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + initiators = value.get('initiators') + verify(len(initiators) == 0, 1, + "length of initiator list is {}, expected 0".format(len(initiators))) + netmasks = value.get('netmasks') + verify(len(netmasks) == 0, 1, + "length of netmask list is {}, expected 0".format(len(netmasks))) + + for idx, value in enumerate(rpc_param['netmask']): + tag = idx + 1 + rpc.add_initiators_to_initiator_group(tag, '-n', rpc_param['initiator_name'], '-m', value) + output = rpc.get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + tag_list = [] + for idx, value in enumerate(jsonvalues): + verify(value['initiators'][0] == rpc_param['initiator_name'], 1, + "initiator value is {}, expected {}".format(value['initiators'][0], rpc_param['initiator_name'])) + tag_list.append(value['tag']) + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, + "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) + for idx, value in enumerate(tag_list): rpc.delete_initiator_group(value) output = rpc.get_initiator_groups() diff --git a/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c b/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c index 88265d670..6e3638304 100644 --- a/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c +++ b/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c @@ -32,6 +32,7 @@ */ #include "spdk/stdinc.h" +#include "spdk_cunit.h" #include "CUnit/Basic.h" #include "iscsi/init_grp.c" @@ -452,6 +453,181 @@ netmask_overwrite_all_to_any_case(void) spdk_iscsi_init_grp_destroy(ig); } +static void +add_delete_initiator_names_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *names[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0003"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_duplicated_initiator_names_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + char *names[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0001"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names); + CU_ASSERT(rc != 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_nonexisting_initiator_names_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *names1[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0003"}; + char *names2[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0004"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names1); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names1[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names2); + CU_ASSERT(rc != 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names1[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names1); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_delete_netmasks_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *netmask; + char *netmasks[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.2"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_duplicated_netmasks_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + char *netmasks[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.0"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks); + CU_ASSERT(rc != 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_nonexisting_netmasks_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *netmask; + char *netmasks1[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.2"}; + char *netmasks2[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.3"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks1); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks1[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks2); + CU_ASSERT(rc != 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks1[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks1); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + + int main(int argc, char **argv) { @@ -500,6 +676,18 @@ main(int argc, char **argv) initiator_name_overwrite_all_to_any_case) == NULL || CU_add_test(suite, "overwrite all to any for netmask case", netmask_overwrite_all_to_any_case) == NULL + || CU_add_test(suite, "add/delete initiator names case", + add_delete_initiator_names_case) == NULL + || CU_add_test(suite, "add duplicated initiator names case", + add_duplicated_initiator_names_case) == NULL + || CU_add_test(suite, "delete nonexisting initiator names case", + delete_nonexisting_initiator_names_case) == NULL + || CU_add_test(suite, "add/delete netmasks case", + add_delete_netmasks_case) == NULL + || CU_add_test(suite, "add duplicated netmasks case", + add_duplicated_netmasks_case) == NULL + || CU_add_test(suite, "delete nonexisting netmasks case", + delete_nonexisting_netmasks_case) == NULL ) { CU_cleanup_registry(); return CU_get_error();