diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 5f9c32369..5b69a680f 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -4659,6 +4659,51 @@ Example response: } ~~~ +## nvmf_subsystem_listener_set_ana_state method {#rpc_nvmf_subsystem_listener_set_ana_state} + +Set ANA state of a listener for an NVMe-oF subsystem. + +### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +nqn | Required | string | Subsystem NQN +tgt_name | Optional | string | Parent NVMe-oF target name. +listen_address | Required | object | @ref rpc_nvmf_listen_address object +ana_state | Required | string | ANA state to set ("optimized", "non_optimized", or "inaccessible") + +### Example + +Example request: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "method": "nvmf_subsystem_listener_set_ana_state", + "params": { + "nqn": "nqn.2016-06.io.spdk:cnode1", + "listen_address": { + "trtype": "RDMA", + "adrfam": "IPv4", + "traddr": "192.168.0.123", + "trsvcid": "4420" + }, + "ana_state", "inaccessible" + } +} +~~~ + +Example response: + +~~~ +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + ## nvmf_subsystem_add_ns method {#rpc_nvmf_subsystem_add_ns} Add a namespace to a subsystem. The namespace ID is returned as the result. diff --git a/lib/nvmf/nvmf_rpc.c b/lib/nvmf/nvmf_rpc.c index 40ae801e2..044fa2ad2 100644 --- a/lib/nvmf/nvmf_rpc.c +++ b/lib/nvmf/nvmf_rpc.c @@ -588,6 +588,7 @@ free_rpc_listen_address(struct rpc_listen_address *r) enum nvmf_rpc_listen_op { NVMF_RPC_LISTEN_ADD, NVMF_RPC_LISTEN_REMOVE, + NVMF_RPC_LISTEN_SET_ANA_STATE, }; struct nvmf_rpc_listener_ctx { @@ -597,6 +598,8 @@ struct nvmf_rpc_listener_ctx { struct spdk_nvmf_transport *transport; struct spdk_nvmf_subsystem *subsystem; struct rpc_listen_address address; + char *ana_state_str; + enum spdk_nvme_ana_state ana_state; struct spdk_jsonrpc_request *request; struct spdk_nvme_transport_id trid; @@ -616,6 +619,7 @@ nvmf_rpc_listener_ctx_free(struct nvmf_rpc_listener_ctx *ctx) free(ctx->nqn); free(ctx->tgt_name); free_rpc_listen_address(&ctx->address); + free(ctx->ana_state_str); free(ctx); } @@ -658,7 +662,8 @@ nvmf_rpc_subsystem_listen(void *cb_arg, int status) if (spdk_nvmf_subsystem_resume(ctx->subsystem, nvmf_rpc_listen_resumed, ctx)) { if (!ctx->response_sent) { - spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Internal error"); } nvmf_rpc_listener_ctx_free(ctx); /* Can't really do anything to recover here - subsystem will remain paused. */ @@ -670,15 +675,38 @@ nvmf_rpc_stop_listen_async_done(void *cb_arg, int status) struct nvmf_rpc_listener_ctx *ctx = cb_arg; if (status) { - SPDK_ERRLOG("Unable to remove listener.\n"); + SPDK_ERRLOG("Unable to stop listener.\n"); spdk_jsonrpc_send_error_response_fmt(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, - "error stopping listener: %d\n", status); + "error stopping listener: %d", status); ctx->response_sent = true; } if (spdk_nvmf_subsystem_resume(ctx->subsystem, nvmf_rpc_listen_resumed, ctx)) { if (!ctx->response_sent) { - spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Internal error"); + } + nvmf_rpc_listener_ctx_free(ctx); + /* Can't really do anything to recover here - subsystem will remain paused. */ + } +} + +static void +nvmf_rpc_set_ana_state_done(void *cb_arg, int status) +{ + struct nvmf_rpc_listener_ctx *ctx = cb_arg; + + if (status) { + SPDK_ERRLOG("Unable to set ANA state.\n"); + spdk_jsonrpc_send_error_response_fmt(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "error setting ANA state: %d", status); + ctx->response_sent = true; + } + + if (spdk_nvmf_subsystem_resume(ctx->subsystem, nvmf_rpc_listen_resumed, ctx)) { + if (!ctx->response_sent) { + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Internal error"); } nvmf_rpc_listener_ctx_free(ctx); /* Can't really do anything to recover here - subsystem will remain paused. */ @@ -714,13 +742,18 @@ nvmf_rpc_listen_paused(struct spdk_nvmf_subsystem *subsystem, spdk_nvmf_transport_stop_listen_async(ctx->transport, &ctx->trid, nvmf_rpc_stop_listen_async_done, ctx); return; + } else if (ctx->op == NVMF_RPC_LISTEN_SET_ANA_STATE) { + nvmf_subsystem_set_ana_state(subsystem, &ctx->trid, ctx->ana_state, + nvmf_rpc_set_ana_state_done, ctx); + return; } else { SPDK_UNREACHABLE(); } if (spdk_nvmf_subsystem_resume(subsystem, nvmf_rpc_listen_resumed, ctx)) { if (!ctx->response_sent) { - spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, "Internal error"); + spdk_jsonrpc_send_error_response(ctx->request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Internal error"); } nvmf_rpc_listener_ctx_free(ctx); /* Can't really do anything to recover here - subsystem will remain paused. */ @@ -919,6 +952,108 @@ rpc_nvmf_subsystem_remove_listener(struct spdk_jsonrpc_request *request, SPDK_RPC_REGISTER("nvmf_subsystem_remove_listener", rpc_nvmf_subsystem_remove_listener, SPDK_RPC_RUNTIME); +static const struct spdk_json_object_decoder nvmf_rpc_set_ana_state_decoder[] = { + {"nqn", offsetof(struct nvmf_rpc_listener_ctx, nqn), spdk_json_decode_string}, + {"listen_address", offsetof(struct nvmf_rpc_listener_ctx, address), decode_rpc_listen_address}, + {"ana_state", offsetof(struct nvmf_rpc_listener_ctx, ana_state_str), spdk_json_decode_string}, + {"tgt_name", offsetof(struct nvmf_rpc_listener_ctx, tgt_name), spdk_json_decode_string, true}, +}; + +static int +rpc_ana_state_parse(const char *str, enum spdk_nvme_ana_state *ana_state) +{ + if (ana_state == NULL || str == NULL) { + return -EINVAL; + } + + if (strcasecmp(str, "optimized") == 0) { + *ana_state = SPDK_NVME_ANA_OPTIMIZED_STATE; + } else if (strcasecmp(str, "non_optimized") == 0) { + *ana_state = SPDK_NVME_ANA_NON_OPTIMIZED_STATE; + } else if (strcasecmp(str, "inaccessible") == 0) { + *ana_state = SPDK_NVME_ANA_INACCESSIBLE_STATE; + } else { + return -ENOENT; + } + + return 0; +} + +static void +rpc_nvmf_subsystem_listener_set_ana_state(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct nvmf_rpc_listener_ctx *ctx; + struct spdk_nvmf_subsystem *subsystem; + struct spdk_nvmf_tgt *tgt; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Out of memory"); + return; + } + + ctx->request = request; + + if (spdk_json_decode_object(params, nvmf_rpc_set_ana_state_decoder, + SPDK_COUNTOF(nvmf_rpc_set_ana_state_decoder), + ctx)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + tgt = spdk_nvmf_get_tgt(ctx->tgt_name); + if (!tgt) { + SPDK_ERRLOG("Unable to find a target object.\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Unable to find a target.\n"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->tgt = tgt; + + subsystem = spdk_nvmf_tgt_find_subsystem(tgt, ctx->nqn); + if (!subsystem) { + SPDK_ERRLOG("Unable to find subsystem with NQN %s\n", ctx->nqn); + spdk_jsonrpc_send_error_response_fmt(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Unable to find subsystem with NQN %s", + ctx->nqn); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->subsystem = subsystem; + + if (rpc_listen_address_to_trid(&ctx->address, &ctx->trid)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + if (rpc_ana_state_parse(ctx->ana_state_str, &ctx->ana_state)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + "Invalid parameters"); + nvmf_rpc_listener_ctx_free(ctx); + return; + } + + ctx->op = NVMF_RPC_LISTEN_SET_ANA_STATE; + + if (spdk_nvmf_subsystem_pause(subsystem, nvmf_rpc_listen_paused, ctx)) { + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "Internal error"); + nvmf_rpc_listener_ctx_free(ctx); + } +} +SPDK_RPC_REGISTER("nvmf_subsystem_listener_set_ana_state", + rpc_nvmf_subsystem_listener_set_ana_state, SPDK_RPC_RUNTIME); + struct spdk_nvmf_ns_params { char *bdev_name; char *ptpl_file; diff --git a/scripts/rpc.py b/scripts/rpc.py index 932d283f1..0abefd189 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -1919,6 +1919,26 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number') p.set_defaults(func=nvmf_subsystem_remove_listener) + def nvmf_subsystem_listener_set_ana_state(args): + rpc.nvmf.nvmf_subsystem_listener_set_ana_state(args.client, + nqn=args.nqn, + ana_state=args.ana_state, + trtype=args.trtype, + traddr=args.traddr, + tgt_name=args.tgt_name, + adrfam=args.adrfam, + trsvcid=args.trsvcid) + + p = subparsers.add_parser('nvmf_subsystem_listener_set_ana_state', help='Set ANA state of a listener for an NVMe-oF subsystem') + p.add_argument('nqn', help='NVMe-oF subsystem NQN') + p.add_argument('-n', '--ana-state', help='ANA state to set: optimized, non-optimized, or inaccessible', required=True) + p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True) + p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True) + p.add_argument('-p', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str) + p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host') + p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number') + p.set_defaults(func=nvmf_subsystem_listener_set_ana_state) + def nvmf_subsystem_add_ns(args): rpc.nvmf.nvmf_subsystem_add_ns(args.client, nqn=args.nqn, diff --git a/scripts/rpc/nvmf.py b/scripts/rpc/nvmf.py index 7ffb61d87..567ef3c88 100644 --- a/scripts/rpc/nvmf.py +++ b/scripts/rpc/nvmf.py @@ -330,6 +330,46 @@ def nvmf_subsystem_remove_listener( return client.call('nvmf_subsystem_remove_listener', params) +def nvmf_subsystem_listener_set_ana_state( + client, + nqn, + ana_state, + trtype, + traddr, + trsvcid, + adrfam, + tgt_name=None): + """Set ANA state of a listener for an NVMe-oF subsystem. + + Args: + nqn: Subsystem NQN. + ana_state: ANA state to set ("optimized", "non_optimized", or "inaccessible"). + trtype: Transport type ("RDMA"). + traddr: Transport address. + trsvcid: Transport service ID. + tgt_name: name of the parent NVMe-oF target (optional). + adrfam: Address family ("IPv4", "IPv6", "IB", or "FC"). + + Returns: + True or False + """ + listen_address = {'trtype': trtype, + 'traddr': traddr, + 'trsvcid': trsvcid} + + if adrfam: + listen_address['adrfam'] = adrfam + + params = {'nqn': nqn, + 'listen_address': listen_address, + 'ana_state': ana_state} + + if tgt_name: + params['tgt_name'] = tgt_name + + return client.call('nvmf_subsystem_listener_set_ana_state', params) + + def nvmf_subsystem_add_ns(client, nqn, bdev_name, tgt_name=None, ptpl_file=None, nsid=None, nguid=None, eui64=None, uuid=None): """Add a namespace to a subsystem.