1use std::{collections::HashMap, time::SystemTime};
16
17use serde::{Deserialize, Serialize};
18use time::OffsetDateTime;
19
20use crate::metrics::TimedAction;
21
22#[derive(Debug, PartialEq, Clone, Copy)]
23pub enum ItemState {
24 Offline,
25 Initializing,
26 Online,
27}
28
29impl ItemState {
30 pub fn to_string(&self) -> &str {
31 match self {
32 ItemState::Offline => "offline",
33 ItemState::Initializing => "initializing",
34 ItemState::Online => "online",
35 }
36 }
37
38 pub fn from_string(s: &str) -> Option<ItemState> {
39 match s {
40 "offline" => Some(ItemState::Offline),
41 "initializing" => Some(ItemState::Initializing),
42 "online" => Some(ItemState::Online),
43 _ => None,
44 }
45 }
46}
47
48#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
49pub struct DiskMetrics {
50 pub last_minute: HashMap<String, TimedAction>,
51 pub api_calls: HashMap<String, u64>,
52 pub total_waiting: u32,
53 pub total_errors_availability: u64,
54 pub total_errors_timeout: u64,
55 pub total_writes: u64,
56 pub total_deletes: u64,
57}
58
59#[derive(Serialize, Deserialize, Debug, Default, Clone)]
60pub struct Disk {
61 pub endpoint: String,
62 #[serde(rename = "rootDisk")]
63 pub root_disk: bool,
64 #[serde(rename = "path")]
65 pub drive_path: String,
66 pub healing: bool,
67 pub scanning: bool,
68 pub state: String,
69 pub uuid: String,
70 pub major: u32,
71 pub minor: u32,
72 pub model: Option<String>,
73 #[serde(rename = "totalspace")]
74 pub total_space: u64,
75 #[serde(rename = "usedspace")]
76 pub used_space: u64,
77 #[serde(rename = "availspace")]
78 pub available_space: u64,
79 #[serde(rename = "readthroughput")]
80 pub read_throughput: f64,
81 #[serde(rename = "writethroughput")]
82 pub write_throughput: f64,
83 #[serde(rename = "readlatency")]
84 pub read_latency: f64,
85 #[serde(rename = "writelatency")]
86 pub write_latency: f64,
87 pub utilization: f64,
88 pub metrics: Option<DiskMetrics>,
89 pub heal_info: Option<HealingDisk>,
90 pub used_inodes: u64,
91 pub free_inodes: u64,
92 pub local: bool,
93 pub pool_index: i32,
94 pub set_index: i32,
95 pub disk_index: i32,
96}
97
98#[derive(Clone, Debug, Default, Serialize, Deserialize)]
99pub struct HealingDisk {
100 pub id: String,
101 pub heal_id: String,
102 pub pool_index: Option<usize>,
103 pub set_index: Option<usize>,
104 pub disk_index: Option<usize>,
105 pub endpoint: String,
106 pub path: String,
107 pub started: Option<OffsetDateTime>,
108 pub last_update: Option<SystemTime>,
109 pub retry_attempts: u64,
110 pub objects_total_count: u64,
111 pub objects_total_size: u64,
112 pub items_healed: u64,
113 pub items_failed: u64,
114 pub item_skipped: u64,
115 pub bytes_done: u64,
116 pub bytes_failed: u64,
117 pub bytes_skipped: u64,
118 pub objects_healed: u64,
119 pub objects_failed: u64,
120 pub bucket: String,
121 pub object: String,
122 pub queue_buckets: Vec<String>,
123 pub healed_buckets: Vec<String>,
124 pub finished: bool,
125}
126
127#[derive(Debug, Default, Serialize, Deserialize)]
128pub enum BackendByte {
129 #[default]
130 Unknown,
131 FS,
132 Erasure,
133}
134
135#[derive(Debug, Default, Serialize, Deserialize)]
136pub struct StorageInfo {
137 pub disks: Vec<Disk>,
138 pub backend: BackendInfo,
139}
140
141#[derive(Debug, Default, Serialize, Deserialize)]
142pub struct BackendDisks(pub HashMap<String, usize>);
143
144impl BackendDisks {
145 pub fn new() -> Self {
146 Self(HashMap::new())
147 }
148 pub fn sum(&self) -> usize {
149 self.0.values().sum()
150 }
151}
152
153#[derive(Debug, Default, Serialize, Deserialize)]
154#[serde(rename_all = "PascalCase", default)]
155pub struct BackendInfo {
156 pub backend_type: BackendByte,
157 pub online_disks: BackendDisks,
158 pub offline_disks: BackendDisks,
159 #[serde(rename = "StandardSCData")]
160 pub standard_sc_data: Vec<usize>,
161 #[serde(rename = "StandardSCParities")]
162 pub standard_sc_parities: Vec<usize>,
163 #[serde(rename = "StandardSCParity")]
164 pub standard_sc_parity: Option<usize>,
165 #[serde(rename = "RRSCData")]
166 pub rr_sc_data: Vec<usize>,
167 #[serde(rename = "RRSCParities")]
168 pub rr_sc_parities: Vec<usize>,
169 #[serde(rename = "RRSCParity")]
170 pub rr_sc_parity: Option<usize>,
171 pub total_sets: Vec<usize>,
172 pub drives_per_set: Vec<usize>,
173}
174
175pub const ITEM_OFFLINE: &str = "offline";
176pub const ITEM_INITIALIZING: &str = "initializing";
177pub const ITEM_ONLINE: &str = "online";
178
179#[derive(Debug, Default, Serialize, Deserialize)]
180pub struct MemStats {
181 pub alloc: u64,
182 pub total_alloc: u64,
183 pub mallocs: u64,
184 pub frees: u64,
185 pub heap_alloc: u64,
186}
187
188#[derive(Debug, Default, Serialize, Deserialize)]
189pub struct ServerProperties {
190 pub state: String,
191 pub endpoint: String,
192 pub scheme: String,
193 pub uptime: u64,
194 pub version: String,
195 #[serde(rename = "commitID")]
196 pub commit_id: String,
197 pub network: HashMap<String, String>,
198 #[serde(rename = "drives")]
199 pub disks: Vec<Disk>,
200 #[serde(rename = "poolNumber")]
201 pub pool_number: i32,
202 #[serde(rename = "poolNumbers")]
203 pub pool_numbers: Vec<i32>,
204 pub mem_stats: MemStats,
205 pub max_procs: u64,
206 pub num_cpu: u64,
207 pub runtime_version: String,
208 pub rustfs_env_vars: HashMap<String, String>,
209}
210
211#[derive(Serialize, Deserialize, Debug, Default)]
212pub struct Kms {
213 pub status: Option<String>,
214 pub encrypt: Option<String>,
215 pub decrypt: Option<String>,
216 pub endpoint: Option<String>,
217 pub version: Option<String>,
218}
219
220#[derive(Serialize, Deserialize, Debug, Default)]
221pub struct Ldap {
222 pub status: Option<String>,
223}
224
225#[derive(Serialize, Deserialize, Debug, Default)]
226pub struct Status {
227 pub status: Option<String>,
228}
229
230pub type Audit = HashMap<String, Status>;
231
232pub type Logger = HashMap<String, Status>;
233
234pub type TargetIDStatus = HashMap<String, Status>;
235
236#[derive(Serialize, Deserialize, Default, Debug)]
237pub struct Services {
238 pub kms: Option<Kms>, #[serde(rename = "kmsStatus")]
240 pub kms_status: Option<Vec<Kms>>,
241 pub ldap: Option<Ldap>,
242 pub logger: Option<Vec<Logger>>,
243 pub audit: Option<Vec<Audit>>,
244 pub notifications: Option<Vec<HashMap<String, Vec<TargetIDStatus>>>>,
245}
246
247#[derive(Serialize, Deserialize, Debug, Default)]
248pub struct Buckets {
249 pub count: u64,
250 pub error: Option<String>,
251}
252
253#[derive(Serialize, Deserialize, Debug, Default)]
254pub struct Objects {
255 pub count: u64,
256 pub error: Option<String>,
257}
258
259#[derive(Serialize, Deserialize, Debug, Default)]
260pub struct Versions {
261 pub count: u64,
262 pub error: Option<String>,
263}
264
265#[derive(Serialize, Deserialize, Debug, Default)]
266pub struct DeleteMarkers {
267 pub count: u64,
268 pub error: Option<String>,
269}
270
271#[derive(Serialize, Deserialize, Debug, Default)]
272pub struct Usage {
273 pub size: u64,
274 pub error: Option<String>,
275}
276
277#[derive(Serialize, Deserialize, Debug, Default)]
278pub struct ErasureSetInfo {
279 pub id: i32,
280 #[serde(rename = "rawUsage")]
281 pub raw_usage: u64,
282 #[serde(rename = "rawCapacity")]
283 pub raw_capacity: u64,
284 pub usage: u64,
285 #[serde(rename = "objectsCount")]
286 pub objects_count: u64,
287 #[serde(rename = "versionsCount")]
288 pub versions_count: u64,
289 #[serde(rename = "deleteMarkersCount")]
290 pub delete_markers_count: u64,
291 #[serde(rename = "healDisks")]
292 pub heal_disks: i32,
293}
294
295#[derive(Serialize, Deserialize, Debug, Default)]
296pub enum BackendType {
297 #[default]
298 #[serde(rename = "FS")]
299 FsType,
300 #[serde(rename = "Erasure")]
301 ErasureType,
302}
303
304#[derive(Serialize, Deserialize)]
305pub struct FSBackend {
306 #[serde(rename = "backendType")]
307 pub backend_type: BackendType,
308}
309
310#[derive(Serialize, Deserialize, Debug, Default)]
311pub struct ErasureBackend {
312 #[serde(rename = "backendType")]
313 pub backend_type: BackendType,
314 #[serde(rename = "onlineDisks")]
315 pub online_disks: usize,
316 #[serde(rename = "offlineDisks")]
317 pub offline_disks: usize,
318 #[serde(rename = "standardSCParity")]
319 pub standard_sc_parity: Option<usize>,
320 #[serde(rename = "rrSCParity")]
321 pub rr_sc_parity: Option<usize>,
322 #[serde(rename = "totalSets")]
323 pub total_sets: Vec<usize>,
324 #[serde(rename = "totalDrivesPerSet")]
325 pub drives_per_set: Vec<usize>,
326}
327
328#[derive(Serialize, Deserialize)]
329pub struct InfoMessage {
330 pub mode: Option<String>,
331 pub domain: Option<Vec<String>>,
332 pub region: Option<String>,
333 #[serde(rename = "sqsARN")]
334 pub sqs_arn: Option<Vec<String>>,
335 #[serde(rename = "deploymentID")]
336 pub deployment_id: Option<String>,
337 pub buckets: Option<Buckets>,
338 pub objects: Option<Objects>,
339 pub versions: Option<Versions>,
340 #[serde(rename = "deletemarkers")]
341 pub delete_markers: Option<DeleteMarkers>,
342 pub usage: Option<Usage>,
343 pub services: Option<Services>,
344 pub backend: Option<ErasureBackend>,
345 pub servers: Option<Vec<ServerProperties>>,
346 pub pools: Option<std::collections::HashMap<i32, std::collections::HashMap<i32, ErasureSetInfo>>>,
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use serde_json;
353 use std::collections::HashMap;
354 use time::OffsetDateTime;
355
356 #[test]
357 fn test_item_state_to_string() {
358 assert_eq!(ItemState::Offline.to_string(), ITEM_OFFLINE);
359 assert_eq!(ItemState::Initializing.to_string(), ITEM_INITIALIZING);
360 assert_eq!(ItemState::Online.to_string(), ITEM_ONLINE);
361 }
362
363 #[test]
364 fn test_item_state_from_string_valid() {
365 assert_eq!(ItemState::from_string(ITEM_OFFLINE), Some(ItemState::Offline));
366 assert_eq!(ItemState::from_string(ITEM_INITIALIZING), Some(ItemState::Initializing));
367 assert_eq!(ItemState::from_string(ITEM_ONLINE), Some(ItemState::Online));
368 }
369
370 #[test]
371 fn test_item_state_from_string_invalid() {
372 assert_eq!(ItemState::from_string("invalid"), None);
373 assert_eq!(ItemState::from_string(""), None);
374 assert_eq!(ItemState::from_string("OFFLINE"), None); }
376
377 #[test]
378 fn test_disk_metrics_default() {
379 let metrics = DiskMetrics::default();
380 assert!(metrics.last_minute.is_empty());
381 assert!(metrics.api_calls.is_empty());
382 assert_eq!(metrics.total_waiting, 0);
383 assert_eq!(metrics.total_errors_availability, 0);
384 assert_eq!(metrics.total_errors_timeout, 0);
385 assert_eq!(metrics.total_writes, 0);
386 assert_eq!(metrics.total_deletes, 0);
387 }
388
389 #[test]
390 fn test_disk_metrics_with_values() {
391 let mut last_minute = HashMap::new();
392 last_minute.insert("read".to_string(), TimedAction::default());
393
394 let mut api_calls = HashMap::new();
395 api_calls.insert("GET".to_string(), 100);
396 api_calls.insert("PUT".to_string(), 50);
397
398 let metrics = DiskMetrics {
399 last_minute,
400 api_calls,
401 total_waiting: 5,
402 total_errors_availability: 2,
403 total_errors_timeout: 1,
404 total_writes: 1000,
405 total_deletes: 50,
406 };
407
408 assert_eq!(metrics.last_minute.len(), 1);
409 assert_eq!(metrics.api_calls.len(), 2);
410 assert_eq!(metrics.total_waiting, 5);
411 assert_eq!(metrics.total_writes, 1000);
412 assert_eq!(metrics.total_deletes, 50);
413 }
414
415 #[test]
416 fn test_disk_default() {
417 let disk = Disk::default();
418 assert!(disk.endpoint.is_empty());
419 assert!(!disk.root_disk);
420 assert!(disk.drive_path.is_empty());
421 assert!(!disk.healing);
422 assert!(!disk.scanning);
423 assert!(disk.state.is_empty());
424 assert!(disk.uuid.is_empty());
425 assert_eq!(disk.major, 0);
426 assert_eq!(disk.minor, 0);
427 assert!(disk.model.is_none());
428 assert_eq!(disk.total_space, 0);
429 assert_eq!(disk.used_space, 0);
430 assert_eq!(disk.available_space, 0);
431 assert_eq!(disk.read_throughput, 0.0);
432 assert_eq!(disk.write_throughput, 0.0);
433 assert_eq!(disk.read_latency, 0.0);
434 assert_eq!(disk.write_latency, 0.0);
435 assert_eq!(disk.utilization, 0.0);
436 assert!(disk.metrics.is_none());
437 assert!(disk.heal_info.is_none());
438 assert_eq!(disk.used_inodes, 0);
439 assert_eq!(disk.free_inodes, 0);
440 assert!(!disk.local);
441 assert_eq!(disk.pool_index, 0);
442 assert_eq!(disk.set_index, 0);
443 assert_eq!(disk.disk_index, 0);
444 }
445
446 #[test]
447 fn test_disk_with_values() {
448 let disk = Disk {
449 endpoint: "http://localhost:9000".to_string(),
450 root_disk: true,
451 drive_path: "/data/disk1".to_string(),
452 healing: false,
453 scanning: true,
454 state: "online".to_string(),
455 uuid: "12345678-1234-1234-1234-123456789abc".to_string(),
456 major: 8,
457 minor: 1,
458 model: Some("Samsung SSD 980".to_string()),
459 total_space: 1000000000000,
460 used_space: 500000000000,
461 available_space: 500000000000,
462 read_throughput: 100.5,
463 write_throughput: 80.3,
464 read_latency: 5.2,
465 write_latency: 7.8,
466 utilization: 50.0,
467 metrics: Some(DiskMetrics::default()),
468 heal_info: None,
469 used_inodes: 1000000,
470 free_inodes: 9000000,
471 local: true,
472 pool_index: 0,
473 set_index: 1,
474 disk_index: 2,
475 };
476
477 assert_eq!(disk.endpoint, "http://localhost:9000");
478 assert!(disk.root_disk);
479 assert_eq!(disk.drive_path, "/data/disk1");
480 assert!(disk.scanning);
481 assert_eq!(disk.state, "online");
482 assert_eq!(disk.major, 8);
483 assert_eq!(disk.minor, 1);
484 assert_eq!(disk.model.unwrap(), "Samsung SSD 980");
485 assert_eq!(disk.total_space, 1000000000000);
486 assert_eq!(disk.utilization, 50.0);
487 assert!(disk.metrics.is_some());
488 assert!(disk.local);
489 }
490
491 #[test]
492 fn test_healing_disk_default() {
493 let healing_disk = HealingDisk::default();
494 assert!(healing_disk.id.is_empty());
495 assert!(healing_disk.heal_id.is_empty());
496 assert!(healing_disk.pool_index.is_none());
497 assert!(healing_disk.set_index.is_none());
498 assert!(healing_disk.disk_index.is_none());
499 assert!(healing_disk.endpoint.is_empty());
500 assert!(healing_disk.path.is_empty());
501 assert!(healing_disk.started.is_none());
502 assert!(healing_disk.last_update.is_none());
503 assert_eq!(healing_disk.retry_attempts, 0);
504 assert_eq!(healing_disk.objects_total_count, 0);
505 assert_eq!(healing_disk.objects_total_size, 0);
506 assert_eq!(healing_disk.items_healed, 0);
507 assert_eq!(healing_disk.items_failed, 0);
508 assert_eq!(healing_disk.item_skipped, 0);
509 assert_eq!(healing_disk.bytes_done, 0);
510 assert_eq!(healing_disk.bytes_failed, 0);
511 assert_eq!(healing_disk.bytes_skipped, 0);
512 assert_eq!(healing_disk.objects_healed, 0);
513 assert_eq!(healing_disk.objects_failed, 0);
514 assert!(healing_disk.bucket.is_empty());
515 assert!(healing_disk.object.is_empty());
516 assert!(healing_disk.queue_buckets.is_empty());
517 assert!(healing_disk.healed_buckets.is_empty());
518 assert!(!healing_disk.finished);
519 }
520
521 #[test]
522 fn test_healing_disk_with_values() {
523 let now = OffsetDateTime::now_utc();
524 let system_time = std::time::SystemTime::now();
525
526 let healing_disk = HealingDisk {
527 id: "heal-001".to_string(),
528 heal_id: "heal-session-123".to_string(),
529 pool_index: Some(0),
530 set_index: Some(1),
531 disk_index: Some(2),
532 endpoint: "http://node1:9000".to_string(),
533 path: "/data/disk1".to_string(),
534 started: Some(now),
535 last_update: Some(system_time),
536 retry_attempts: 3,
537 objects_total_count: 10000,
538 objects_total_size: 1000000000,
539 items_healed: 8000,
540 items_failed: 100,
541 item_skipped: 50,
542 bytes_done: 800000000,
543 bytes_failed: 10000000,
544 bytes_skipped: 5000000,
545 objects_healed: 7900,
546 objects_failed: 100,
547 bucket: "test-bucket".to_string(),
548 object: "test-object".to_string(),
549 queue_buckets: vec!["bucket1".to_string(), "bucket2".to_string()],
550 healed_buckets: vec!["bucket3".to_string()],
551 finished: false,
552 };
553
554 assert_eq!(healing_disk.id, "heal-001");
555 assert_eq!(healing_disk.heal_id, "heal-session-123");
556 assert_eq!(healing_disk.pool_index.unwrap(), 0);
557 assert_eq!(healing_disk.set_index.unwrap(), 1);
558 assert_eq!(healing_disk.disk_index.unwrap(), 2);
559 assert_eq!(healing_disk.retry_attempts, 3);
560 assert_eq!(healing_disk.objects_total_count, 10000);
561 assert_eq!(healing_disk.items_healed, 8000);
562 assert_eq!(healing_disk.queue_buckets.len(), 2);
563 assert_eq!(healing_disk.healed_buckets.len(), 1);
564 assert!(!healing_disk.finished);
565 }
566
567 #[test]
568 fn test_backend_byte_default() {
569 let backend = BackendByte::default();
570 assert!(matches!(backend, BackendByte::Unknown));
571 }
572
573 #[test]
574 fn test_backend_byte_variants() {
575 let unknown = BackendByte::Unknown;
576 let fs = BackendByte::FS;
577 let erasure = BackendByte::Erasure;
578
579 assert!(matches!(unknown, BackendByte::Unknown));
581 assert!(matches!(fs, BackendByte::FS));
582 assert!(matches!(erasure, BackendByte::Erasure));
583 }
584
585 #[test]
586 fn test_storage_info_creation() {
587 let storage_info = StorageInfo {
588 disks: vec![
589 Disk {
590 endpoint: "node1:9000".to_string(),
591 state: "online".to_string(),
592 ..Default::default()
593 },
594 Disk {
595 endpoint: "node2:9000".to_string(),
596 state: "offline".to_string(),
597 ..Default::default()
598 },
599 ],
600 backend: BackendInfo::default(),
601 };
602
603 assert_eq!(storage_info.disks.len(), 2);
604 assert_eq!(storage_info.disks[0].endpoint, "node1:9000");
605 assert_eq!(storage_info.disks[1].state, "offline");
606 }
607
608 #[test]
609 fn test_backend_disks_new() {
610 let backend_disks = BackendDisks::new();
611 assert!(backend_disks.0.is_empty());
612 }
613
614 #[test]
615 fn test_backend_disks_sum() {
616 let mut backend_disks = BackendDisks::new();
617 backend_disks.0.insert("pool1".to_string(), 4);
618 backend_disks.0.insert("pool2".to_string(), 6);
619 backend_disks.0.insert("pool3".to_string(), 2);
620
621 assert_eq!(backend_disks.sum(), 12);
622 }
623
624 #[test]
625 fn test_backend_disks_sum_empty() {
626 let backend_disks = BackendDisks::new();
627 assert_eq!(backend_disks.sum(), 0);
628 }
629
630 #[test]
631 fn test_backend_info_default() {
632 let backend_info = BackendInfo::default();
633 assert!(matches!(backend_info.backend_type, BackendByte::Unknown));
634 assert_eq!(backend_info.online_disks.sum(), 0);
635 assert_eq!(backend_info.offline_disks.sum(), 0);
636 assert!(backend_info.standard_sc_data.is_empty());
637 assert!(backend_info.standard_sc_parities.is_empty());
638 assert!(backend_info.standard_sc_parity.is_none());
639 assert!(backend_info.rr_sc_data.is_empty());
640 assert!(backend_info.rr_sc_parities.is_empty());
641 assert!(backend_info.rr_sc_parity.is_none());
642 assert!(backend_info.total_sets.is_empty());
643 assert!(backend_info.drives_per_set.is_empty());
644 }
645
646 #[test]
647 fn test_backend_info_with_values() {
648 let mut online_disks = BackendDisks::new();
649 online_disks.0.insert("set1".to_string(), 4);
650 online_disks.0.insert("set2".to_string(), 4);
651
652 let mut offline_disks = BackendDisks::new();
653 offline_disks.0.insert("set1".to_string(), 0);
654 offline_disks.0.insert("set2".to_string(), 1);
655
656 let backend_info = BackendInfo {
657 backend_type: BackendByte::Erasure,
658 online_disks,
659 offline_disks,
660 standard_sc_data: vec![4, 4],
661 standard_sc_parities: vec![2, 2],
662 standard_sc_parity: Some(2),
663 rr_sc_data: vec![2, 2],
664 rr_sc_parities: vec![1, 1],
665 rr_sc_parity: Some(1),
666 total_sets: vec![2],
667 drives_per_set: vec![6, 6],
668 };
669
670 assert!(matches!(backend_info.backend_type, BackendByte::Erasure));
671 assert_eq!(backend_info.online_disks.sum(), 8);
672 assert_eq!(backend_info.offline_disks.sum(), 1);
673 assert_eq!(backend_info.standard_sc_data.len(), 2);
674 assert_eq!(backend_info.standard_sc_parity.unwrap(), 2);
675 assert_eq!(backend_info.total_sets.len(), 1);
676 assert_eq!(backend_info.drives_per_set.len(), 2);
677 }
678
679 #[test]
680 fn test_mem_stats_default() {
681 let mem_stats = MemStats::default();
682 assert_eq!(mem_stats.alloc, 0);
683 assert_eq!(mem_stats.total_alloc, 0);
684 assert_eq!(mem_stats.mallocs, 0);
685 assert_eq!(mem_stats.frees, 0);
686 assert_eq!(mem_stats.heap_alloc, 0);
687 }
688
689 #[test]
690 fn test_mem_stats_with_values() {
691 let mem_stats = MemStats {
692 alloc: 1024000,
693 total_alloc: 5120000,
694 mallocs: 1000,
695 frees: 800,
696 heap_alloc: 2048000,
697 };
698
699 assert_eq!(mem_stats.alloc, 1024000);
700 assert_eq!(mem_stats.total_alloc, 5120000);
701 assert_eq!(mem_stats.mallocs, 1000);
702 assert_eq!(mem_stats.frees, 800);
703 assert_eq!(mem_stats.heap_alloc, 2048000);
704 }
705
706 #[test]
707 fn test_server_properties_default() {
708 let server_props = ServerProperties::default();
709 assert!(server_props.state.is_empty());
710 assert!(server_props.endpoint.is_empty());
711 assert!(server_props.scheme.is_empty());
712 assert_eq!(server_props.uptime, 0);
713 assert!(server_props.version.is_empty());
714 assert!(server_props.commit_id.is_empty());
715 assert!(server_props.network.is_empty());
716 assert!(server_props.disks.is_empty());
717 assert_eq!(server_props.pool_number, 0);
718 assert!(server_props.pool_numbers.is_empty());
719 assert_eq!(server_props.mem_stats.alloc, 0);
720 assert_eq!(server_props.max_procs, 0);
721 assert_eq!(server_props.num_cpu, 0);
722 assert!(server_props.runtime_version.is_empty());
723 assert!(server_props.rustfs_env_vars.is_empty());
724 }
725
726 #[test]
727 fn test_server_properties_with_values() {
728 let mut network = HashMap::new();
729 network.insert("interface".to_string(), "eth0".to_string());
730 network.insert("ip".to_string(), "192.168.1.100".to_string());
731
732 let mut env_vars = HashMap::new();
733 env_vars.insert("RUSTFS_ROOT_USER".to_string(), "admin".to_string());
734 env_vars.insert("RUSTFS_ROOT_PASSWORD".to_string(), "password".to_string());
735
736 let server_props = ServerProperties {
737 state: "online".to_string(),
738 endpoint: "http://localhost:9000".to_string(),
739 scheme: "http".to_string(),
740 uptime: 3600,
741 version: "1.0.0".to_string(),
742 commit_id: "abc123def456".to_string(),
743 network,
744 disks: vec![Disk::default()],
745 pool_number: 1,
746 pool_numbers: vec![0, 1],
747 mem_stats: MemStats {
748 alloc: 1024000,
749 total_alloc: 5120000,
750 mallocs: 1000,
751 frees: 800,
752 heap_alloc: 2048000,
753 },
754 max_procs: 8,
755 num_cpu: 4,
756 runtime_version: "1.70.0".to_string(),
757 rustfs_env_vars: env_vars,
758 };
759
760 assert_eq!(server_props.state, "online");
761 assert_eq!(server_props.endpoint, "http://localhost:9000");
762 assert_eq!(server_props.uptime, 3600);
763 assert_eq!(server_props.version, "1.0.0");
764 assert_eq!(server_props.network.len(), 2);
765 assert_eq!(server_props.disks.len(), 1);
766 assert_eq!(server_props.pool_number, 1);
767 assert_eq!(server_props.pool_numbers.len(), 2);
768 assert_eq!(server_props.mem_stats.alloc, 1024000);
769 assert_eq!(server_props.max_procs, 8);
770 assert_eq!(server_props.num_cpu, 4);
771 assert_eq!(server_props.rustfs_env_vars.len(), 2);
772 }
773
774 #[test]
775 fn test_kms_default() {
776 let kms = Kms::default();
777 assert!(kms.status.is_none());
778 assert!(kms.encrypt.is_none());
779 assert!(kms.decrypt.is_none());
780 assert!(kms.endpoint.is_none());
781 assert!(kms.version.is_none());
782 }
783
784 #[test]
785 fn test_kms_with_values() {
786 let kms = Kms {
787 status: Some("enabled".to_string()),
788 encrypt: Some("AES256".to_string()),
789 decrypt: Some("AES256".to_string()),
790 endpoint: Some("https://kms.example.com".to_string()),
791 version: Some("1.0".to_string()),
792 };
793
794 assert_eq!(kms.status.unwrap(), "enabled");
795 assert_eq!(kms.encrypt.unwrap(), "AES256");
796 assert_eq!(kms.decrypt.unwrap(), "AES256");
797 assert_eq!(kms.endpoint.unwrap(), "https://kms.example.com");
798 assert_eq!(kms.version.unwrap(), "1.0");
799 }
800
801 #[test]
802 fn test_ldap_default() {
803 let ldap = Ldap::default();
804 assert!(ldap.status.is_none());
805 }
806
807 #[test]
808 fn test_ldap_with_values() {
809 let ldap = Ldap {
810 status: Some("enabled".to_string()),
811 };
812
813 assert_eq!(ldap.status.unwrap(), "enabled");
814 }
815
816 #[test]
817 fn test_status_default() {
818 let status = Status::default();
819 assert!(status.status.is_none());
820 }
821
822 #[test]
823 fn test_status_with_values() {
824 let status = Status {
825 status: Some("active".to_string()),
826 };
827
828 assert_eq!(status.status.unwrap(), "active");
829 }
830
831 #[test]
832 fn test_services_default() {
833 let services = Services::default();
834 assert!(services.kms.is_none());
835 assert!(services.kms_status.is_none());
836 assert!(services.ldap.is_none());
837 assert!(services.logger.is_none());
838 assert!(services.audit.is_none());
839 assert!(services.notifications.is_none());
840 }
841
842 #[test]
843 fn test_services_with_values() {
844 let services = Services {
845 kms: Some(Kms::default()),
846 kms_status: Some(vec![Kms::default()]),
847 ldap: Some(Ldap::default()),
848 logger: Some(vec![HashMap::new()]),
849 audit: Some(vec![HashMap::new()]),
850 notifications: Some(vec![HashMap::new()]),
851 };
852
853 assert!(services.kms.is_some());
854 assert_eq!(services.kms_status.unwrap().len(), 1);
855 assert!(services.ldap.is_some());
856 assert_eq!(services.logger.unwrap().len(), 1);
857 assert_eq!(services.audit.unwrap().len(), 1);
858 assert_eq!(services.notifications.unwrap().len(), 1);
859 }
860
861 #[test]
862 fn test_buckets_default() {
863 let buckets = Buckets::default();
864 assert_eq!(buckets.count, 0);
865 assert!(buckets.error.is_none());
866 }
867
868 #[test]
869 fn test_buckets_with_values() {
870 let buckets = Buckets {
871 count: 10,
872 error: Some("Access denied".to_string()),
873 };
874
875 assert_eq!(buckets.count, 10);
876 assert_eq!(buckets.error.unwrap(), "Access denied");
877 }
878
879 #[test]
880 fn test_objects_default() {
881 let objects = Objects::default();
882 assert_eq!(objects.count, 0);
883 assert!(objects.error.is_none());
884 }
885
886 #[test]
887 fn test_versions_default() {
888 let versions = Versions::default();
889 assert_eq!(versions.count, 0);
890 assert!(versions.error.is_none());
891 }
892
893 #[test]
894 fn test_delete_markers_default() {
895 let delete_markers = DeleteMarkers::default();
896 assert_eq!(delete_markers.count, 0);
897 assert!(delete_markers.error.is_none());
898 }
899
900 #[test]
901 fn test_usage_default() {
902 let usage = Usage::default();
903 assert_eq!(usage.size, 0);
904 assert!(usage.error.is_none());
905 }
906
907 #[test]
908 fn test_erasure_set_info_default() {
909 let erasure_set = ErasureSetInfo::default();
910 assert_eq!(erasure_set.id, 0);
911 assert_eq!(erasure_set.raw_usage, 0);
912 assert_eq!(erasure_set.raw_capacity, 0);
913 assert_eq!(erasure_set.usage, 0);
914 assert_eq!(erasure_set.objects_count, 0);
915 assert_eq!(erasure_set.versions_count, 0);
916 assert_eq!(erasure_set.delete_markers_count, 0);
917 assert_eq!(erasure_set.heal_disks, 0);
918 }
919
920 #[test]
921 fn test_erasure_set_info_with_values() {
922 let erasure_set = ErasureSetInfo {
923 id: 1,
924 raw_usage: 1000000000,
925 raw_capacity: 2000000000,
926 usage: 800000000,
927 objects_count: 10000,
928 versions_count: 15000,
929 delete_markers_count: 500,
930 heal_disks: 2,
931 };
932
933 assert_eq!(erasure_set.id, 1);
934 assert_eq!(erasure_set.raw_usage, 1000000000);
935 assert_eq!(erasure_set.raw_capacity, 2000000000);
936 assert_eq!(erasure_set.usage, 800000000);
937 assert_eq!(erasure_set.objects_count, 10000);
938 assert_eq!(erasure_set.versions_count, 15000);
939 assert_eq!(erasure_set.delete_markers_count, 500);
940 assert_eq!(erasure_set.heal_disks, 2);
941 }
942
943 #[test]
944 fn test_backend_type_default() {
945 let backend_type = BackendType::default();
946 assert!(matches!(backend_type, BackendType::FsType));
947 }
948
949 #[test]
950 fn test_backend_type_variants() {
951 let fs_type = BackendType::FsType;
952 let erasure_type = BackendType::ErasureType;
953
954 assert!(matches!(fs_type, BackendType::FsType));
955 assert!(matches!(erasure_type, BackendType::ErasureType));
956 }
957
958 #[test]
959 fn test_fs_backend_creation() {
960 let fs_backend = FSBackend {
961 backend_type: BackendType::FsType,
962 };
963
964 assert!(matches!(fs_backend.backend_type, BackendType::FsType));
965 }
966
967 #[test]
968 fn test_erasure_backend_default() {
969 let erasure_backend = ErasureBackend::default();
970 assert!(matches!(erasure_backend.backend_type, BackendType::FsType));
971 assert_eq!(erasure_backend.online_disks, 0);
972 assert_eq!(erasure_backend.offline_disks, 0);
973 assert!(erasure_backend.standard_sc_parity.is_none());
974 assert!(erasure_backend.rr_sc_parity.is_none());
975 assert!(erasure_backend.total_sets.is_empty());
976 assert!(erasure_backend.drives_per_set.is_empty());
977 }
978
979 #[test]
980 fn test_erasure_backend_with_values() {
981 let erasure_backend = ErasureBackend {
982 backend_type: BackendType::ErasureType,
983 online_disks: 8,
984 offline_disks: 0,
985 standard_sc_parity: Some(2),
986 rr_sc_parity: Some(1),
987 total_sets: vec![2],
988 drives_per_set: vec![4, 4],
989 };
990
991 assert!(matches!(erasure_backend.backend_type, BackendType::ErasureType));
992 assert_eq!(erasure_backend.online_disks, 8);
993 assert_eq!(erasure_backend.offline_disks, 0);
994 assert_eq!(erasure_backend.standard_sc_parity.unwrap(), 2);
995 assert_eq!(erasure_backend.rr_sc_parity.unwrap(), 1);
996 assert_eq!(erasure_backend.total_sets.len(), 1);
997 assert_eq!(erasure_backend.drives_per_set.len(), 2);
998 }
999
1000 #[test]
1001 fn test_info_message_creation() {
1002 let mut pools = HashMap::new();
1003 let mut pool_sets = HashMap::new();
1004 pool_sets.insert(0, ErasureSetInfo::default());
1005 pools.insert(0, pool_sets);
1006
1007 let info_message = InfoMessage {
1008 mode: Some("distributed".to_string()),
1009 domain: Some(vec!["example.com".to_string()]),
1010 region: Some("us-east-1".to_string()),
1011 sqs_arn: Some(vec!["arn:aws:sqs:us-east-1:123456789012:test-queue".to_string()]),
1012 deployment_id: Some("deployment-123".to_string()),
1013 buckets: Some(Buckets { count: 5, error: None }),
1014 objects: Some(Objects {
1015 count: 1000,
1016 error: None,
1017 }),
1018 versions: Some(Versions {
1019 count: 1200,
1020 error: None,
1021 }),
1022 delete_markers: Some(DeleteMarkers { count: 50, error: None }),
1023 usage: Some(Usage {
1024 size: 1000000000,
1025 error: None,
1026 }),
1027 services: Some(Services::default()),
1028 backend: Some(ErasureBackend::default()),
1029 servers: Some(vec![ServerProperties::default()]),
1030 pools: Some(pools),
1031 };
1032
1033 assert_eq!(info_message.mode.unwrap(), "distributed");
1034 assert_eq!(info_message.domain.unwrap().len(), 1);
1035 assert_eq!(info_message.region.unwrap(), "us-east-1");
1036 assert_eq!(info_message.sqs_arn.unwrap().len(), 1);
1037 assert_eq!(info_message.deployment_id.unwrap(), "deployment-123");
1038 assert_eq!(info_message.buckets.unwrap().count, 5);
1039 assert_eq!(info_message.objects.unwrap().count, 1000);
1040 assert_eq!(info_message.versions.unwrap().count, 1200);
1041 assert_eq!(info_message.delete_markers.unwrap().count, 50);
1042 assert_eq!(info_message.usage.unwrap().size, 1000000000);
1043 assert!(info_message.services.is_some());
1044 assert_eq!(info_message.servers.unwrap().len(), 1);
1045 assert_eq!(info_message.pools.unwrap().len(), 1);
1046 }
1047
1048 #[test]
1049 fn test_serialization_deserialization() {
1050 let disk = Disk {
1051 endpoint: "http://localhost:9000".to_string(),
1052 state: "online".to_string(),
1053 total_space: 1000000000,
1054 used_space: 500000000,
1055 ..Default::default()
1056 };
1057
1058 let json = serde_json::to_string(&disk).unwrap();
1059 let deserialized: Disk = serde_json::from_str(&json).unwrap();
1060
1061 assert_eq!(deserialized.endpoint, "http://localhost:9000");
1062 assert_eq!(deserialized.state, "online");
1063 assert_eq!(deserialized.total_space, 1000000000);
1064 assert_eq!(deserialized.used_space, 500000000);
1065 }
1066
1067 #[test]
1068 fn test_debug_format_all_structures() {
1069 let item_state = ItemState::Online;
1070 let disk_metrics = DiskMetrics::default();
1071 let disk = Disk::default();
1072 let healing_disk = HealingDisk::default();
1073 let backend_byte = BackendByte::default();
1074 let storage_info = StorageInfo {
1075 disks: vec![],
1076 backend: BackendInfo::default(),
1077 };
1078 let backend_info = BackendInfo::default();
1079 let mem_stats = MemStats::default();
1080 let server_props = ServerProperties::default();
1081
1082 assert!(!format!("{item_state:?}").is_empty());
1084 assert!(!format!("{disk_metrics:?}").is_empty());
1085 assert!(!format!("{disk:?}").is_empty());
1086 assert!(!format!("{healing_disk:?}").is_empty());
1087 assert!(!format!("{backend_byte:?}").is_empty());
1088 assert!(!format!("{storage_info:?}").is_empty());
1089 assert!(!format!("{backend_info:?}").is_empty());
1090 assert!(!format!("{mem_stats:?}").is_empty());
1091 assert!(!format!("{server_props:?}").is_empty());
1092 }
1093
1094 #[test]
1095 fn test_memory_efficiency() {
1096 assert!(std::mem::size_of::<ItemState>() < 100);
1098 assert!(std::mem::size_of::<BackendByte>() < 100);
1099 assert!(std::mem::size_of::<BackendType>() < 100);
1100 assert!(std::mem::size_of::<MemStats>() < 1000);
1101 assert!(std::mem::size_of::<Buckets>() < 1000);
1102 assert!(std::mem::size_of::<Objects>() < 1000);
1103 assert!(std::mem::size_of::<Usage>() < 1000);
1104 }
1105
1106 #[test]
1107 fn test_constants() {
1108 assert_eq!(ITEM_OFFLINE, "offline");
1109 assert_eq!(ITEM_INITIALIZING, "initializing");
1110 assert_eq!(ITEM_ONLINE, "online");
1111 }
1112}