1use serde_json;
30use serde_json::{Map, Value};
31use std::fs::read_dir;
32use std::thread::sleep;
33use std::time::Duration;
34use url;
35
36use super::data::*;
37use super::error::*;
38use super::ipc::{uds_path, TransPort};
39use super::misc::verify_init_id_str;
40
41use std::fs;
42use std::os::linux::fs::MetadataExt;
43
44const DEFAULT_TIMEOUT: u32 = 30_000;
45const JOB_RETRY_INTERVAL: u64 = 1;
46
47pub struct Client {
49 tp: TransPort,
50 plugin_name: String,
51}
52
53#[allow(dead_code)]
54#[derive(Deserialize, Debug)]
55struct Job {
56 status: u32,
57 percent: u8,
58 data: Option<Value>,
59}
60
61const JOB_STATUS_INPROGRESS: u32 = 1;
62const JOB_STATUS_COMPLETE: u32 = 2;
63const JOB_STATUS_ERROR: u32 = 3;
64const VOLUME_THINP_YES: u32 = 1;
65const VOLUME_THINP_NO: u32 = 2;
66const VOLUME_THINP_DEFAULT: u32 = 3;
67const POOL_MEMBER_TYPE_DISK: u32 = 2;
68const POOL_MEMBER_TYPE_POOL: u32 = 3;
69
70const WRITE_CACHE_POLICY_WRITE_BACK: u8 = 2;
71const WRITE_CACHE_POLICY_AUTO: u8 = 3;
72const WRITE_CACHE_POLICY_WRITE_THROUGH: u8 = 4;
73
74const WRITE_CACHE_STATUS_WRITE_BACK: u8 = 2;
75const WRITE_CACHE_STATUS_WRITE_THROUGH: u8 = 3;
76
77const READ_CACHE_POLICY_ENABLED: u8 = 2;
78const READ_CACHE_POLICY_DISABLED: u8 = 3;
79
80const READ_CACHE_STATUS_ENABLED: u8 = 2;
81const READ_CACHE_STATUS_DISABLED: u8 = 3;
82
83const PHYSICAL_DISK_CACHE_ENABLED: u8 = 2;
84const PHYSICAL_DISK_CACHE_DISABLED: u8 = 3;
85const PHYSICAL_DISK_CACHE_USE_DISK_SETTING: u8 = 4;
86
87trait OkOrPlugBug<T> {
88 fn ok_or_plugin_bug(self, val: &Value) -> Result<T>;
89}
90
91impl<T> OkOrPlugBug<T> for Option<T> {
92 fn ok_or_plugin_bug(self, val: &Value) -> Result<T> {
93 match self {
94 Some(i) => Ok(i),
95 None => Err(LsmError::PluginBug(format!(
96 "Plugin return unexpected data: {:?}",
97 val
98 ))),
99 }
100 }
101}
102
103#[derive(Debug)]
105pub struct PluginInfo {
106 pub version: String,
108 pub description: String,
110 pub name: String,
112}
113
114pub fn available_plugins() -> Result<Vec<PluginInfo>> {
122 let mut ret = Vec::new();
123 let uds_path = uds_path();
124 match read_dir(&uds_path) {
125 Err(_) => {
126 return Err(LsmError::DaemonNotRunning(format!(
127 "LibStorageMgmt daemon is not running for \
128 socket folder: '{}'",
129 uds_path
130 )))
131 }
132 Ok(paths) => {
133 for path in paths {
134 match path {
135 Err(_) => continue,
139 Ok(dir_entry) => {
140 let plugin_name = match dir_entry.file_name().into_string() {
141 Ok(i) => i,
142 Err(_) => continue,
143 };
144 let plugin_ipc_path = get_plugin_ipc_path(&plugin_name);
145
146 match fs::metadata(&plugin_ipc_path) {
150 Ok(meta) => {
151 if meta.st_mode() & 0o140000 != 0o140000 {
152 continue;
153 }
154 }
155 Err(_) => continue,
156 }
157
158 let mut tp = TransPort::new(&plugin_ipc_path)?;
163 let val = tp.invoke("plugin_info", None)?;
164 let data: Vec<String> = serde_json::from_value(val.clone())?;
165 let desc = data.first().ok_or_plugin_bug(&val)?;
166 let version = data.get(1).ok_or_plugin_bug(&val)?;
167 ret.push(PluginInfo {
168 version: version.to_string(),
169 description: desc.to_string(),
170 name: plugin_name,
171 });
172 }
173 };
174 }
175 }
176 };
177
178 Ok(ret)
179}
180
181fn get_plugin_ipc_path(plugin_name: &str) -> String {
182 format!("{}/{}", uds_path(), plugin_name)
183}
184
185impl Client {
186 pub fn new(uri: &str, password: Option<&str>, timeout: Option<u32>) -> Result<Client> {
194 let p = match url::Url::parse(uri) {
195 Ok(p) => p,
196 Err(e) => {
197 return Err(LsmError::InvalidArgument(format!(
198 "Failed to parse URI: {}",
199 e
200 )))
201 }
202 };
203 let plugin_name = p.scheme().to_string();
204 let plugin_ipc_path = get_plugin_ipc_path(&plugin_name);
205 let mut tp = TransPort::new(&plugin_ipc_path)?;
206 let mut args = Map::new();
207 let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT);
208 args.insert("password".to_string(), serde_json::to_value(password)?);
209 args.insert("uri".to_string(), serde_json::to_value(uri)?);
210 args.insert("timeout".to_string(), serde_json::to_value(timeout)?);
211 tp.invoke("plugin_register", Some(args))?;
212
213 Ok(Client { tp, plugin_name })
214 }
215
216 pub fn systems(&mut self) -> Result<Vec<System>> {
218 Ok(serde_json::from_value(self.tp.invoke("systems", None)?)?)
219 }
220
221 pub fn volumes(&mut self) -> Result<Vec<Volume>> {
223 Ok(serde_json::from_value(self.tp.invoke("volumes", None)?)?)
224 }
225
226 pub fn pools(&mut self) -> Result<Vec<Pool>> {
228 Ok(serde_json::from_value(self.tp.invoke("pools", None)?)?)
229 }
230
231 pub fn disks(&mut self) -> Result<Vec<Disk>> {
233 Ok(serde_json::from_value(self.tp.invoke("disks", None)?)?)
234 }
235
236 pub fn fs(&mut self) -> Result<Vec<FileSystem>> {
238 Ok(serde_json::from_value(self.tp.invoke("fs", None)?)?)
239 }
240
241 pub fn nfs_exports(&mut self) -> Result<Vec<NfsExport>> {
243 Ok(serde_json::from_value(self.tp.invoke("exports", None)?)?)
244 }
245
246 pub fn access_groups(&mut self) -> Result<Vec<AccessGroup>> {
248 Ok(serde_json::from_value(
249 self.tp.invoke("access_groups", None)?,
250 )?)
251 }
252
253 pub fn target_ports(&mut self) -> Result<Vec<TargetPort>> {
255 Ok(serde_json::from_value(
256 self.tp.invoke("target_ports", None)?,
257 )?)
258 }
259
260 pub fn batteries(&mut self) -> Result<Vec<Battery>> {
262 Ok(serde_json::from_value(self.tp.invoke("batteries", None)?)?)
263 }
264
265 fn _job_free(&mut self, job_id: &str) -> Result<()> {
266 let mut args = Map::new();
267 args.insert("job_id".to_string(), serde_json::to_value(job_id)?);
268 self.tp.invoke("job_free", Some(args))?;
269 Ok(())
270 }
271
272 fn _wait_job(&mut self, job_id: &str) -> Result<Value> {
273 loop {
274 let mut args = Map::new();
275 args.insert("job_id".to_string(), serde_json::to_value(job_id)?);
276 let j: Job = serde_json::from_value(self.tp.invoke("job_status", Some(args))?)?;
277
278 match j.status {
279 JOB_STATUS_INPROGRESS => {
280 sleep(Duration::new(JOB_RETRY_INTERVAL, 0));
281 continue;
282 }
283 JOB_STATUS_COMPLETE => match j.data {
284 Some(v) => {
285 self._job_free(job_id)?;
286 return Ok(v);
287 }
288 None => break,
289 },
290 JOB_STATUS_ERROR =>
291 {
294 return Err(LsmError::PluginBug(
295 "Got no error detail for failed job".to_string(),
296 ))
297 }
298 _ => {
299 return Err(LsmError::PluginBug(format!(
300 "Got invalid job status {}",
301 j.status
302 )))
303 }
304 };
305 }
306 Ok(Value::Null)
307 }
308
309 fn wait_job_none(&mut self, job_id: &str) -> Result<()> {
310 self._wait_job(job_id)?;
311 Ok(())
312 }
313
314 fn wait_job_volume(&mut self, job_id: &str) -> Result<Volume> {
315 match self._wait_job(job_id) {
316 Ok(j) => {
317 if j.is_null() {
318 Err(LsmError::PluginBug(
319 "Expecting a volume, but got None".to_string(),
320 ))
321 } else {
322 let v: Volume = serde_json::from_value(j)?;
323 Ok(v)
324 }
325 }
326 Err(e) => Err(e),
327 }
328 }
329
330 fn wait_job_fs(&mut self, job_id: &str) -> Result<FileSystem> {
331 match self._wait_job(job_id) {
332 Ok(j) => {
333 if j.is_null() {
334 Err(LsmError::PluginBug(
335 "Expecting a file system, but got None".to_string(),
336 ))
337 } else {
338 let f: FileSystem = serde_json::from_value(j)?;
339 Ok(f)
340 }
341 }
342 Err(e) => Err(e),
343 }
344 }
345
346 fn wait_job_fs_snap(&mut self, job_id: &str) -> Result<FileSystemSnapShot> {
347 match self._wait_job(job_id) {
348 Ok(j) => {
349 if j.is_null() {
350 Err(LsmError::PluginBug(
351 "Expecting a file system snapshot, but got None".to_string(),
352 ))
353 } else {
354 let f: FileSystemSnapShot = serde_json::from_value(j)?;
355 Ok(f)
356 }
357 }
358 Err(e) => Err(e),
359 }
360 }
361
362 pub fn volume_create(
376 &mut self,
377 pool: &Pool,
378 name: &str,
379 size_bytes: u64,
380 thinp: &VolumeCreateArgThinP,
381 ) -> Result<Volume> {
382 let mut args = Map::new();
383 let thinp_val = match *thinp {
384 VolumeCreateArgThinP::Full => serde_json::to_value(VOLUME_THINP_YES)?,
385 VolumeCreateArgThinP::Thin => serde_json::to_value(VOLUME_THINP_NO)?,
386 VolumeCreateArgThinP::Default => serde_json::to_value(VOLUME_THINP_DEFAULT)?,
387 };
388 args.insert("provisioning".to_string(), thinp_val);
389 args.insert("size_bytes".to_string(), serde_json::to_value(size_bytes)?);
390 args.insert("volume_name".to_string(), serde_json::to_value(name)?);
391 args.insert("pool".to_string(), serde_json::to_value(pool)?);
392
393 let ret = self.tp.invoke("volume_create", Some(args))?;
394 self.get_vol_from_async(&ret)
395 }
396
397 pub fn volume_delete(&mut self, vol: &Volume) -> Result<()> {
408 let mut args = Map::new();
409 args.insert("volume".to_string(), serde_json::to_value(vol)?);
410 let ret = self.tp.invoke("volume_delete", Some(args))?;
411 self.wait_if_async(&ret)
412 }
413
414 pub fn time_out_set(&mut self, ms: u32) -> Result<()> {
416 let mut args = Map::new();
417 args.insert("ms".to_string(), serde_json::to_value(ms)?);
418 self.tp.invoke("time_out_set", Some(args))?;
419 Ok(())
420 }
421
422 pub fn time_out_get(&mut self) -> Result<u32> {
424 Ok(serde_json::from_value(
425 self.tp.invoke("time_out_get", None)?,
426 )?)
427 }
428
429 pub fn capabilities(&mut self, sys: &System) -> Result<Capabilities> {
441 let mut args = Map::new();
442 args.insert("system".to_string(), serde_json::to_value(sys)?);
443 Ok(serde_json::from_value(
444 self.tp.invoke("capabilities", Some(args))?,
445 )?)
446 }
447
448 pub fn plugin_info(&mut self) -> Result<PluginInfo> {
450 let val = self.tp.invoke("plugin_info", None)?;
451 let data: Vec<String> = serde_json::from_value(val.clone())?;
452 let desc = data.first().ok_or_plugin_bug(&val)?;
453 let version = data.get(1).ok_or_plugin_bug(&val)?;
454 Ok(PluginInfo {
455 version: version.to_string(),
456 description: desc.to_string(),
457 name: self.plugin_name.clone(),
458 })
459 }
460
461 pub fn sys_read_cache_pct_set(&mut self, sys: &System, read_pct: u32) -> Result<()> {
469 if read_pct > 100 {
470 return Err(LsmError::InvalidArgument(
471 "Invalid read_pct, should be in range 0 - 100".to_string(),
472 ));
473 }
474 let mut args = Map::new();
475 args.insert("system".to_string(), serde_json::to_value(sys)?);
476 args.insert("read_pct".to_string(), serde_json::to_value(read_pct)?);
477 Ok(serde_json::from_value(
478 self.tp.invoke("system_read_cache_pct_update", Some(args))?,
479 )?)
480 }
481
482 pub fn iscsi_chap_auth_set(
494 &mut self,
495 init_id: &str,
496 in_user: Option<&str>,
497 in_pass: Option<&str>,
498 out_user: Option<&str>,
499 out_pass: Option<&str>,
500 ) -> Result<()> {
501 let mut args = Map::new();
502 args.insert("init_id".to_string(), serde_json::to_value(init_id)?);
503 args.insert(
504 "in_user".to_string(),
505 serde_json::to_value(in_user.unwrap_or(""))?,
506 );
507 args.insert(
508 "in_password".to_string(),
509 serde_json::to_value(in_pass.unwrap_or(""))?,
510 );
511 args.insert(
512 "out_user".to_string(),
513 serde_json::to_value(out_user.unwrap_or(""))?,
514 );
515 args.insert(
516 "out_password".to_string(),
517 serde_json::to_value(out_pass.unwrap_or(""))?,
518 );
519 self.tp.invoke("iscsi_chap_auth", Some(args))?;
520 Ok(())
521 }
522
523 pub fn volume_resize(&mut self, vol: &Volume, new_size_bytes: u64) -> Result<Volume> {
530 let mut args = Map::new();
531 args.insert("volume".to_string(), serde_json::to_value(vol)?);
532 args.insert(
533 "new_size_bytes".to_string(),
534 serde_json::to_value(new_size_bytes)?,
535 );
536 let ret = self.tp.invoke("volume_resize", Some(args))?;
537 self.get_vol_from_async(&ret)
538 }
539
540 fn wait_if_async(&mut self, ret: &Value) -> Result<()> {
541 if ret.is_null() {
542 return Ok(());
543 }
544 self.wait_job_none(ret.as_str().ok_or_plugin_bug(ret)?)
545 }
546
547 fn get_fs_from_async(&mut self, ret: &Value) -> Result<FileSystem> {
549 let ret_array = ret.as_array().ok_or_plugin_bug(ret)?;
550 if ret_array.len() != 2 {
551 return Err(LsmError::PluginBug(format!(
552 "Plugin return unexpected data: {:?}",
553 ret
554 )));
555 }
556 let job_id = ret_array.first().ok_or_plugin_bug(ret)?;
557 if job_id.is_null() {
558 Ok(serde_json::from_value(
559 ret_array.get(1).ok_or_plugin_bug(ret)?.clone(),
560 )?)
561 } else {
562 self.wait_job_fs(job_id.as_str().ok_or_plugin_bug(ret)?)
563 }
564 }
565
566 fn get_vol_from_async(&mut self, ret: &Value) -> Result<Volume> {
567 let ret_array = ret.as_array().ok_or_plugin_bug(ret)?;
568 if ret_array.len() != 2 {
569 return Err(LsmError::PluginBug(format!(
570 "Plugin return unexpected data: {:?}",
571 ret
572 )));
573 }
574 let job_id = ret_array.first().ok_or_plugin_bug(ret)?;
575 if job_id.is_null() {
576 Ok(serde_json::from_value(
577 ret_array.get(1).ok_or_plugin_bug(ret)?.clone(),
578 )?)
579 } else {
580 self.wait_job_volume(job_id.as_str().ok_or_plugin_bug(ret)?)
581 }
582 }
583
584 fn get_fs_snap_from_asyn(&mut self, ret: &Value) -> Result<FileSystemSnapShot> {
585 let ret_array = ret.as_array().ok_or_plugin_bug(ret)?;
586 if ret_array.len() != 2 {
587 return Err(LsmError::PluginBug(format!(
588 "Plugin return unexpected data: {:?}",
589 ret
590 )));
591 }
592 let job_id = ret_array.first().ok_or_plugin_bug(ret)?;
593 if job_id.is_null() {
594 Ok(serde_json::from_value(
595 ret_array.first().ok_or_plugin_bug(ret)?.clone(),
596 )?)
597 } else {
598 self.wait_job_fs_snap(job_id.as_str().ok_or_plugin_bug(ret)?)
599 }
600 }
601
602 pub fn volume_replicate(
611 &mut self,
612 pool: Option<Pool>,
613 rep_type: VolumeReplicateType,
614 src_vol: &Volume,
615 name: &str,
616 ) -> Result<Volume> {
617 let mut args = Map::new();
618 args.insert("pool".to_string(), serde_json::to_value(pool)?);
619 args.insert("volume_src".to_string(), serde_json::to_value(src_vol)?);
620 args.insert(
621 "rep_type".to_string(),
622 serde_json::to_value(rep_type as i32)?,
623 );
624 args.insert("name".to_string(), serde_json::to_value(name)?);
625 let ret = self.tp.invoke("volume_replicate", Some(args))?;
626 self.get_vol_from_async(&ret)
627 }
628
629 pub fn volume_rep_range_blk_size(&mut self, sys: &System) -> Result<u32> {
633 let mut args = Map::new();
634 args.insert("system".to_string(), serde_json::to_value(sys)?);
635 Ok(serde_json::from_value(self.tp.invoke(
636 "volume_replicate_range_block_size",
637 Some(args),
638 )?)?)
639 }
640
641 pub fn volume_replicate_range(
648 &mut self,
649 rep_type: VolumeReplicateType,
650 src_vol: &Volume,
651 dst_vol: &Volume,
652 ranges: &[BlockRange],
653 ) -> Result<()> {
654 let mut args = Map::new();
655 args.insert(
656 "rep_type".to_string(),
657 serde_json::to_value(rep_type as i32)?,
658 );
659 args.insert("ranges".to_string(), serde_json::to_value(ranges)?);
660 args.insert("volume_src".to_string(), serde_json::to_value(src_vol)?);
661 args.insert("volume_dest".to_string(), serde_json::to_value(dst_vol)?);
662 let ret = self.tp.invoke("volume_replicate_range", Some(args))?;
663 self.wait_if_async(&ret)
664 }
665
666 pub fn volume_enable(&mut self, vol: &Volume) -> Result<()> {
673 let mut args = Map::new();
674 args.insert("volume".to_string(), serde_json::to_value(vol)?);
675 self.tp.invoke("volume_enable", Some(args))?;
676 Ok(())
677 }
678
679 pub fn volume_disable(&mut self, vol: &Volume) -> Result<()> {
681 let mut args = Map::new();
682 args.insert("volume".to_string(), serde_json::to_value(vol)?);
683 self.tp.invoke("volume_disable", Some(args))?;
684 Ok(())
685 }
686
687 pub fn volume_mask(&mut self, vol: &Volume, ag: &AccessGroup) -> Result<()> {
697 let mut args = Map::new();
698 args.insert("volume".to_string(), serde_json::to_value(vol)?);
699 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
700 self.tp.invoke("volume_mask", Some(args))?;
701 Ok(())
702 }
703
704 pub fn volume_unmask(&mut self, vol: &Volume, ag: &AccessGroup) -> Result<()> {
706 let mut args = Map::new();
707 args.insert("volume".to_string(), serde_json::to_value(vol)?);
708 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
709 self.tp.invoke("volume_unmask", Some(args))?;
710 Ok(())
711 }
712
713 pub fn access_group_create(
727 &mut self,
728 name: &str,
729 init_id: &str,
730 init_type: InitiatorType,
731 sys: &System,
732 ) -> Result<AccessGroup> {
733 verify_init_id_str(init_id, init_type)?;
734 let mut args = Map::new();
735 args.insert("name".to_string(), serde_json::to_value(name)?);
736 args.insert("init_id".to_string(), serde_json::to_value(init_id)?);
737 args.insert(
738 "init_type".to_string(),
739 serde_json::to_value(init_type as i32)?,
740 );
741 args.insert("system".to_string(), serde_json::to_value(sys)?);
742 Ok(serde_json::from_value(
743 self.tp.invoke("access_group_create", Some(args))?,
744 )?)
745 }
746
747 pub fn access_group_delete(&mut self, ag: &AccessGroup) -> Result<()> {
756 let mut args = Map::new();
757 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
758 self.tp.invoke("access_group_delete", Some(args))?;
759 Ok(())
760 }
761
762 pub fn access_group_init_add(
771 &mut self,
772 ag: &AccessGroup,
773 init_id: &str,
774 init_type: InitiatorType,
775 ) -> Result<AccessGroup> {
776 verify_init_id_str(init_id, init_type)?;
777 let mut args = Map::new();
778 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
779 args.insert("init_id".to_string(), serde_json::to_value(init_id)?);
780 args.insert(
781 "init_type".to_string(),
782 serde_json::to_value(init_type as i32)?,
783 );
784 Ok(serde_json::from_value(
785 self.tp.invoke("access_group_initiator_add", Some(args))?,
786 )?)
787 }
788
789 pub fn access_group_init_del(
799 &mut self,
800 ag: &AccessGroup,
801 init_id: &str,
802 init_type: InitiatorType,
803 ) -> Result<AccessGroup> {
804 verify_init_id_str(init_id, init_type)?;
805 let mut args = Map::new();
806 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
807 args.insert("init_id".to_string(), serde_json::to_value(init_id)?);
808 args.insert(
809 "init_type".to_string(),
810 serde_json::to_value(init_type as i32)?,
811 );
812 Ok(serde_json::from_value(
813 self.tp
814 .invoke("access_group_initiator_delete", Some(args))?,
815 )?)
816 }
817
818 pub fn vols_masked_to_ag(&mut self, ag: &AccessGroup) -> Result<Vec<Volume>> {
820 let mut args = Map::new();
821 args.insert("access_group".to_string(), serde_json::to_value(ag)?);
822 Ok(serde_json::from_value(self.tp.invoke(
823 "volumes_accessible_by_access_group",
824 Some(args),
825 )?)?)
826 }
827
828 pub fn ags_granted_to_vol(&mut self, vol: &Volume) -> Result<Vec<AccessGroup>> {
830 let mut args = Map::new();
831 args.insert("volume".to_string(), serde_json::to_value(vol)?);
832 Ok(serde_json::from_value(
833 self.tp
834 .invoke("access_groups_granted_to_volume", Some(args))?,
835 )?)
836 }
837
838 pub fn vol_has_child_dep(&mut self, vol: &Volume) -> Result<bool> {
840 let mut args = Map::new();
841 args.insert("volume".to_string(), serde_json::to_value(vol)?);
842 Ok(serde_json::from_value(
843 self.tp.invoke("volume_child_dependency", Some(args))?,
844 )?)
845 }
846
847 pub fn vol_child_dep_rm(&mut self, vol: &Volume) -> Result<()> {
854 let mut args = Map::new();
855 args.insert("volume".to_string(), serde_json::to_value(vol)?);
856 let ret = self.tp.invoke("volume_child_dependency_rm", Some(args))?;
857 self.wait_if_async(&ret)
858 }
859
860 pub fn fs_create(&mut self, pool: &Pool, name: &str, size_bytes: u64) -> Result<FileSystem> {
871 let mut args = Map::new();
872 args.insert("size_bytes".to_string(), serde_json::to_value(size_bytes)?);
873 args.insert("name".to_string(), serde_json::to_value(name)?);
874 args.insert("pool".to_string(), serde_json::to_value(pool)?);
875
876 let ret = self.tp.invoke("fs_create", Some(args))?;
877 self.get_fs_from_async(&ret)
878 }
879
880 pub fn fs_resize(&mut self, fs: &FileSystem, new_size_bytes: u64) -> Result<FileSystem> {
882 let mut args = Map::new();
883 args.insert("fs".to_string(), serde_json::to_value(fs)?);
884 args.insert(
885 "new_size_bytes".to_string(),
886 serde_json::to_value(new_size_bytes)?,
887 );
888 let ret = self.tp.invoke("fs_resize", Some(args))?;
889 self.get_fs_from_async(&ret)
890 }
891
892 pub fn fs_delete(&mut self, fs: &FileSystem) -> Result<()> {
903 let mut args = Map::new();
904 args.insert("fs".to_string(), serde_json::to_value(fs)?);
905 let ret = self.tp.invoke("fs_delete", Some(args))?;
906 self.wait_if_async(&ret)
907 }
908
909 pub fn fs_clone(
918 &mut self,
919 src_fs: &FileSystem,
920 dst_fs_name: &str,
921 snapshot: Option<&FileSystemSnapShot>,
922 ) -> Result<FileSystem> {
923 let mut args = Map::new();
924 args.insert("src_fs".to_string(), serde_json::to_value(src_fs)?);
925 args.insert(
926 "dest_fs_name".to_string(),
927 serde_json::to_value(dst_fs_name)?,
928 );
929 args.insert("snapshot".to_string(), serde_json::to_value(snapshot)?);
930
931 let ret = self.tp.invoke("fs_clone", Some(args))?;
932 self.get_fs_from_async(&ret)
933 }
934
935 pub fn fs_file_clone(
940 &mut self,
941 fs: &FileSystem,
942 src_file_name: &str,
943 dst_file_name: &str,
944 snapshot: Option<&FileSystemSnapShot>,
945 ) -> Result<()> {
946 let mut args = Map::new();
947 args.insert("fs".to_string(), serde_json::to_value(fs)?);
948 args.insert(
949 "src_file_name".to_string(),
950 serde_json::to_value(src_file_name)?,
951 );
952 args.insert(
953 "dest_file_name".to_string(),
954 serde_json::to_value(dst_file_name)?,
955 );
956 args.insert("snapshot".to_string(), serde_json::to_value(snapshot)?);
957
958 let ret = self.tp.invoke("fs_file_clone", Some(args))?;
959 self.wait_if_async(&ret)
960 }
961
962 pub fn fs_snapshots(&mut self, fs: &FileSystem) -> Result<Vec<FileSystemSnapShot>> {
964 let mut args = Map::new();
965 args.insert("fs".to_string(), serde_json::to_value(fs)?);
966 Ok(serde_json::from_value(
967 self.tp.invoke("fs_snapshots", Some(args))?,
968 )?)
969 }
970
971 pub fn fs_snapshot_create(
973 &mut self,
974 fs: &FileSystem,
975 name: &str,
976 ) -> Result<FileSystemSnapShot> {
977 let mut args = Map::new();
978 args.insert("fs".to_string(), serde_json::to_value(fs)?);
979 args.insert("snapshot_name".to_string(), serde_json::to_value(name)?);
980 let ret = self.tp.invoke("fs_snapshot_create", Some(args))?;
981 self.get_fs_snap_from_asyn(&ret)
982 }
983
984 pub fn fs_snapshot_delete(
986 &mut self,
987 fs: &FileSystem,
988 snapshot: &FileSystemSnapShot,
989 ) -> Result<()> {
990 let mut args = Map::new();
991 args.insert("fs".to_string(), serde_json::to_value(fs)?);
992 args.insert("snapshot".to_string(), serde_json::to_value(snapshot)?);
993 let ret = self.tp.invoke("fs_snapshot_delete", Some(args))?;
994 self.wait_if_async(&ret)
995 }
996
997 pub fn fs_snapshot_restore(
1008 &mut self,
1009 fs: &FileSystem,
1010 snapshot: &FileSystemSnapShot,
1011 all_file: bool,
1012 files: Option<&[&str]>,
1013 restore_files: Option<&[&str]>,
1014 ) -> Result<()> {
1015 let mut args = Map::new();
1016 if all_file {
1017 let files: [&str; 0] = [];
1018 let restore_files: [&str; 0] = [];
1019 args.insert("files".to_string(), serde_json::to_value(files)?);
1020 args.insert(
1021 "restore_files".to_string(),
1022 serde_json::to_value(restore_files)?,
1023 );
1024 } else {
1025 let files = files.unwrap_or(&[]);
1026 if files.is_empty() {
1027 return Err(LsmError::InvalidArgument(
1028 "Invalid argument: `all_file` is false while \
1029 `files` is empty"
1030 .to_string(),
1031 ));
1032 }
1033 let restore_files = restore_files.unwrap_or(&[]);
1034 if !restore_files.is_empty() && files.len() != restore_files.len() {
1035 return Err(LsmError::InvalidArgument(
1036 "Invalid argument: `files` and `restore_files` have \
1037 different lengths"
1038 .to_string(),
1039 ));
1040 }
1041 args.insert("files".to_string(), serde_json::to_value(files)?);
1042 args.insert(
1043 "restore_files".to_string(),
1044 serde_json::to_value(restore_files)?,
1045 );
1046 }
1047
1048 args.insert("fs".to_string(), serde_json::to_value(fs)?);
1049 args.insert("snapshot".to_string(), serde_json::to_value(snapshot)?);
1050 args.insert("all_files".to_string(), serde_json::to_value(all_file)?);
1051 let ret = self.tp.invoke("fs_snapshot_restore", Some(args))?;
1052 self.wait_if_async(&ret)
1053 }
1054
1055 pub fn fs_has_child_dep(&mut self, fs: &FileSystem, files: Option<Vec<&str>>) -> Result<bool> {
1057 let mut args = Map::new();
1058 args.insert("fs".to_string(), serde_json::to_value(fs)?);
1059 let files: Vec<&str> = files.unwrap_or_default();
1060 args.insert("files".to_string(), serde_json::to_value(files)?);
1061 Ok(serde_json::from_value(
1062 self.tp.invoke("fs_child_dependency", Some(args))?,
1063 )?)
1064 }
1065
1066 pub fn fs_child_dep_rm(&mut self, fs: &FileSystem, files: Option<Vec<&str>>) -> Result<()> {
1073 let mut args = Map::new();
1074 args.insert("fs".to_string(), serde_json::to_value(fs)?);
1075 let files: Vec<&str> = files.unwrap_or_default();
1076 args.insert("files".to_string(), serde_json::to_value(files)?);
1077 let ret = self.tp.invoke("fs_child_dependency_rm", Some(args))?;
1078 self.wait_if_async(&ret)
1079 }
1080
1081 pub fn nfs_exp_auth_type_list(&mut self) -> Result<Vec<String>> {
1083 Ok(serde_json::from_value(
1084 self.tp.invoke("export_auth", None)?,
1085 )?)
1086 }
1087
1088 pub fn fs_export(
1100 &mut self,
1101 fs: &FileSystem,
1102 export_path: Option<&str>,
1103 access: &NfsAccess,
1104 auth_type: Option<&str>,
1105 options: Option<&str>,
1106 ) -> Result<NfsExport> {
1107 let root_list = access.root_list;
1108 let rw_list = access.rw_list;
1109 let ro_list = access.ro_list;
1110
1111 if rw_list.is_empty() && ro_list.is_empty() {
1112 return Err(LsmError::InvalidArgument(
1113 "At least one host should exists in `rw_list` or `ro_list`".to_string(),
1114 ));
1115 }
1116 for host in root_list {
1117 if !rw_list.contains(host) && !ro_list.contains(host) {
1118 return Err(LsmError::InvalidArgument(format!(
1119 "Host defined in `root_list` should be also \
1120 defined in `rw_list` or `ro_list`: '{}'",
1121 host
1122 )));
1123 }
1124 }
1125 for host in rw_list {
1126 if ro_list.contains(host) {
1127 return Err(LsmError::InvalidArgument(format!(
1128 "Host should not both in `rw_list` \
1129 and `ro_list`: '{}'",
1130 host
1131 )));
1132 }
1133 }
1134
1135 let mut args = Map::new();
1136 args.insert("fs_id".to_string(), serde_json::to_value(&fs.id)?);
1137 args.insert(
1138 "export_path".to_string(),
1139 serde_json::to_value(export_path)?,
1140 );
1141 args.insert("root_list".to_string(), serde_json::to_value(root_list)?);
1142 args.insert("rw_list".to_string(), serde_json::to_value(rw_list)?);
1143 args.insert("ro_list".to_string(), serde_json::to_value(ro_list)?);
1144
1145 let anon_uid = access.anon_uid.unwrap_or(NfsExport::ANON_UID_GID_NA);
1146 let anon_gid = access.anon_gid.unwrap_or(NfsExport::ANON_UID_GID_NA);
1147 args.insert("anon_uid".to_string(), serde_json::to_value(anon_uid)?);
1148 args.insert("anon_gid".to_string(), serde_json::to_value(anon_gid)?);
1149 args.insert("auth_type".to_string(), serde_json::to_value(auth_type)?);
1150 args.insert("options".to_string(), serde_json::to_value(options)?);
1151 Ok(serde_json::from_value(
1152 self.tp.invoke("export_fs", Some(args))?,
1153 )?)
1154 }
1155
1156 pub fn fs_unexport(&mut self, exp: &NfsExport) -> Result<()> {
1158 let mut args = Map::new();
1159 args.insert("export".to_string(), serde_json::to_value(exp)?);
1160 Ok(serde_json::from_value(
1161 self.tp.invoke("export_remove", Some(args))?,
1162 )?)
1163 }
1164
1165 pub fn vol_raid_info(&mut self, vol: &Volume) -> Result<VolumeRaidInfo> {
1167 let mut args = Map::new();
1168 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1169 let ret: Vec<i32> =
1170 serde_json::from_value(self.tp.invoke("volume_raid_info", Some(args))?)?;
1171 if ret.len() != 5 {
1172 return Err(LsmError::PluginBug(format!(
1173 "vol_raid_info() is expecting 5 integers from plugin, \
1174 but got '{:?}'",
1175 ret
1176 )));
1177 }
1178 Ok(VolumeRaidInfo {
1179 raid_type: From::from(ret[0]),
1180 strip_size: ret[1] as u32,
1181 disk_count: ret[2] as u32,
1182 min_io_size: ret[3] as u32,
1183 opt_io_size: ret[4] as u32,
1184 })
1185 }
1186
1187 pub fn pool_member_info(&mut self, pool: &Pool) -> Result<PoolMemberInfo> {
1189 let mut args = Map::new();
1190 args.insert("pool".to_string(), serde_json::to_value(pool)?);
1191 let ret = self.tp.invoke("pool_member_info", Some(args))?;
1192 let ret_array = ret.as_array().ok_or_plugin_bug(&ret)?;
1193 if ret_array.len() != 3 {
1194 return Err(LsmError::PluginBug(format!(
1195 "Plugin return unexpected data: {:?}",
1196 ret
1197 )));
1198 }
1199 let raid_type: i32 =
1200 serde_json::from_value(ret_array.first().ok_or_plugin_bug(&ret)?.clone())?;
1201 let raid_type: RaidType = From::from(raid_type);
1202 let member_type: u32 =
1203 serde_json::from_value(ret_array.get(1).ok_or_plugin_bug(&ret)?.clone())?;
1204 let member_ids: Vec<String> =
1205 serde_json::from_value(ret_array.get(2).ok_or_plugin_bug(&ret)?.clone())?;
1206 let mut members: Vec<PoolMember> = Vec::new();
1207 match member_type {
1208 POOL_MEMBER_TYPE_DISK => {
1209 for disk in self.disks()? {
1210 if member_ids.contains(&disk.id) {
1211 members.push(PoolMember::Disk(disk));
1212 }
1213 }
1214 }
1215 POOL_MEMBER_TYPE_POOL => {
1216 for pool in self.pools()? {
1217 if member_ids.contains(&pool.id) {
1218 members.push(PoolMember::Pool(pool));
1219 }
1220 }
1221 }
1222 _ => (),
1223 };
1224 Ok(PoolMemberInfo { raid_type, members })
1225 }
1226
1227 pub fn vol_raid_create_cap_get(&mut self, sys: &System) -> Result<(Vec<RaidType>, Vec<u32>)> {
1232 let mut args = Map::new();
1233 args.insert("system".to_string(), serde_json::to_value(sys)?);
1234 let ret = self.tp.invoke("volume_raid_create_cap_get", Some(args))?;
1235 let ret_array = ret.as_array().ok_or_plugin_bug(&ret)?;
1236 if ret_array.len() != 2 {
1237 return Err(LsmError::PluginBug(format!(
1238 "vol_raid_create_cap_get() is expecting array with \
1239 2 members from plugin, but got '{:?}'",
1240 ret
1241 )));
1242 }
1243 let raid_types: Vec<i32> =
1244 serde_json::from_value(ret_array.first().ok_or_plugin_bug(&ret)?.clone())?;
1245 let strip_sizes: Vec<u32> =
1246 serde_json::from_value(ret_array.get(1).ok_or_plugin_bug(&ret)?.clone())?;
1247 let mut new_raid_types: Vec<RaidType> = Vec::new();
1248 for raid_type in raid_types {
1249 new_raid_types.push(From::from(raid_type));
1250 }
1251 Ok((new_raid_types, strip_sizes))
1252 }
1253
1254 pub fn vol_raid_create(
1256 &mut self,
1257 name: &str,
1258 raid_type: RaidType,
1259 disks: &[Disk],
1260 strip_size: Option<u32>,
1261 ) -> Result<Volume> {
1262 if disks.is_empty() {
1263 return Err(LsmError::InvalidArgument("no disk included".to_string()));
1264 }
1265
1266 if raid_type == RaidType::Raid1 && disks.len() != 2 {
1267 return Err(LsmError::InvalidArgument(
1268 "RAID 1 only allow 2 disks".to_string(),
1269 ));
1270 }
1271
1272 if raid_type == RaidType::Raid5 && disks.len() < 3 {
1273 return Err(LsmError::InvalidArgument(
1274 "RAID 5 require 3 or more disks".to_string(),
1275 ));
1276 }
1277
1278 if raid_type == RaidType::Raid6 && disks.len() < 4 {
1279 return Err(LsmError::InvalidArgument(
1280 "RAID 6 require 4 or more disks".to_string(),
1281 ));
1282 }
1283
1284 if raid_type == RaidType::Raid10 && (disks.len() % 2 != 0 || disks.len() < 4) {
1285 return Err(LsmError::InvalidArgument(
1286 "RAID 10 require even disks count and 4 or more disks".to_string(),
1287 ));
1288 }
1289
1290 if raid_type == RaidType::Raid50 && (disks.len() % 2 != 0 || disks.len() < 6) {
1291 return Err(LsmError::InvalidArgument(
1292 "RAID 50 require even disks count and 6 or more disks".to_string(),
1293 ));
1294 }
1295
1296 if raid_type == RaidType::Raid60 && (disks.len() % 2 != 0 || disks.len() < 8) {
1297 return Err(LsmError::InvalidArgument(
1298 "RAID 60 require even disks count and 8 or more disks".to_string(),
1299 ));
1300 }
1301
1302 let mut args = Map::new();
1303 args.insert("name".to_string(), serde_json::to_value(name)?);
1304 args.insert(
1305 "raid_type".to_string(),
1306 serde_json::to_value(raid_type as i32)?,
1307 );
1308 args.insert("disks".to_string(), serde_json::to_value(disks)?);
1309 let strip_size = strip_size.unwrap_or(0u32);
1310 args.insert("strip_size".to_string(), serde_json::to_value(strip_size)?);
1311 Ok(serde_json::from_value(
1312 self.tp.invoke("volume_raid_create", Some(args))?,
1313 )?)
1314 }
1315
1316 pub fn vol_ident_led_on(&mut self, vol: &Volume) -> Result<()> {
1320 let mut args = Map::new();
1321 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1322 self.tp.invoke("volume_ident_led_on", Some(args))?;
1323 Ok(())
1324 }
1325
1326 pub fn vol_ident_led_off(&mut self, vol: &Volume) -> Result<()> {
1330 let mut args = Map::new();
1331 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1332 self.tp.invoke("volume_ident_led_off", Some(args))?;
1333 Ok(())
1334 }
1335
1336 pub fn vol_cache_info(&mut self, vol: &Volume) -> Result<VolumeCacheInfo> {
1338 let mut args = Map::new();
1339 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1340 let ret: Vec<u8> =
1341 serde_json::from_value(self.tp.invoke("volume_cache_info", Some(args))?)?;
1342 if ret.len() != 5 {
1343 return Err(LsmError::PluginBug(format!(
1344 "vol_cache_info() is expecting 5 u8 from plugin, \
1345 but got '{:?}'",
1346 ret
1347 )));
1348 }
1349 Ok(VolumeCacheInfo {
1350 write_cache_setting: match ret[0] {
1351 WRITE_CACHE_POLICY_WRITE_BACK => CachePolicy::Enabled,
1352 WRITE_CACHE_POLICY_WRITE_THROUGH => CachePolicy::Disabled,
1353 WRITE_CACHE_POLICY_AUTO => CachePolicy::Auto,
1354 _ => CachePolicy::Unknown,
1355 },
1356 write_cache_status: match ret[1] {
1357 WRITE_CACHE_STATUS_WRITE_BACK => CachePolicy::Enabled,
1358 WRITE_CACHE_STATUS_WRITE_THROUGH => CachePolicy::Disabled,
1359 _ => CachePolicy::Unknown,
1360 },
1361 read_cache_setting: match ret[2] {
1362 READ_CACHE_POLICY_ENABLED => CachePolicy::Enabled,
1363 READ_CACHE_POLICY_DISABLED => CachePolicy::Disabled,
1364 _ => CachePolicy::Unknown,
1365 },
1366 read_cache_status: match ret[3] {
1367 READ_CACHE_STATUS_ENABLED => CachePolicy::Enabled,
1368 READ_CACHE_STATUS_DISABLED => CachePolicy::Disabled,
1369 _ => CachePolicy::Unknown,
1370 },
1371 physical_disk_cache_status: match ret[4] {
1372 PHYSICAL_DISK_CACHE_ENABLED => CachePolicy::Enabled,
1373 PHYSICAL_DISK_CACHE_DISABLED => CachePolicy::Disabled,
1374 PHYSICAL_DISK_CACHE_USE_DISK_SETTING => CachePolicy::UseDiskSetting,
1375 _ => CachePolicy::Unknown,
1376 },
1377 })
1378 }
1379
1380 pub fn vol_phy_disk_cache_set(&mut self, vol: &Volume, pdc: CachePolicy) -> Result<()> {
1382 let pdc: u8 = match pdc {
1383 CachePolicy::Enabled => PHYSICAL_DISK_CACHE_ENABLED,
1384 CachePolicy::Disabled => PHYSICAL_DISK_CACHE_DISABLED,
1385 CachePolicy::UseDiskSetting => PHYSICAL_DISK_CACHE_USE_DISK_SETTING,
1386 _ => {
1387 return Err(LsmError::InvalidArgument(format!(
1388 "Invalid pdc argument {:?}",
1389 pdc
1390 )))
1391 }
1392 };
1393 let mut args = Map::new();
1394 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1395 args.insert("pdc".to_string(), serde_json::to_value(pdc)?);
1396 self.tp
1397 .invoke("volume_physical_disk_cache_update", Some(args))?;
1398 Ok(())
1399 }
1400
1401 pub fn vol_write_cache_set(&mut self, vol: &Volume, wcp: CachePolicy) -> Result<()> {
1403 let wcp: u8 = match wcp {
1404 CachePolicy::Enabled => WRITE_CACHE_POLICY_WRITE_BACK,
1405 CachePolicy::Disabled => WRITE_CACHE_POLICY_WRITE_THROUGH,
1406 CachePolicy::Auto => WRITE_CACHE_POLICY_AUTO,
1407 _ => {
1408 return Err(LsmError::InvalidArgument(format!(
1409 "Invalid wcp argument {:?}",
1410 wcp
1411 )))
1412 }
1413 };
1414 let mut args = Map::new();
1415 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1416 args.insert("wcp".to_string(), serde_json::to_value(wcp)?);
1417 self.tp
1418 .invoke("volume_write_cache_policy_update", Some(args))?;
1419 Ok(())
1420 }
1421
1422 pub fn vol_read_cache_set(&mut self, vol: &Volume, rcp: CachePolicy) -> Result<()> {
1424 let rcp: u8 = match rcp {
1425 CachePolicy::Enabled => READ_CACHE_POLICY_ENABLED,
1426 CachePolicy::Disabled => READ_CACHE_POLICY_DISABLED,
1427 _ => {
1428 return Err(LsmError::InvalidArgument(format!(
1429 "Invalid rcp argument {:?}",
1430 rcp
1431 )))
1432 }
1433 };
1434 let mut args = Map::new();
1435 args.insert("volume".to_string(), serde_json::to_value(vol)?);
1436 args.insert("rcp".to_string(), serde_json::to_value(rcp)?);
1437 self.tp
1438 .invoke("volume_read_cache_policy_update", Some(args))?;
1439 Ok(())
1440 }
1441}