aboutsummaryrefslogtreecommitdiff
path: root/src/garage/cli_v2/block.rs
blob: ff3c79e98482f00902b5406cef1a56bdb5784c16 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//use bytesize::ByteSize;
use format_table::format_table;

use garage_util::error::*;

use garage_api::admin::api::*;

use crate::cli::structs::*;
use crate::cli_v2::*;

impl Cli {
	pub async fn cmd_block(&self, cmd: BlockOperation) -> Result<(), Error> {
		match cmd {
			BlockOperation::ListErrors => self.cmd_list_block_errors().await,
			BlockOperation::Info { hash } => self.cmd_get_block_info(hash).await,

			bo => cli_v1::cmd_admin(
				&self.admin_rpc_endpoint,
				self.rpc_host,
				AdminRpc::BlockOperation(bo),
			)
			.await
			.ok_or_message("cli_v1"),
		}
	}

	pub async fn cmd_list_block_errors(&self) -> Result<(), Error> {
		let errors = self.local_api_request(LocalListBlockErrorsRequest).await?.0;

		let tf = timeago::Formatter::new();
		let mut tf2 = timeago::Formatter::new();
		tf2.ago("");

		let mut table = vec!["Hash\tRC\tErrors\tLast error\tNext try".into()];
		for e in errors {
			let next_try = if e.next_try_in_secs > 0 {
				tf2.convert(Duration::from_secs(e.next_try_in_secs))
			} else {
				"asap".to_string()
			};
			table.push(format!(
				"{}\t{}\t{}\t{}\tin {}",
				e.block_hash,
				e.refcount,
				e.error_count,
				tf.convert(Duration::from_secs(e.last_try_secs_ago)),
				next_try
			));
		}
		format_table(table);

		Ok(())
	}

	pub async fn cmd_get_block_info(&self, hash: String) -> Result<(), Error> {
		let info = self
			.local_api_request(LocalGetBlockInfoRequest { block_hash: hash })
			.await?;

		println!("Block hash: {}", info.block_hash);
		println!("Refcount: {}", info.refcount);
		println!();

		let mut table = vec!["Version\tBucket\tKey\tMPU\tDeleted".into()];
		let mut nondeleted_count = 0;
		for ver in info.versions.iter() {
			match &ver.backlink {
				Some(BlockVersionBacklink::Object { bucket_id, key }) => {
					table.push(format!(
						"{:.16}\t{:.16}\t{}\t\t{:?}",
						ver.version_id, bucket_id, key, ver.deleted
					));
				}
				Some(BlockVersionBacklink::Upload {
					upload_id,
					upload_deleted: _,
					upload_garbage_collected: _,
					bucket_id,
					key,
				}) => {
					table.push(format!(
						"{:.16}\t{:.16}\t{}\t{:.16}\t{:.16}",
						ver.version_id,
						bucket_id.as_deref().unwrap_or(""),
						key.as_deref().unwrap_or(""),
						upload_id,
						ver.deleted
					));
				}
				None => {
					table.push(format!("{:.16}\t\t\tyes", ver.version_id));
				}
			}
			if !ver.deleted {
				nondeleted_count += 1;
			}
		}
		format_table(table);

		if info.refcount != nondeleted_count {
			println!();
			println!(
                "Warning: refcount does not match number of non-deleted versions, you should try `garage repair block-rc`."
            );
		}

		Ok(())
	}
}