cyfs_bdt/debug/
command.rs

1use std::{
2    str::FromStr, 
3    path::{ Path, PathBuf },
4    time::Duration,
5};
6use clap::{App, SubCommand, Arg};
7use cyfs_base::*;
8use crate::stack::Stack;
9
10pub fn debug_command_line() -> clap::App<'static, 'static> {
11    App::new("bdt-debug")
12        .about("bdt stack debug")
13        .arg(Arg::with_name("host")
14        .short("h")
15        .long("host")
16        .value_name("host")
17        .help("connect remote host")
18        .default_value("127.0.0.1"))
19        .arg(Arg::with_name("port")
20        .short("p")
21        .long("port")
22        .value_name("port")
23        .help("local server port")
24        .default_value("12345"))
25        .subcommand(SubCommand::with_name("test"))
26        .subcommand(SubCommand::with_name("ping")
27            .arg(Arg::with_name("remote").required(true))
28            .arg(Arg::with_name("count").required(true))
29            .arg(Arg::with_name("timeout").required(true))
30        )
31        .subcommand(SubCommand::with_name("nc")
32            .arg(Arg::with_name("remote").required(true))
33            .arg(Arg::with_name("port").required(true))
34            .arg(Arg::with_name("bench").required(false))
35            .arg(Arg::with_name("task").required(false))
36        )
37        .subcommand(SubCommand::with_name("get_chunk")
38            .arg(Arg::with_name("remotes").required(true))
39            .arg(Arg::with_name("timeout").required(true))
40            .arg(Arg::with_name("chunk_id").required(true))
41            .arg(Arg::with_name("local_path").required(false))
42        )
43        .subcommand(SubCommand::with_name("get_file")
44            .arg(Arg::with_name("remotes").required(true))
45            .arg(Arg::with_name("timeout").required(true))
46            .arg(Arg::with_name("file_id").required(true))
47            .arg(Arg::with_name("local_path").required(true))
48        )
49        .subcommand(SubCommand::with_name("put_chunk")
50            .arg(Arg::with_name("local_path").required(true))
51        )
52        .subcommand(SubCommand::with_name("put_file")
53            .arg(Arg::with_name("local_path").required(true))
54        )
55        .subcommand(SubCommand::with_name("sn_conn_status")
56            .arg(Arg::with_name("timeout").required(true))
57        )
58        .subcommand(SubCommand::with_name("bench_datagram")
59            .arg(Arg::with_name("remote").required(true))
60            .arg(Arg::with_name("plaintext").required(true))
61            .arg(Arg::with_name("timeout").required(true))
62        )
63}
64
65pub enum DebugCommand {
66    Test(DebugCommandTest),
67    Ping(DebugCommandPing), 
68    Nc(DebugCommandNc), 
69    GetChunk(DebugCommandGetChunk),
70    GetFile(DebugCommandGetFile),
71    PutChunk(DebugCommandPutChunk),
72    PutFile(DebugCommandPutFile),
73    SnConnStatus(DebugCommandSnConnStatus),
74    BenchDatagram(DebugCommandBenchDatagram),
75}
76
77impl DebugCommand {
78    pub async fn from_str(stack: &Stack, command: &str) -> Result<Self, String> {
79        let params = debug_command_line().get_matches_from_safe(command.split(" "))
80            .map_err(|err| err.message)?;
81        let subcommand = params.subcommand_name().ok_or_else(|| "no subcommand\r\n".to_string())?;
82        match subcommand {
83            "test" => {
84                let _ = params.subcommand_matches("test").unwrap();
85                Ok(Self::Test(DebugCommandTest{}))
86            },
87            "ping" => {
88                let subcommand = params.subcommand_matches("ping").unwrap();
89                let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
90                    .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
91                let count = u32::from_str(subcommand.value_of("count").unwrap()).unwrap();
92                let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
93
94                Ok(Self::Ping(DebugCommandPing {
95                    remote, 
96                    count, 
97                    timeout: Duration::from_secs(timeout)
98                }))
99            },
100            "nc" => {
101                let subcommand = params.subcommand_matches("nc").unwrap();
102                let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
103                    .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
104                let port = u16::from_str(subcommand.value_of("port").unwrap()).unwrap();
105                let bench = u32::from_str(subcommand.value_of("bench").unwrap_or("0")).unwrap();
106                let task_num = u32::from_str(subcommand.value_of("task").unwrap_or("1")).unwrap();
107                Ok(Self::Nc(DebugCommandNc {
108                    remote, 
109                    port, 
110                    timeout: Duration::from_secs(8),
111                    bench,
112                    task_num,
113                }))
114            }, 
115            "get_chunk" => {
116                let subcommand = params.subcommand_matches("get_chunk").unwrap();
117                let reomotes_str = String::from_str(subcommand.value_of("remotes").unwrap()).unwrap();
118                let remotes_split = reomotes_str.split(",");
119                let mut remotes = Vec::new();
120                for remote_file in remotes_split {
121                    let remote = remote_device(stack, remote_file).await
122                    .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
123                    remotes.push(remote.desc().clone());
124                }
125                let chunk_id_str = String::from_str(subcommand.value_of("chunk_id").unwrap()).unwrap();
126                let chunk_id = ChunkId::from_str(&chunk_id_str.as_str()).map_err(|err| format!("load chunk_id {} fail: {}\r\n",
127                    chunk_id_str, err))?;
128                let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap_or("default")).unwrap();
129                let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
130                let timeout = u32::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
131
132                Ok(Self::GetChunk(DebugCommandGetChunk {
133                    remotes, 
134                    timeout, 
135                    chunk_id, 
136                    local_path
137                }))
138            },
139            "get_file" => {
140                let subcommand = params.subcommand_matches("get_file").unwrap();
141                let reomotes_str = String::from_str(subcommand.value_of("remotes").unwrap()).unwrap();
142                let remotes_split = reomotes_str.split(",");
143                let mut remotes = Vec::new();
144                for remote_file in remotes_split {
145                    let remote = remote_device(stack, remote_file).await
146                    .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
147                    remotes.push(remote.desc().device_id());
148                }
149                let file_id_str = String::from_str(subcommand.value_of("file_id").unwrap()).unwrap();
150                let hex_data = hex::decode(file_id_str.as_bytes()).unwrap();
151                let (file_id, _) = File::raw_decode(hex_data.as_slice()).map_err(|err| format!("load file_id {} fail: {}",
152                    file_id_str, err))?;
153                let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
154                let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
155                let timeout = u32::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
156
157                Ok(Self::GetFile(DebugCommandGetFile {
158                    remotes, 
159                    timeout, 
160                    file_id, 
161                    local_path
162                }))
163            },
164            "put_chunk" => {
165                let subcommand = params.subcommand_matches("put_chunk").unwrap();
166                let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
167                let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
168
169                Ok(Self::PutChunk(DebugCommandPutChunk {
170                    local_path
171                }))
172            },
173            "put_file" => {
174                let subcommand = params.subcommand_matches("put_file").unwrap();
175                let local_path_str = String::from_str(subcommand.value_of("local_path").unwrap()).unwrap();
176                let local_path = PathBuf::from_str(&local_path_str.as_str()).unwrap();
177
178                Ok(Self::PutFile(DebugCommandPutFile {
179                    local_path,
180                }))
181            },
182            "sn_conn_status" => {
183                let subcommand = params.subcommand_matches("sn_conn_status").unwrap();
184                let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
185                Ok(Self::SnConnStatus(DebugCommandSnConnStatus {
186                    timeout_sec: timeout,
187                }))
188            },
189            "bench_datagram" => {
190                let subcommand = params.subcommand_matches("bench_datagram").unwrap();
191                let remote = remote_device(stack, subcommand.value_of("remote").unwrap()).await
192                    .map_err(|err| format!("load remote desc {} failed for {}\r\n", subcommand.value_of("remote").unwrap(), err))?;
193                let plaintext = u32::from_str(subcommand.value_of("plaintext").unwrap()).unwrap();
194                let timeout = u64::from_str(subcommand.value_of("timeout").unwrap()).unwrap();
195
196                Ok(Self::BenchDatagram(DebugCommandBenchDatagram {
197                    remote,
198                    timeout: Duration::from_secs(timeout),
199                    plaintext: plaintext != 0
200                }))
201            }
202            _ => {
203                Err(format!("invalid subcommand {}\r\n", subcommand))
204            }
205        }
206    } 
207}
208
209pub struct DebugCommandTest {}
210
211pub struct DebugCommandBenchDatagram {
212    pub remote: Device, 
213    pub timeout: Duration,
214    pub plaintext: bool,
215}
216
217pub struct DebugCommandPing {
218    pub remote: Device, 
219    pub count: u32, 
220    pub timeout: Duration
221}
222
223#[derive(Clone, Debug)]
224pub struct DebugCommandNc {
225    pub remote: Device, 
226    pub port: u16, 
227    pub timeout: Duration,
228    pub bench: u32,
229    pub task_num: u32,
230}
231
232pub struct DebugCommandGetChunk {
233    pub remotes: Vec<DeviceDesc>, 
234    pub timeout: u32,
235    pub chunk_id: ChunkId,
236    pub local_path: PathBuf,
237}
238
239pub struct DebugCommandGetFile {
240    pub remotes: Vec<DeviceId>, 
241    pub timeout: u32,
242    pub file_id: File,
243    pub local_path: PathBuf,
244}
245
246pub struct DebugCommandPutChunk {
247    pub local_path: PathBuf,
248}
249
250pub struct DebugCommandPutFile {
251    pub local_path: PathBuf,
252}
253
254pub struct DebugCommandSnConnStatus {
255    pub timeout_sec: u64,
256}
257
258async fn remote_device(
259    stack: &Stack, 
260    str: &str) -> BuckyResult<Device> {
261    if let Ok(device_id) = DeviceId::from_str(str) {
262        if let Some(device) = stack.device_cache().get(&device_id).await {
263            Ok(device)
264        } else {
265            Err(BuckyError::new(BuckyErrorCode::NotFound, "not found"))
266        }
267    } else {
268        let path = Path::new(str);
269        if !path.exists() {
270            Err(BuckyError::new(BuckyErrorCode::NotFound, "not found"))
271        } else {
272            let mut buf = vec![];
273            let (device, _) = Device::decode_from_file(&path, &mut buf)?;
274            let device_id = device.desc().device_id();
275            if stack.device_cache().get(&device_id).await.is_none() {
276                stack.device_cache().add(&device_id, &device);
277            }
278            Ok(device)
279        }
280    }
281} 
282
283
284
285
286
287
288
289
290
291
292
293