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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
use des::{to_bool, to_f64};

// See `src/mon/PGMap.h` in ceph's source
#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct PGMap {
    pub osd_stats_sum: OsdStatsSum,
    pub pg_stats_delta: PgStatsDelta,
    pub min_last_epoch_clean: Option<i32>,
    pub stamp: String,
    pub pg_stats_sum: PgStatsSum,
    pub last_pg_scan: i32,
    #[serde(deserialize_with = "to_f64")]
    pub full_ratio: f64,
    pub pool_stats: Vec<PoolStats>,
    pub version: i32,
    pub last_osdmap_epoch: i32,
    #[serde(deserialize_with = "to_f64")]
    pub near_full_ratio: f64,
    pub osd_stats: Vec<OsdStats>,
    pub pg_stats: Vec<PgStats>,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct OsdStats {
    pub snap_trim_queue_len: i32,
    pub kb: i32,
    pub fs_perf_stat: FsPerfStat,
    pub hb_in: Vec<i32>,
    pub num_snap_trimming: i32,
    pub hb_out: Vec<i32>,
    pub kb_avail: i32,
    pub kb_used: i32,
    pub op_queue_age_hist: OpQueueAgeHist,
    pub osd: i32,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct PgStatsDelta {
    pub acting: Option<i32>,
    pub log_size: i32,
    pub ondisk_log_size: i32,
    pub stat_sum: StatSum,
    pub up: Option<i32>,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct StatSum {
    pub num_evict: Option<i32>,
    pub num_evict_kb: Option<i32>,
    pub num_bytes_hit_set_archive: Option<i32>,
    pub num_whiteouts: i32,
    pub num_objects_pinned: Option<i32>,
    pub num_scrub_errors: i32,
    pub num_evict_mode_full: Option<i32>,
    pub num_read: i32,
    pub num_objects_recovered: i32,
    pub num_objects_omap: i32,
    pub num_objects_missing_on_primary: i32,
    pub num_write: i32,
    pub num_object_clones: i32,
    pub num_objects: i32,
    pub num_deep_scrub_errors: i32,
    pub num_shallow_scrub_errors: i32,
    pub num_read_kb: i32,
    pub num_objects_missing: Option<i32>,
    pub num_flush_kb: Option<i32>,
    pub num_flush_mode_high: Option<i32>,
    pub num_write_kb: i32,
    pub num_evict_mode_some: Option<i32>,
    pub num_objects_degraded: i32,
    pub num_flush: Option<i32>,
    pub num_objects_misplaced: Option<i32>,
    pub num_bytes_recovered: i32,
    pub num_objects_hit_set_archive: i32,
    pub num_keys_recovered: i32,
    pub num_flush_mode_low: Option<i32>,
    pub num_objects_unfound: i32,
    pub num_promote: Option<i32>,
    pub num_object_copies: i32,
    pub num_bytes: i32,
    pub num_objects_dirty: i32,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct PgStatsSum {
    pub acting: Option<i32>,
    pub log_size: i32,
    pub ondisk_log_size: i32,
    pub stat_sum: StatSum,
    pub up: Option<i32>,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct OsdStatsSum {
    pub snap_trim_queue_len: i32,
    pub kb: i32,
    pub fs_perf_stat: FsPerfStat,
    pub hb_in: Vec<i32>,
    pub num_snap_trimming: i32,
    pub hb_out: Vec<i32>,
    pub kb_avail: i32,
    pub kb_used: i32,
    pub op_queue_age_hist: OpQueueAgeHist,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct OpQueueAgeHist {
    pub upper_bound: i32,
    pub histogram: Vec<i32>,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct FsPerfStat {
    pub apply_latency_ms: i32,
    pub commit_latency_ms: i32,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct PoolStats {
    pub log_size: i32,
    pub ondisk_log_size: i32,
    pub up: Option<i32>,
    pub acting: Option<i32>,
    pub poolid: i32,
    pub stat_sum: StatSum,
}

#[derive(Deserialize, Debug, PartialEq, Clone)]
pub struct PgStats {
    pub last_scrub: String,
    pub last_clean_scrub_stamp: String,
    pub parent_split_bits: i32,
    pub last_active: String,
    pub pin_stats_invalid: Option<bool>,
    pub reported_epoch: String,
    pub log_start: String,
    pub log_size: i32,
    pub hitset_stats_invalid: Option<bool>,
    #[serde(deserialize_with = "to_bool")]
    pub stats_invalid: bool,
    pub acting_primary: i32,
    pub reported_seq: String,
    pub ondisk_log_size: i32,
    pub mapping_epoch: i32,
    pub dirty_stats_invalid: Option<bool>,
    pub state: String,
    pub version: String,
    pub last_became_peered: Option<String>,
    pub last_undegraded: Option<String>,
    pub pgid: String,
    pub parent: String,
    pub acting: Vec<i32>,
    pub up_primary: i32,
    pub last_fullsized: Option<String>,
    pub last_epoch_clean: i32,
    pub last_deep_scrub_stamp: String,
    pub stat_sum: StatSum,
    pub last_deep_scrub: String,
    pub last_fresh: String,
    pub last_scrub_stamp: String,
    pub created: i32,
    pub up: Vec<i32>,
    pub hitset_bytes_stats_invalid: Option<bool>,
    pub last_peered: Option<String>,
    pub last_became_active: String,
    pub omap_stats_invalid: Option<bool>,
    pub last_clean: String,
    pub last_unstale: String,
    pub last_change: String,
    pub blocked_by: Option<Vec<i32>>,
    pub ondisk_log_start: String,
}

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

    // Jewel tests
    #[test]
    #[should_panic]
    fn pgmap_from_jewel_file_panic() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_safe.json").unwrap();
        // An OSD is safe to remove so up should not be 0
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len(), 0);
    }

    #[test]
    fn pgmap_from_jewel_file() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_safe.json").unwrap();
        // First pg_stat.up should be length 3
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len() as i32, 3);
    }

    #[test]
    #[should_panic]
    fn pgmap_from_jewel_file_no_osd_panic() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_no_osd.json").unwrap();
        // First pg_stat.state should be "creating" since there are no OSDs
        assert_eq!(
            pgmap.pg_stats.first().unwrap().state,
            "active+clean".to_owned()
        );
    }

    #[test]
    fn pgmap_from_jewel_file_no_osd() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_no_osd.json").unwrap();
        // First pg_stat.up should be length 0
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len() as i32, 0);
    }

    #[test]
    fn pgmap_from_jewel_file_non_safe() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_non_safe.json").unwrap();
        // Up should be 2 as to not meet min_size + 1, as such acting should be 2
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len() as i32, 2);
        assert_eq!(pgmap.pg_stats.first().unwrap().acting.len() as i32, 2);
    }

    #[test]
    #[should_panic]
    fn pgmap_from_jewel_file_non_safe_panic() {
        let pgmap = PGMap::from_file("test/jewel/pg_dump_non_safe.json").unwrap();
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len() as i32, 3);
    }

    // Firefly tests
    #[test]
    fn pgmap_from_firefly_file() {
        let pgmap = PGMap::from_file("test/firefly/pg_dump_safe.json").unwrap();
        assert_eq!(pgmap.pg_stats.first().unwrap().up.len() as i32, 3);
    }

    #[test]
    #[should_panic]
    fn pgmap_from_firefly_file_panic() {
        let pgmap = PGMap::from_file("test/firefly/pg_dump_safe.json").unwrap();
        assert_eq!(pgmap.pg_stats.first().unwrap().acting.len() as i32, 0);
    }

    #[test]
    #[should_panic]
    fn pgmap_from_ceph_panic() {
        use from::FromCeph;
        let pgmap = PGMap::from_ceph("pg dump");
        assert_eq!(pgmap.is_ok(), true);
    }
}