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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::io;

use super::{lxc_output, Location};

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Snapshot {
    pub architecture: String,
    pub config: BTreeMap<String, String>,
    pub created_at: String,
    //pub devices: TODO,
    pub ephemeral: bool,
    pub expanded_config: BTreeMap<String, String>,
    pub expanded_devices: BTreeMap<String, BTreeMap<String, String>>,
    pub last_used_at: String,
    pub name: String,
    pub profiles: Vec<String>,
    pub stateful: bool,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct State {
    pub status: String,
    pub status_code: usize,
    //pub disk: TODO,
    pub memory: BTreeMap<String, usize>,
    //pub network: TODO,
    pub pid: usize,
    pub processes: usize,
    pub cpu: BTreeMap<String, usize>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
/// LXD container information
pub struct Info {
    pub architecture: String,
    pub config: BTreeMap<String, String>,
    pub devices: BTreeMap<String, BTreeMap<String, String>>,
    pub ephemeral: bool,
    pub profiles: Vec<String>,
    pub created_at: String,
    pub expanded_config: BTreeMap<String, String>,
    pub expanded_devices: BTreeMap<String, BTreeMap<String, String>>,
    pub name: String,
    pub stateful: bool,
    pub status: String,
    pub status_code: usize,
    pub last_used_at: String,
    pub state: Option<State>,
    pub snapshots: Option<Vec<Snapshot>>,
}

impl Info {
    /// Retrieve LXD container information from all containers
    ///
    /// # Arguments
    ///
    /// * `location` - The location of the host
    ///
    /// # Return
    ///
    /// The LXD container information
    ///
    /// # Errors
    ///
    /// Errors that are encountered while retrieving info will be returned
    ///
    /// # Example
    ///
    /// ```
    /// use lxd::{Info, Location};
    ///
    /// let info = Info::all(Location::Local).unwrap();
    /// ```
    pub fn all(location: Location) -> io::Result<Vec<Self>> {
        let json = match location {
            Location::Local => lxc_output(&["list", "--format", "json"])?,
            Location::Remote(remote) => lxc_output(&["list", &format!("{}:", remote), "--format", "json"])?
        };

        serde_json::from_slice::<Vec<Self>>(&json).map_err(|err| {
            io::Error::new(
                io::ErrorKind::Other,
                format!("LXD info: failed to parse json: {}", err)
            )
        })
    }

    /// Retrieve LXD container information from one container
    ///
    /// # Arguments
    ///
    /// * `location` - The location of the host
    /// * `name` - The name of the container
    ///
    /// # Return
    ///
    /// The LXD container information
    ///
    /// # Errors
    ///
    /// Errors that are encountered while retrieving info will be returned
    ///
    /// # Example
    ///
    /// ```
    /// use lxd::{Container, Info, Location};
    ///
    /// let mut container = Container::new(Location::Local, "test-info", "ubuntu:16.04").unwrap();
    /// let info = Info::new(Location::Local, "test-info").unwrap();
    /// ```
    pub fn new(location: Location, name: &str) -> io::Result<Self> {
        let json = match location {
            Location::Local => lxc_output(&["list", &format!("{}$", name), "--format", "json"])?,
            Location::Remote(remote) => lxc_output(&["list", &format!("{}:", remote), &format!("{}$", name), "--format", "json"])?
        };

        match serde_json::from_slice::<Vec<Self>>(&json) {
            Ok(mut list) => if list.len() == 1 {
                Ok(list.remove(0))
            } else {
                Err(io::Error::new(
                    io::ErrorKind::NotFound,
                    format!("LXD info: {} not found", name)
                ))
            },
            Err(err) => {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("LXD info: failed to parse json: {}", err)
                ))
            }
        }
    }
}