vbdev_lvol: add bdev_lvol_get_lvols RPC
This provides information about logical volumes without providing information about the bdevs. It is useful for listing the lvols associated with specific lvol stores and for listing lvols that are degraded and have no associated bdev. Signed-off-by: Mike Gerdts <mgerdts@nvidia.com> Change-Id: I795161ac88d9707831d9fcd2079635c7e46ecc42 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/17547 Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
parent
68cde3b770
commit
a4862f5a56
@ -24,6 +24,11 @@ New API `spdk_lvol_iter_immediate_clones` was added to iterate the clones of an
|
||||
New APIs `spdk_lvol_get_by_uuid` and `spdk_lvol_get_by_names` to get lvols by the lvol's UUID or
|
||||
lvstore and lvol names.
|
||||
|
||||
New `bdev_lvol_get_lvols` RPC to list logical volumes. This provides information about logical
|
||||
volumes without providing information about the bdevs. It is useful for listing the lvols
|
||||
associated with specific lvol stores and for listing lvols that are degraded and have no
|
||||
associated bdev.
|
||||
|
||||
### nvmf
|
||||
|
||||
New `spdk_nvmf_request_copy_to/from_buf()` APIs have been added, which support
|
||||
|
@ -9903,6 +9903,58 @@ Example response:
|
||||
}
|
||||
~~~
|
||||
|
||||
### bdev_lvol_get_lvols {#rpc_bdev_lvol_get_lvols}
|
||||
|
||||
Get a list of logical volumes. This list can be limited by lvol store and will display volumes even if
|
||||
they are degraded. Degraded lvols do not have an associated bdev, thus this RPC call may return lvols
|
||||
not returned by `bdev_get_bdevs`.
|
||||
|
||||
#### Parameters
|
||||
|
||||
Name | Optional | Type | Description
|
||||
----------------------- | -------- | ----------- | -----------
|
||||
lvs_uuid | Optional | string | Only show volumes in the logical volume store with this UUID
|
||||
lvs_name | Optional | string | Only show volumes in the logical volume store with this name
|
||||
|
||||
Either lvs_uuid or lvs_name may be specified, but not both.
|
||||
If both lvs_uuid and lvs_name are omitted, information about lvols in all logical volume stores is returned.
|
||||
|
||||
#### Example
|
||||
|
||||
Example request:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "bdev_lvol_get_lvols",
|
||||
"id": 1,
|
||||
"params": {
|
||||
"lvs_name": "lvs_test"
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Example response:
|
||||
|
||||
~~~json
|
||||
[
|
||||
{
|
||||
"alias": "lvs_test/lvol1",
|
||||
"uuid": "b335c368-851d-4099-81e0-018cc494fdf6",
|
||||
"name": "lvol1",
|
||||
"is_thin_provisioned": false,
|
||||
"is_snapshot": false,
|
||||
"is_clone": false,
|
||||
"is_esnap_clone": false,
|
||||
"is_degraded": false,
|
||||
"lvs": {
|
||||
"name": "lvs_test",
|
||||
"uuid": "a1c8d950-5715-4558-936d-ab9e6eca0794"
|
||||
}
|
||||
}
|
||||
]
|
||||
~~~
|
||||
|
||||
## RAID
|
||||
|
||||
### bdev_raid_get_bdevs {#rpc_bdev_raid_get_bdevs}
|
||||
|
@ -150,6 +150,12 @@ bdev_lvol_create [-h] [-u UUID] [-l LVS_NAME] [-t] [-c CLEAR_METHOD] lvol_name s
|
||||
optional arguments:
|
||||
-h, --help show help
|
||||
-c, --clear-method specify data clusters clear method "none", "unmap" (default), "write_zeroes"
|
||||
bdev_lvol_get_lvols [-h] [-u LVS_UUID] [-l LVS_NAME]
|
||||
Display logical volume list, including those that do not have associated bdevs.
|
||||
optional arguments:
|
||||
-h, --help show help
|
||||
-u LVS_UUID, --lvs_uuid UUID show volumes only in the specified lvol store
|
||||
-l LVS_NAME, --lvs_name LVS_NAME show volumes only in the specified lvol store
|
||||
bdev_get_bdevs [-h] [-b NAME]
|
||||
User can view created bdevs using this call including those created on top of lvols.
|
||||
optional arguments:
|
||||
|
@ -1149,6 +1149,113 @@ cleanup:
|
||||
SPDK_RPC_REGISTER("bdev_lvol_get_lvstores", rpc_bdev_lvol_get_lvstores, SPDK_RPC_RUNTIME)
|
||||
SPDK_RPC_REGISTER_ALIAS_DEPRECATED(bdev_lvol_get_lvstores, get_lvol_stores)
|
||||
|
||||
struct rpc_bdev_lvol_get_lvols {
|
||||
char *lvs_uuid;
|
||||
char *lvs_name;
|
||||
};
|
||||
|
||||
static void
|
||||
free_rpc_bdev_lvol_get_lvols(struct rpc_bdev_lvol_get_lvols *req)
|
||||
{
|
||||
free(req->lvs_uuid);
|
||||
free(req->lvs_name);
|
||||
}
|
||||
|
||||
static const struct spdk_json_object_decoder rpc_bdev_lvol_get_lvols_decoders[] = {
|
||||
{"lvs_uuid", offsetof(struct rpc_bdev_lvol_get_lvols, lvs_uuid), spdk_json_decode_string, true},
|
||||
{"lvs_name", offsetof(struct rpc_bdev_lvol_get_lvols, lvs_name), spdk_json_decode_string, true},
|
||||
};
|
||||
|
||||
static void
|
||||
rpc_dump_lvol(struct spdk_json_write_ctx *w, struct spdk_lvol *lvol)
|
||||
{
|
||||
struct spdk_lvol_store *lvs = lvol->lvol_store;
|
||||
char uuid[SPDK_UUID_STRING_LEN];
|
||||
|
||||
spdk_json_write_object_begin(w);
|
||||
|
||||
spdk_json_write_named_string_fmt(w, "alias", "%s/%s", lvs->name, lvol->name);
|
||||
spdk_json_write_named_string(w, "uuid", lvol->uuid_str);
|
||||
spdk_json_write_named_string(w, "name", lvol->name);
|
||||
spdk_json_write_named_bool(w, "is_thin_provisioned", spdk_blob_is_thin_provisioned(lvol->blob));
|
||||
spdk_json_write_named_bool(w, "is_snapshot", spdk_blob_is_snapshot(lvol->blob));
|
||||
spdk_json_write_named_bool(w, "is_clone", spdk_blob_is_clone(lvol->blob));
|
||||
spdk_json_write_named_bool(w, "is_esnap_clone", spdk_blob_is_esnap_clone(lvol->blob));
|
||||
spdk_json_write_named_bool(w, "is_degraded", spdk_blob_is_degraded(lvol->blob));
|
||||
|
||||
spdk_json_write_named_object_begin(w, "lvs");
|
||||
spdk_json_write_named_string(w, "name", lvs->name);
|
||||
spdk_uuid_fmt_lower(uuid, sizeof(uuid), &lvs->uuid);
|
||||
spdk_json_write_named_string(w, "uuid", uuid);
|
||||
spdk_json_write_object_end(w);
|
||||
|
||||
spdk_json_write_object_end(w);
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_dump_lvols(struct spdk_json_write_ctx *w, struct lvol_store_bdev *lvs_bdev)
|
||||
{
|
||||
struct spdk_lvol_store *lvs = lvs_bdev->lvs;
|
||||
struct spdk_lvol *lvol;
|
||||
|
||||
TAILQ_FOREACH(lvol, &lvs->lvols, link) {
|
||||
rpc_dump_lvol(w, lvol);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_bdev_lvol_get_lvols(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
|
||||
{
|
||||
struct rpc_bdev_lvol_get_lvols req = {};
|
||||
struct spdk_json_write_ctx *w;
|
||||
struct lvol_store_bdev *lvs_bdev = NULL;
|
||||
struct spdk_lvol_store *lvs = NULL;
|
||||
int rc;
|
||||
|
||||
if (params != NULL) {
|
||||
if (spdk_json_decode_object(params, rpc_bdev_lvol_get_lvols_decoders,
|
||||
SPDK_COUNTOF(rpc_bdev_lvol_get_lvols_decoders),
|
||||
&req)) {
|
||||
SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n");
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
|
||||
"spdk_json_decode_object failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = vbdev_get_lvol_store_by_uuid_xor_name(req.lvs_uuid, req.lvs_name, &lvs);
|
||||
if (rc != 0) {
|
||||
spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
lvs_bdev = vbdev_get_lvs_bdev_by_lvs(lvs);
|
||||
if (lvs_bdev == NULL) {
|
||||
spdk_jsonrpc_send_error_response(request, ENODEV, spdk_strerror(-ENODEV));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
w = spdk_jsonrpc_begin_result(request);
|
||||
spdk_json_write_array_begin(w);
|
||||
|
||||
if (lvs_bdev != NULL) {
|
||||
rpc_dump_lvols(w, lvs_bdev);
|
||||
} else {
|
||||
for (lvs_bdev = vbdev_lvol_store_first(); lvs_bdev != NULL;
|
||||
lvs_bdev = vbdev_lvol_store_next(lvs_bdev)) {
|
||||
rpc_dump_lvols(w, lvs_bdev);
|
||||
}
|
||||
}
|
||||
spdk_json_write_array_end(w);
|
||||
|
||||
spdk_jsonrpc_end_result(request, w);
|
||||
|
||||
cleanup:
|
||||
free_rpc_bdev_lvol_get_lvols(&req);
|
||||
}
|
||||
|
||||
SPDK_RPC_REGISTER("bdev_lvol_get_lvols", rpc_bdev_lvol_get_lvols, SPDK_RPC_RUNTIME)
|
||||
|
||||
struct rpc_bdev_lvol_grow_lvstore {
|
||||
char *uuid;
|
||||
char *lvs_name;
|
||||
|
@ -261,3 +261,24 @@ def bdev_lvol_get_lvstores(client, uuid=None, lvs_name=None):
|
||||
if lvs_name:
|
||||
params['lvs_name'] = lvs_name
|
||||
return client.call('bdev_lvol_get_lvstores', params)
|
||||
|
||||
|
||||
def bdev_lvol_get_lvols(client, lvs_uuid=None, lvs_name=None):
|
||||
"""List logical volumes
|
||||
|
||||
Args:
|
||||
lvs_uuid: Only show volumes in the logical volume store with this UUID (optional)
|
||||
lvs_name: Only show volumes in the logical volume store with this name (optional)
|
||||
|
||||
Either lvs_uuid or lvs_name may be specified, but not both.
|
||||
If both lvs_uuid and lvs_name are omitted, information about volumes in all
|
||||
logical volume stores is returned.
|
||||
"""
|
||||
if (lvs_uuid and lvs_name):
|
||||
raise ValueError("Exactly one of uuid or lvs_name may be specified")
|
||||
params = {}
|
||||
if lvs_uuid:
|
||||
params['lvs_uuid'] = lvs_uuid
|
||||
if lvs_name:
|
||||
params['lvs_name'] = lvs_name
|
||||
return client.call('bdev_lvol_get_lvols', params)
|
||||
|
@ -2052,6 +2052,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
|
||||
p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
|
||||
p.set_defaults(func=bdev_lvol_get_lvstores)
|
||||
|
||||
def bdev_lvol_get_lvols(args):
|
||||
print_dict(rpc.lvol.bdev_lvol_get_lvols(args.client,
|
||||
lvs_uuid=args.lvs_uuid,
|
||||
lvs_name=args.lvs_name))
|
||||
|
||||
p = subparsers.add_parser('bdev_lvol_get_lvols', help='Display current logical volume list')
|
||||
p.add_argument('-u', '--lvs-uuid', help='only lvols in lvol store UUID', required=False)
|
||||
p.add_argument('-l', '--lvs-name', help='only lvols in lvol store name', required=False)
|
||||
p.set_defaults(func=bdev_lvol_get_lvols)
|
||||
|
||||
def bdev_raid_get_bdevs(args):
|
||||
print_json(rpc.bdev.bdev_raid_get_bdevs(args.client,
|
||||
category=args.category))
|
||||
|
@ -533,6 +533,31 @@ function test_construct_nested_lvol() {
|
||||
check_leftover_devices
|
||||
}
|
||||
|
||||
# List lvols without going through the bdev layer.
|
||||
function test_lvol_list() {
|
||||
# create an lvol store
|
||||
malloc_name=$(rpc_cmd bdev_malloc_create $MALLOC_SIZE_MB $MALLOC_BS)
|
||||
lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore "$malloc_name" lvs_test)
|
||||
|
||||
# An empty lvolstore is not listed by bdev_lvol_get_lvols
|
||||
lvols=$(rpc_cmd bdev_lvol_get_lvols)
|
||||
[ "$(jq -r '. | length' <<< "$lvols")" == "0" ]
|
||||
|
||||
# Create an lvol, it should appear in the list
|
||||
lvol_uuid=$(rpc_cmd bdev_lvol_create -u "$lvs_uuid" lvol_test "$LVS_DEFAULT_CAPACITY_MB")
|
||||
lvols=$(rpc_cmd bdev_lvol_get_lvols)
|
||||
[ "$(jq -r '. | length' <<< "$lvols")" == "1" ]
|
||||
[ "$(jq -r '.[0].uuid' <<< "$lvols")" == "$lvol_uuid" ]
|
||||
[ "$(jq -r '.[0].name' <<< "$lvols")" == "lvol_test" ]
|
||||
[ "$(jq -r '.[0].alias' <<< "$lvols")" == "lvs_test/lvol_test" ]
|
||||
[ "$(jq -r '.[0].lvs.name' <<< "$lvols")" == "lvs_test" ]
|
||||
[ "$(jq -r '.[0].lvs.uuid' <<< "$lvols")" == "$lvs_uuid" ]
|
||||
|
||||
rpc_cmd bdev_lvol_delete_lvstore -u "$lvs_uuid"
|
||||
rpc_cmd bdev_malloc_delete "$malloc_name"
|
||||
check_leftover_devices
|
||||
}
|
||||
|
||||
# Send SIGTERM after creating lvol store
|
||||
function test_sigterm() {
|
||||
# create an lvol store
|
||||
@ -563,6 +588,7 @@ run_test "test_construct_lvol_inexistent_lvs" test_construct_lvol_inexistent_lvs
|
||||
run_test "test_construct_lvol_full_lvs" test_construct_lvol_full_lvs
|
||||
run_test "test_construct_lvol_alias_conflict" test_construct_lvol_alias_conflict
|
||||
run_test "test_construct_nested_lvol" test_construct_nested_lvol
|
||||
run_test "test_lvol_list" test_lvol_list
|
||||
run_test "test_sigterm" test_sigterm
|
||||
|
||||
trap - SIGINT SIGTERM EXIT
|
||||
|
Loading…
Reference in New Issue
Block a user