Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Use direct lctl device calls for snapshots #2364

Merged
merged 5 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 146 additions & 14 deletions iml-agent/src/action_plugins/lustre/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,46 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{agent_error::ImlAgentError, lustre::lctl};
use combine::{stream::easy, EasyParser};
use crate::{agent_error::ImlAgentError, device_scanner_client, lustre::lctl};
use chrono::{DateTime, TimeZone, Utc};
use futures::future::try_join_all;
use iml_wire_types::snapshot::{Create, Destroy, List, Mount, Snapshot, Unmount};

mod parse;
type DeviceSnapshots = (String, DateTime<Utc>, DateTime<Utc>, Option<String>);

pub async fn list(l: List) -> Result<Vec<Snapshot>, ImlAgentError> {
let mut args = vec!["snapshot_list", "--fsname", &l.fsname];
let mut args = vec!["--device", &l.target, "snapshot", "-l"];
if let Some(name) = &l.name {
args.push("--name");
args.push(name);
}
let stdout = lctl(args).await?;
let stdout = stdout.trim();

if stdout.is_empty() {
Ok(vec![])
} else {
parse_snapshot_list(&stdout).map_err(ImlAgentError::CombineEasyError)
return Ok(vec![]);
}
}

fn parse_snapshot_list(input: &str) -> Result<Vec<Snapshot>, easy::Errors<char, String, usize>> {
let (snapshots, _) = parse::parse().easy_parse(input).map_err(|err| {
err.map_position(|p| p.translate_position(input))
.map_range(String::from)
})?;
let mounts = device_scanner_client::get_snapshot_mounts().await?;

let xs = parse_device_snapshots(stdout).into_iter().map(
|(snapshot_name, create_time, modify_time, comment)| {
build_snapshot(
&l.target,
snapshot_name,
create_time,
modify_time,
comment,
&mounts,
)
},
);

let snapshots = try_join_all(xs)
.await?
.into_iter()
.filter_map(|x| x)
.collect::<Vec<Snapshot>>();

Ok(snapshots)
}

Expand Down Expand Up @@ -73,3 +86,122 @@ pub async fn unmount(u: Unmount) -> Result<(), ImlAgentError> {
let args = &["snapshot_umount", "--fsname", &u.fsname, "--name", &u.name];
lctl(args).await.map(drop)
}

async fn build_snapshot(
target: &str,
snapshot_name: String,
create_time: DateTime<Utc>,
modify_time: DateTime<Utc>,
comment: Option<String>,
mounts: &[device_types::mount::Mount],
) -> Result<Option<Snapshot>, ImlAgentError> {
let mut fs_name = target.rsplitn(2, '-').nth(1).map(String::from);
let filesystem_name = if let Some(name) = fs_name.take() {
name
} else {
return Ok(None);
};

let snapshot_fsname = get_snapshot_label(&snapshot_name, &target).await?;
let snapshot_fsname = if let Some(name) = snapshot_fsname {
name
} else {
return Ok(None);
};

let mounted: bool = mounts
.iter()
.find_map(|x| {
let s = x.opts.0.split(',').find(|x| x.starts_with("svname="))?;

let s = s.split('=').nth(1)?.rsplitn(2, '-').nth(1)?;

if s == snapshot_fsname {
return Some(true);
}

None
})
.unwrap_or_default();

Ok(Some(Snapshot {
filesystem_name,
snapshot_name,
create_time,
modify_time,
comment,
snapshot_fsname,
mounted,
}))
}

async fn get_snapshot_label(
snapshot_name: &str,
target: &str,
) -> Result<Option<String>, ImlAgentError> {
let x = lctl(vec![
"--device",
target,
"snapshot",
"--get_label",
snapshot_name,
])
.await?;
let x = x.trim().rsplitn(2, '-').nth(1).map(String::from);

Ok(x)
}

fn parse_device_snapshots(x: &str) -> Vec<DeviceSnapshots> {
x.trim()
.lines()
.map(|x| x.splitn(4, ' ').collect::<Vec<_>>())
.filter_map(|xs| {
let mut it = xs.into_iter();

let name = it.next()?.to_string();
let create = chrono::Utc.timestamp(it.next()?.parse().ok()?, 0);
let update = chrono::Utc.timestamp(it.next()?.parse().ok()?, 0);
let comment = it.next().map(String::from);

Some((name, create, update, comment))
})
.collect()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_parse_device_list() {
let fixture = r#"
testsnapa 1604388443 1604388443
abcd 1604331109 1604331109
testabc 1604331284 1604331284
snaptwo 1604469817 1604469817
testabc4 1604332160 1604332160
1-es01a-2020-11-02T16:10:24Z 1604333425 1604333425 automatically created by IML
1-es01a-2020-11-02T16:40:24Z 1604335225 1604335225 automatically created by IML
snapone 1604469809 1604469809
testsnapz 1604468838 1604468838
1-es01a-2020-11-02T16:20:24Z 1604334025 1604334025 automatically created by IML
1-es01a-2020-11-02T16:30:24Z 1604334625 1604334625 automatically created by IML
1-es01a-2020-11-02T16:00:24Z 1604332825 1604332825 automatically created by IML
snapthree 1604470070 1604470070
testsnapd 1604469760 1604469760
tstsnap 1604335880 1604335880
testsnapb 1604416099 1604416099
1-es01a-2020-11-02T15:50:24Z 1604332227 1604332227 automatically created by IML
testabc3 1604331898 1604331898
mysnap 1604330923 1604330922 mynew snapshot
Test3 1604416497 1604416497
testsnap 1604330047 1604075186
"#;
let fixture = fixture;

let xs = parse_device_snapshots(fixture);

insta::assert_debug_snapshot!(xs);
}
}
176 changes: 0 additions & 176 deletions iml-agent/src/action_plugins/lustre/snapshot/parse.rs

This file was deleted.

Loading