Skip to content

Commit

Permalink
feat(lvol): generic api for setting blob attributes
Browse files Browse the repository at this point in the history
Lvol object now exposes a generic API for setting arbitrary
blob attributes.

Signed-off-by: Mikhail Tcymbaliuk <[email protected]>
  • Loading branch information
mtzaurus committed Jul 22, 2023
1 parent a0c2d5a commit 66d0fbc
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 8 deletions.
2 changes: 1 addition & 1 deletion io-engine/src/lvs/lvs_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pub enum Error {
#[snafu(display("failed to set property {} on {}", prop, name))]
SetProperty {
source: Errno,
prop: PropName,
prop: String,
name: String,
},
#[snafu(display("failed to sync properties {}", name))]
Expand Down
78 changes: 75 additions & 3 deletions io-engine/src/lvs/lvs_lvol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use pin_utils::core_reexport::fmt::Formatter;

use std::{
convert::TryFrom,
ffi::{c_void, CStr},
ffi::{c_ushort, c_void, CStr},
fmt::{Debug, Display},
os::raw::c_char,
pin::Pin,
Expand Down Expand Up @@ -51,6 +51,7 @@ use crate::{
},
ffihelper::{
cb_arg,
done_cb,
errno_result_from_i32,
pair,
ErrnoResult,
Expand Down Expand Up @@ -92,6 +93,12 @@ impl From<PropValue> for PropName {
}
}

impl Display for PropValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}

impl Display for PropName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = match self {
Expand Down Expand Up @@ -394,6 +401,71 @@ impl Lvol {
)
}
}

/// Low-level function to set blob attributes.
pub async fn set_blob_attr<A: AsRef<str>>(
&self,
attr: A,
value: String,
sync_metadata: bool,
) -> Result<(), Error> {
extern "C" fn blob_attr_set_cb(cb_arg: *mut c_void, errno: i32) {
done_cb(cb_arg, errno);
}

let attr_name = attr.as_ref().into_cstring();
let attr_val = value.clone().into_cstring();

let r = unsafe {
spdk_blob_set_xattr(
self.blob_checked(),
attr_name.as_ptr() as *const c_char,
attr_val.as_ptr() as *const c_void,
attr_val.to_bytes().len() as c_ushort,
)
};

if r != 0 {
error!(
lvol = self.name(),
attr = attr.as_ref(),
value,
errno = r,
"Failed to set blob attribute"
);
return Err(Error::SetProperty {
source: Errno::from_i32(r),
prop: attr.as_ref().to_owned(),
name: self.name(),
});
}

if !sync_metadata {
return Ok(());
}

// Sync metadata if requested.
let (snd, rcv) = oneshot::channel::<i32>();

unsafe {
spdk_blob_sync_md(
self.blob_checked(),
Some(blob_attr_set_cb),
cb_arg(snd),
)
};

match rcv.await.expect("sync attribute callback disappeared") {
0 => Ok(()),
errno => {
error!(lvol=self.name(), errno,"Failed to sync blob metadata, properties might be out of sync");
Err(Error::SyncProperty {
source: Errno::from_i32(errno),
name: self.name(),
})
}
}
}
}

struct LvolPtpl {
Expand Down Expand Up @@ -748,7 +820,7 @@ impl LvsLvol for Lvol {
}
.to_result(|e| Error::SetProperty {
source: Errno::from_i32(e),
prop: prop.into(),
prop: prop.to_string(),
name: self.name(),
})?;
}
Expand All @@ -765,7 +837,7 @@ impl LvsLvol for Lvol {
}
.to_result(|e| Error::SetProperty {
source: Errno::from_i32(e),
prop: prop.into(),
prop: prop.to_string(),
name: self.name(),
})?;
}
Expand Down
124 changes: 123 additions & 1 deletion io-engine/tests/snapshot_lvol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use once_cell::sync::OnceCell;
use common::{bdev_io, compose::MayastorTest};

use io_engine::{
bdev::device_open,
bdev::{device_create, device_open},
core::{
CloneParams,
CloneXattrs,
Expand All @@ -30,6 +30,9 @@ use std::{convert::TryFrom, str};
use uuid::Uuid;
static MAYASTOR: OnceCell<MayastorTest> = OnceCell::new();

static POOL_DISK_NAME: &str = "/tmp/disk1.img";
static POOL_DEVICE_NAME: &str = "aio:///tmp/disk1.img";

/// Get the global Mayastor test suite instance.
fn get_ms() -> &'static MayastorTest<'static> {
MAYASTOR.get_or_init(|| MayastorTest::new(MayastorCliArgs::default()))
Expand Down Expand Up @@ -1007,3 +1010,122 @@ async fn test_thick_provision_lvol_alloc_after_snapshot() {
const IDX: u32 = 12;
test_lvol_alloc_after_snapshot(IDX, false).await;
}

#[tokio::test]
async fn test_snapshot_attr() {
let ms = get_ms();

common::delete_file(&[POOL_DISK_NAME.into()]);
common::truncate_file(POOL_DISK_NAME, 128 * 1024);

ms.spawn(async move {
// Create a pool and lvol.
let mut pool =
create_test_pool("pool20", POOL_DEVICE_NAME.into()).await;
let lvol = pool
.create_lvol(
"lvol20",
32 * 1024 * 1024,
Some(&Uuid::new_v4().to_string()),
false,
)
.await
.expect("Failed to create test lvol");

// Create a test snapshot.
let entity_id = String::from("lvol20_e1");
let parent_id = lvol.uuid();
let txn_id = Uuid::new_v4().to_string();
let snap_name = String::from("lvol20_snap1");
let snapshot_uuid = Uuid::new_v4().to_string();

let snapshot_params = SnapshotParams::new(
Some(entity_id),
Some(parent_id),
Some(txn_id),
Some(snap_name),
Some(snapshot_uuid),
Some(Utc::now().to_string()),
);

lvol.create_snapshot(snapshot_params.clone())
.await
.expect("Failed to create a snapshot");

let mut snapshot_list = Lvol::list_all_snapshots();
assert_eq!(1, snapshot_list.len(), "Snapshot Count not matched!!");
let snapshot_lvol = UntypedBdev::lookup_by_uuid_str(
snapshot_list
.get(0)
.unwrap()
.snapshot_params()
.snapshot_uuid()
.unwrap_or_default()
.as_str(),
)
.map(|b| Lvol::try_from(b).expect("Can't create Lvol from device"))
.unwrap();

// Set snapshot attribute.
let snap_attr_name = String::from("my.attr.name");
let snap_attr_value = String::from("top_secret");

snapshot_lvol
.set_blob_attr(
snap_attr_name.clone(),
snap_attr_value.clone(),
true,
)
.await
.expect("Failed to set snapshot attribute");

// Check attribute.
let v = Lvol::get_blob_xattr(&snapshot_lvol, &snap_attr_name)
.expect("Failed to get snapshot attribute");
assert_eq!(v, snap_attr_value, "Snapshot attribute doesn't match");

// Export pool, then reimport it again and check the attribute again.
pool.export().await.expect("Failed to export test pool");

// Make sure no snapshots exist after pool is exported.
assert_eq!(
Lvol::list_all_snapshots().len(),
0,
"Snapshots still exist after pool was exported"
);

// Recreate the pool device, as pool export destroys it.
device_create(POOL_DEVICE_NAME).await.unwrap();

pool = Lvs::import("pool20", POOL_DEVICE_NAME)
.await
.expect("Failed to import pool");

snapshot_list = Lvol::list_all_snapshots();
assert_eq!(
1,
snapshot_list.len(),
"No snapshots found after pool imported"
);

let imported_snapshot_lvol = UntypedBdev::lookup_by_uuid_str(
snapshot_list
.get(0)
.unwrap()
.snapshot_params()
.snapshot_uuid()
.unwrap_or_default()
.as_str(),
)
.map(|b| Lvol::try_from(b).expect("Can't create Lvol from device"))
.unwrap();

// Get attribute from imported snapshot and check.
let v = Lvol::get_blob_xattr(&imported_snapshot_lvol, &snap_attr_name)
.expect("Failed to get snapshot attribute");
assert_eq!(v, snap_attr_value, "Snapshot attribute doesn't match");

pool.destroy().await.expect("Failed to destroy test pool");
})
.await;
}
6 changes: 3 additions & 3 deletions nix/pkgs/libspdk/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ let
# 7. Copy SHA256 from 'got' of the error message to 'sha256' field.
# 8. 'nix-shell' build must now succeed.
drvAttrs = rec {
version = "23.01-2a61eeb";
version = "23.01-b656160";

src = fetchFromGitHub {
owner = "openebs";
repo = "spdk";
rev = "2a61eebfb93f6414f6ef9161c42cd8dedcf8bc67";
sha256 = "sha256-7j+BgOFPBJwt7oRh3KhB10/Da2zXIYTH7ietZYicyks=";
rev = "b656160537ab6591f0485b942d9e9464e225b3c4";
sha256 = "sha256-9unc8//wRY25pBFzqqINtiNQ1BuYSQ3wNdO2Q3hoEkM=";
fetchSubmodules = true;
};

Expand Down

0 comments on commit 66d0fbc

Please sign in to comment.