Commit 672ba928 authored by Damiano Cipriani's avatar Damiano Cipriani Committed by Jim Harris
Browse files

vbdev_lvol: add lvol set external parent rpc



Added the RPC interface for the operation to set the parent external snapshot
of a lvol. Also some lvol tests have been added to check the behaviour
of the RPC.

Change-Id: I4454b1625096881ac8f4b31a7e088212c6d8b3c3
Signed-off-by: default avatarDamiano Cipriani <damiano.cipriani@suse.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/21712


Reviewed-by: default avatarTomasz Zawadzki <tomasz.zawadzki@intel.com>
Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
parent 99ffb3d9
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -501,6 +501,7 @@ Example response:
    "bdev_lvol_start_shallow_copy",
    "bdev_lvol_check_shallow_copy",
    "bdev_lvol_set_parent",
    "bdev_lvol_set_parent_bdev",
    "bdev_daos_delete",
    "bdev_daos_create",
    "bdev_daos_resize"
@@ -10685,6 +10686,47 @@ Example response:
}
~~~

### bdev_lvol_set_parent_bdev {#rpc_bdev_lvol_set_parent_bdev}

Set an external snapshot as the parent of a lvol, making the lvol a clone of this external
snapshot (see @ref rpc_bdev_lvol_clone_bdev).
The previous parent of the lvol, if any, can be another external snapshot or a snapshot; if the
lvol is not a clone, it must be thin-provisioned.
The size of the external snapshot device must be an integer multiple of cluster size of lvol's lvolstore.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
lvol_name               | Required | string      | UUID or alias of the lvol to set external parent of
esnap_name              | Required | string      | UUID or name of the external snapshot to become parent of lvol

#### Example

Example request:

~~~json
{
  "jsonrpc": "2.0",
  "method": "bdev_lvol_set_parent_bdev",
  "id": 1,
  "params": {
    "lvol_name": "LVS1/LVOL0",
    "esnap_name": "e465527b-f412-4f70-a03e-c4a5d608f65e"
  }
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

### bdev_lvol_start_shallow_copy {#rpc_bdev_lvol_start_shallow_copy}

Start a shallow copy of an lvol over a given bdev. Only clusters allocated to the lvol will be written on the bdev.
+42 −0
Original line number Diff line number Diff line
@@ -1607,3 +1607,45 @@ cleanup:
}

SPDK_RPC_REGISTER("bdev_lvol_set_parent", rpc_bdev_lvol_set_parent, SPDK_RPC_RUNTIME)

static void
rpc_bdev_lvol_set_parent_bdev(struct spdk_jsonrpc_request *request,
			      const struct spdk_json_val *params)
{
	struct rpc_bdev_lvol_set_parent req = {};
	struct spdk_lvol *lvol;
	struct spdk_bdev *lvol_bdev;

	SPDK_INFOLOG(lvol_rpc, "Set external parent of lvol\n");

	if (spdk_json_decode_object(params, rpc_bdev_lvol_set_parent_decoders,
				    SPDK_COUNTOF(rpc_bdev_lvol_set_parent_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;
	}

	lvol_bdev = spdk_bdev_get_by_name(req.lvol_name);
	if (lvol_bdev == NULL) {
		SPDK_ERRLOG("lvol bdev '%s' does not exist\n", req.lvol_name);
		spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV));
		goto cleanup;
	}

	lvol = vbdev_lvol_get_from_bdev(lvol_bdev);
	if (lvol == NULL) {
		SPDK_ERRLOG("lvol does not exist\n");
		spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV));
		goto cleanup;
	}

	vbdev_lvol_set_external_parent(lvol, req.parent_name, rpc_bdev_lvol_set_parent_cb, request);

cleanup:
	free_rpc_bdev_lvol_set_parent(&req);
}

SPDK_RPC_REGISTER("bdev_lvol_set_parent_bdev", rpc_bdev_lvol_set_parent_bdev,
		  SPDK_RPC_RUNTIME)
+14 −0
Original line number Diff line number Diff line
@@ -264,6 +264,20 @@ def bdev_lvol_set_parent(client, lvol_name, snapshot_name):
    return client.call('bdev_lvol_set_parent', params)


def bdev_lvol_set_parent_bdev(client, lvol_name, esnap_name):
    """Set the parent external snapshot of a lvol

    Args:
        lvol_name: name of the lvol to set parent of
        esnap_name: name of the external snapshot to become parent of lvol
    """
    params = {
        'lvol_name': lvol_name,
        'parent_name': esnap_name
    }
    return client.call('bdev_lvol_set_parent_bdev', params)


def bdev_lvol_delete_lvstore(client, uuid=None, lvs_name=None):
    """Destroy a logical volume store.

+10 −0
Original line number Diff line number Diff line
@@ -2163,6 +2163,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
    p.add_argument('snapshot_name', help='snapshot name')
    p.set_defaults(func=bdev_lvol_set_parent)

    def bdev_lvol_set_parent_bdev(args):
        rpc.lvol.bdev_lvol_set_parent_bdev(args.client,
                                           lvol_name=args.lvol_name,
                                           esnap_name=args.esnap_name)

    p = subparsers.add_parser('bdev_lvol_set_parent_bdev', help='Set the parent external snapshot of a lvol')
    p.add_argument('lvol_name', help='lvol name')
    p.add_argument('esnap_name', help='external snapshot name')
    p.set_defaults(func=bdev_lvol_set_parent_bdev)

    def bdev_lvol_delete_lvstore(args):
        rpc.lvol.bdev_lvol_delete_lvstore(args.client,
                                          uuid=args.uuid,
+239 −0
Original line number Diff line number Diff line
@@ -459,6 +459,242 @@ function test_esnap_remove_degraded() {
	rpc_cmd bdev_aio_delete "$aio_bdev"
}

function test_lvol_set_parent_bdev_from_esnap() {
	local vol_size_mb=20
	local vol_size=$((vol_size_mb * 1024 * 1024))
	local vol_blocks_count=$((vol_size / MALLOC_BS))
	local three_clusters_size=$((LVS_DEFAULT_CLUSTER_SIZE * 3))
	local three_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 3 / MALLOC_BS))
	local two_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 2 / MALLOC_BS))

	# Create the lvstore on a malloc device.
	malloc_name=$(rpc_cmd bdev_malloc_create $MALLOC_SIZE_MB $MALLOC_BS)
	lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore "$malloc_name" lvs_test)

	# Create a bdev that will be the old external snapshot
	# State:
	#    esnap1
	rpc_cmd bdev_malloc_create -b esnap1 "$vol_size_mb" $MALLOC_BS

	# Perform write operation over esnap1
	nbd_start_disks "$DEFAULT_RPC_ADDR" esnap1 /dev/nbd0
	run_fio_test /dev/nbd0 0 $vol_size "write" "0xaa" ""
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd0
	sleep 1

	# Create a bdev that will be the new external snapshot
	# New state:
	#    esnap1
	#    esnap2
	esnap2_uuid=037128af-3662-4137-9e24-e74e44310ad3
	rpc_cmd bdev_malloc_create -b esnap2 -u "$esnap2_uuid" "$vol_size_mb" $MALLOC_BS

	# Perform write operation over esnap2
	# Calculate md5 of the last part of esnap2 corresponding to 2 clusters size
	nbd_start_disks "$DEFAULT_RPC_ADDR" esnap2 /dev/nbd2
	run_fio_test /dev/nbd2 0 $vol_size "write" "0xbb" ""
	md5_2=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2
	sleep 1

	# Create an esnap1 clone: lvol
	# New state:
	#    esnap1 <-- lvol
	#    esnap2
	lvol_uuid=$(rpc_cmd bdev_lvol_clone_bdev esnap1 lvs_test lvol)

	# Perform write operation over the first 3 clusters of lvol
	# Calculate md5sum of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	run_fio_test /dev/nbd2 0 $three_clusters_size "write" "0xcc" ""
	md5_lvol_1=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Change parent of lvol
	# New state:
	#    esnap1
	#    esnap2  <-- lvol
	rpc_cmd bdev_lvol_set_parent_bdev "$lvol_uuid" "$esnap2_uuid"

	# Check lvol consistency
	verify_esnap_clone "$lvol_uuid" "$esnap2_uuid"

	# Try again with aliases instead uuid
	rpc_cmd bdev_lvol_set_parent_bdev lvs_test/lvol esnap2 | grep "File exists"

	# Calculate again md5 of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	md5_lvol_1_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Check that first three clusters of lvol didn't change anche that the last 2 clusters changed
	[[ $md5_lvol_1 == "$md5_lvol_1_new" ]]
	[[ $md5_lvol_2 != "$md5_lvol_2_new" ]]
	[[ $md5_lvol_2_new == "$md5_2" ]]

	# Clean up
	rpc_cmd bdev_lvol_delete "$lvol_uuid"
	rpc_cmd bdev_malloc_delete esnap1
	rpc_cmd bdev_malloc_delete esnap2
	rpc_cmd bdev_lvol_delete_lvstore -u "$lvs_uuid"
	rpc_cmd bdev_malloc_delete "$malloc_name"
	check_leftover_devices
}

function test_lvol_set_parent_bdev_from_snapshot() {
	local vol_size_mb=20
	local vol_size=$((vol_size_mb * 1024 * 1024))
	local vol_blocks_count=$((vol_size / MALLOC_BS))
	local three_clusters_size=$((LVS_DEFAULT_CLUSTER_SIZE * 3))
	local three_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 3 / MALLOC_BS))
	local two_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 2 / MALLOC_BS))

	# Create the lvstore on a malloc device.
	malloc_name=$(rpc_cmd bdev_malloc_create $MALLOC_SIZE_MB $MALLOC_BS)
	lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore "$malloc_name" lvs_test)

	# Create a bdev that will be the external snapshot
	# State:
	#    esnap1
	esnap1_uuid=533c2e20-3e74-47a1-9c4f-0ffe4922ffed
	rpc_cmd bdev_malloc_create -b esnap1 -u "$esnap1_uuid" "$vol_size_mb" $MALLOC_BS

	# Perform write operation over the external snapshot
	# Calculate md5 of the last part of esnap1 corresponding to 2 clusters size
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$esnap1_uuid" /dev/nbd1
	run_fio_test /dev/nbd1 0 $vol_size "write" "0xaa" ""
	md5_1=$(dd if=/dev/nbd1 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd1
	sleep 1

	# Create a volume: lvol
	# New state:
	#    esnap1
	#    lvol
	lvol_uuid=$(rpc_cmd bdev_lvol_create -u "$lvs_uuid" lvol "$vol_size_mb")

	# Perform write operation over lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	run_fio_test /dev/nbd2 0 $vol_size "write" "0xbb" ""
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Make a snapshot of lvol: snap2
	# New state:
	#    esnap1
	#    snap2  <-- lvol
	snap2_uuid=$(rpc_cmd bdev_lvol_snapshot "$lvol_uuid" snap2)

	# Perform write operation over the first 3 clusters of lvol
	# Calculate md5sum of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	run_fio_test /dev/nbd2 0 $three_clusters_size "write" "0xcc" ""
	md5_lvol_1=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Change parent of lvol
	# New state:
	#    esnap1 <-- lvol
	#    snap2
	rpc_cmd bdev_lvol_set_parent_bdev "$lvol_uuid" "$esnap1_uuid"

	# Check lvol consistency
	verify_esnap_clone "$lvol_uuid" "$esnap1_uuid"

	# Try again with aliases instead uuid
	rpc_cmd bdev_lvol_set_parent_bdev lvs_test/lvol esnap1 | grep "File exists"

	# Calculate again md5 of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	md5_lvol_1_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Check that first three clusters of lvol didn't change anche that the last 2 clusters changed
	[[ $md5_lvol_1 == "$md5_lvol_1_new" ]]
	[[ $md5_lvol_2 != "$md5_lvol_2_new" ]]
	[[ $md5_lvol_2_new == "$md5_1" ]]

	# Clean up
	rpc_cmd bdev_lvol_delete "$lvol_uuid"
	rpc_cmd bdev_lvol_delete "$snap2_uuid"
	rpc_cmd bdev_malloc_delete esnap1
	rpc_cmd bdev_lvol_delete_lvstore -u "$lvs_uuid"
	rpc_cmd bdev_malloc_delete "$malloc_name"
	check_leftover_devices
}

function test_lvol_set_parent_bdev_from_none() {
	local vol_size_mb=20
	local vol_size=$((vol_size_mb * 1024 * 1024))
	local three_clusters_size=$((LVS_DEFAULT_CLUSTER_SIZE * 3))
	local three_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 3 / MALLOC_BS))
	local two_clusters_block_count=$((LVS_DEFAULT_CLUSTER_SIZE * 2 / MALLOC_BS))

	# Create the lvstore on a malloc device.
	malloc_name=$(rpc_cmd bdev_malloc_create $MALLOC_SIZE_MB $MALLOC_BS)
	lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore "$malloc_name" lvs_test)

	# Create a thin provisioned volume: lvol
	# New state:
	#    lvol
	lvol_uuid=$(rpc_cmd bdev_lvol_create -t -u "$lvs_uuid" lvol "$vol_size_mb")

	# Perform write operation over the first 3 clusters of lvol
	# Calculate md5sum of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	run_fio_test /dev/nbd2 0 $three_clusters_size "write" "0xaa" ""
	md5_lvol_1=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Create a bdev that will be the external snapshot
	# New State:
	#    esnap
	#    lvol
	esnap_uuid=61571088-ffcf-48d9-af1f-259eb853f7b4
	rpc_cmd bdev_malloc_create -b esnap -u "$esnap_uuid" "$vol_size_mb" $MALLOC_BS

	# Perform write operation over the external snapshot
	# Calculate md5 of the last part of esnap corresponding to 2 clusters size
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$esnap_uuid" /dev/nbd2
	run_fio_test /dev/nbd2 0 $vol_size "write" "0xbb" ""
	md5_2=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2
	sleep 1

	# Change parent of lvol
	# New state:
	#    esnap <-- lvol
	rpc_cmd bdev_lvol_set_parent_bdev "$lvol_uuid" "$esnap_uuid"

	# Check lvol consistency
	verify_esnap_clone "$lvol_uuid" "$esnap_uuid"

	# Try again with aliases instead uuid
	rpc_cmd bdev_lvol_set_parent_bdev lvs_test/lvol esnap | grep "File exists"

	# Calculate again md5 of the first 3 clusters and of last 2 clusters of lvol
	nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd2
	md5_lvol_1_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$three_clusters_block_count | md5sum)
	md5_lvol_2_new=$(dd if=/dev/nbd2 bs=$MALLOC_BS count=$two_clusters_block_count skip=$three_clusters_block_count | md5sum)
	nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd2

	# Check that first three clusters of lvol didn't change anche that the last 2 clusters changed
	[[ $md5_lvol_1 == "$md5_lvol_1_new" ]]
	[[ $md5_lvol_2 != "$md5_lvol_2_new" ]]
	[[ $md5_lvol_2_new == "$md5_2" ]]

	# Clean up
	rpc_cmd bdev_lvol_delete "$lvol_uuid"
	rpc_cmd bdev_malloc_delete esnap
	rpc_cmd bdev_lvol_delete_lvstore -u "$lvs_uuid"
	rpc_cmd bdev_malloc_delete "$malloc_name"
	check_leftover_devices
}

$SPDK_BIN_DIR/spdk_tgt &
spdk_pid=$!
trap 'killprocess "$spdk_pid"; rm -f "$testdir/aio_bdev_0"; exit 1' SIGINT SIGTERM SIGPIPE EXIT
@@ -470,6 +706,9 @@ run_test "test_esnap_reload" test_esnap_reload_missing
run_test "test_esnap_clones" test_esnap_clones
run_test "test_esnap_late_arrival" test_esnap_late_arrival
run_test "test_esnap_remove_degraded" test_esnap_remove_degraded
run_test "test_lvol_set_parent_bdev_from_esnap" test_lvol_set_parent_bdev_from_esnap
run_test "test_lvol_set_parent_bdev_from_snapshot" test_lvol_set_parent_bdev_from_snapshot
run_test "test_lvol_set_parent_bdev_from_none" test_lvol_set_parent_bdev_from_none

trap - SIGINT SIGTERM SIGPIPE EXIT
killprocess $spdk_pid