1use std::collections::HashMap;
19use std::fmt::Debug;
20
21use crate::error::MetricsResult;
22use crate::ir::{TsPoint, TsValue};
23use crate::IntoPoint;
24
25use chrono::offset::Utc;
26use chrono::DateTime;
27use log::debug;
28use serde::de::DeserializeOwned;
29use uuid::Uuid;
30
31#[derive(Clone, Deserialize, Debug)]
32pub struct SolidfireConfig {
33 pub endpoint: String,
35 pub user: String,
36 pub password: String,
37 pub certificate: Option<String>,
40 pub region: String,
42}
43
44pub struct Solidfire {
45 client: reqwest::Client,
46 config: SolidfireConfig,
47}
48
49impl Solidfire {
50 pub fn new(client: &reqwest::Client, config: SolidfireConfig) -> Self {
51 Solidfire {
52 client: client.clone(),
53 config,
54 }
55 }
56}
57
58#[derive(Debug, Deserialize, IntoPoint)]
59#[serde(rename_all = "camelCase")]
60pub struct AddressBlock {
61 pub available: String,
62 pub size: u64,
63 pub start: String,
64}
65
66#[derive(Debug, Deserialize, IntoPoint)]
67#[serde(rename_all = "camelCase")]
68pub struct Cluster {
69 pub cipi: String,
70 pub cluster: String,
71 pub encryption_capable: bool,
72 pub ensemble: Vec<String>,
73 pub mipi: String,
74 pub name: String,
75 #[serde(rename = "nodeID")]
76 pub node_id: String,
77 #[serde(rename = "pendingNodeID")]
78 pub pending_node_id: u64,
79 pub role: u64,
80 pub sipi: String,
81 pub state: ClusterState,
82 pub version: String,
83}
84
85#[derive(Debug, Deserialize, IntoPoint)]
86#[serde(rename_all = "camelCase")]
87pub struct ClusterCapacity {
88 active_block_space: u64,
89 active_sessions: u64,
90 #[serde(rename = "averageIOPS")]
91 average_iops: u64,
92 #[serde(rename = "clusterRecentIOSize")]
93 cluster_recent_io_size: u64,
94 #[serde(rename = "currentIOPS")]
95 current_iops: u64,
96 #[serde(rename = "maxIOPS")]
97 max_iops: u64,
98 max_over_provisionable_space: u64,
99 max_provisioned_space: u64,
100 max_used_metadata_space: u64,
101 max_used_space: u64,
102 non_zero_blocks: u64,
103 peak_active_sessions: u64,
104 #[serde(rename = "peakIOPS")]
105 peak_iops: u64,
106 provisioned_space: u64,
107 timestamp: String,
108 total_ops: u64,
109 unique_blocks: u64,
110 unique_blocks_used_space: u64,
111 used_metadata_space: u64,
112 used_metadata_space_in_snapshots: u64,
113 used_space: u64,
114 zero_blocks: u64,
115}
116
117#[derive(Debug, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub struct ClusterCapacityResult {
120 cluster_capacity: ClusterCapacity,
121}
122
123impl IntoPoint for ClusterCapacityResult {
124 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
125 self.cluster_capacity.into_point(name, is_time_series)
126 }
127}
128
129#[derive(Debug, Deserialize, IntoPoint)]
130#[serde(rename_all = "camelCase")]
131pub struct ClusterFullThreshold {
132 pub block_fullness: FullnessStatus,
133 pub fullness: String,
134 pub max_metadata_over_provision_factor: u16,
135 pub metadata_fullness: FullnessStatus,
136 pub slice_reserve_used_threshold_pct: u16,
137 pub stage2_aware_threshold: u16,
138 pub stage2_block_threshold_bytes: u64,
139 pub stage3_block_threshold_bytes: u64,
140 pub stage3_block_threshold_percent: u16,
141 pub stage3_low_threshold: i64,
142 pub stage4_block_threshold_bytes: u64,
143 pub stage4_critical_threshold: i64,
144 pub stage5_block_threshold_bytes: u64,
145 pub sum_total_cluster_bytes: u64,
146 pub sum_total_metadata_cluster_bytes: u64,
147 pub sum_used_cluster_bytes: u64,
148 pub sum_used_metadata_cluster_bytes: u64,
149}
150
151#[derive(Debug, Deserialize, IntoPoint)]
152#[serde(rename_all = "camelCase")]
153pub struct ClusterInfo {
154 pub attributes: HashMap<String, String>,
155 pub encryption_at_rest_state: EncryptionState,
156 pub ensemble: Vec<String>,
157 pub mvip: String,
158 pub mvip_interface: String,
159 #[serde(rename = "mvipNodeID")]
160 pub mvip_node_id: u16,
161 pub mvip_vlan_tag: String,
162 pub name: String,
163 pub rep_count: u16,
164 pub svip: String,
165 pub svip_interface: String,
166 #[serde(rename = "svipNodeID")]
167 pub svip_node_id: u16,
168 pub svip_vlan_tag: String,
169 #[serde(rename = "uniqueID")]
170 pub unique_id: String,
171 pub uuid: Uuid,
172}
173
174#[derive(Debug, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct ClusterInfoResult {
177 pub cluster_info: ClusterInfo,
178}
179
180impl IntoPoint for ClusterInfoResult {
181 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
182 self.cluster_info.into_point(name, is_time_series)
183 }
184}
185
186#[derive(Debug, Deserialize)]
187pub enum ClusterState {
188 Available,
189 Pending,
190 Active,
191 PendingActive,
192}
193
194#[derive(Debug, Deserialize, IntoPoint)]
195#[serde(rename_all = "camelCase")]
196pub struct ClusterStat {
197 #[serde(rename = "actualIOPS")]
198 pub actual_iops: u64,
199 #[serde(rename = "averageIOPSize")]
200 pub average_iop_size: u64,
201 pub client_queue_depth: u64,
202 pub cluster_utilization: f64,
203 pub latency_u_sec: u64,
204 #[serde(rename = "normalizedIOPS")]
205 pub normalized_iops: u64,
206 pub read_bytes: u64,
208 pub read_bytes_last_sample: u64,
209 pub read_latency_u_sec: u64,
210 pub read_latency_u_sec_total: u64,
211 pub read_ops: u64,
212 pub read_ops_last_sample: u64,
213 pub sample_period_msec: u64,
214 pub services_count: u64,
215 pub services_total: u64,
216 pub timestamp: String,
217 pub unaligned_reads: u64,
218 pub unaligned_writes: u64,
219 pub write_bytes: u64,
220 pub write_bytes_last_sample: u64,
221 pub write_latency_u_sec: u64,
222 pub write_latency_u_sec_total: u64,
223 pub write_ops: u64,
224 pub write_ops_last_sample: u64,
225}
226
227#[derive(Debug, Deserialize)]
228#[serde(rename_all = "camelCase")]
229pub struct ClusterStatsResult {
230 pub cluster_stats: ClusterStat,
231}
232
233impl IntoPoint for ClusterStatsResult {
234 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
235 self.cluster_stats.into_point(name, is_time_series)
236 }
237}
238
239#[derive(Debug, Deserialize, IntoPoint)]
240#[serde(rename_all = "camelCase")]
241pub struct Drive {
242 pub attributes: HashMap<String, String>,
243 pub capacity: u64,
244 pub classic_slot: String,
245 #[serde(rename = "driveID")]
246 pub drive_id: u64,
247 #[serde(rename = "nodeID")]
248 pub node_id: u64,
249 pub serial: String,
250 pub status: DriveStatus,
251 #[serde(rename = "drive")]
252 pub drive_type: DriveType,
253}
254
255#[derive(Debug, Deserialize)]
256#[serde(rename_all = "camelCase")]
257pub enum DriveStatus {
258 Available,
259 Active,
260 Erasing,
261 Failed,
262 Removing,
263}
264
265#[derive(Debug, Deserialize)]
266#[serde(rename_all = "camelCase")]
267pub enum DriveType {
268 Block,
269 Unknown,
270 Volume,
271}
272
273#[derive(Debug, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub enum EncryptionState {
276 Enabling,
277 Enabled,
278 Disabling,
279 Disabled,
280}
281
282#[derive(Debug, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub enum FullnessStatus {
285 Stage1Happy,
286 Stage2Aware,
287 Stage3Low,
288 Stage4Critical,
289 Stage5CompletelyConsumed,
290}
291
292#[derive(Debug, Deserialize, IntoPoint)]
293#[serde(rename_all = "camelCase")]
294pub struct MetadataHosts {
295 dead_secondaries: Vec<u64>,
296 live_secondaries: Vec<u64>,
297 primary: u64,
298}
299
300#[derive(Debug, Deserialize, IntoPoint)]
301#[serde(rename_all = "camelCase")]
302pub struct Node {
303 #[serde(rename = "associatedFServiceID")]
304 pub associated_f_service_id: u64,
305 #[serde(rename = "associatedMasterServiceID")]
306 pub associated_master_service_id: u64,
307 pub attributes: HashMap<String, String>,
308 pub cip: String,
309 pub cipi: String,
310 pub fibre_channel_target_port_group: Option<u64>,
311 pub mip: String,
312 pub mipi: String,
313 pub name: String,
314 #[serde(rename = "nodeID")]
315 pub node_id: u64,
316 pub node_slot: String,
317 pub platform_info: PlatformInfo,
318 #[serde(rename = "protocolEndpointIDs")]
319 pub protocol_endpoints_ids: Option<Vec<Uuid>>,
320 pub sip: String,
321 pub sipi: String,
322 pub software_version: String,
323 pub uuid: Uuid,
324 pub virtual_networks: Vec<VirtualNetwork>,
325}
326
327#[derive(Debug, Deserialize)]
328pub struct Nodes {
329 pub nodes: Vec<Node>,
330}
331
332#[derive(Debug, Deserialize, IntoPoint)]
333#[serde(rename_all = "camelCase")]
334pub struct NodeStats {
335 pub count: u64,
336 pub cpu: u16,
337 pub cpu_total: u64,
338 pub c_bytes_in: u64,
340 pub c_bytes_out: u64,
342 pub s_bytes_in: u64,
344 pub s_bytes_out: u64,
346 pub m_bytes_in: u64,
348 pub m_bytes_out: u64,
350 pub network_utilization_cluster: u16,
351 pub network_utilization_storage: u16,
352 pub read_latency_u_sec_total: u64,
353 pub read_ops: u64,
354 pub timestamp: String,
355 pub used_memory: u64,
356 pub write_latency_u_sec_total: u64,
357 pub write_ops: u64,
358}
359
360#[derive(Debug, Deserialize, IntoPoint)]
361#[serde(rename_all = "camelCase")]
362pub struct PlatformInfo {
363 pub chassis_type: String,
364 pub cpu_model: String,
365 #[serde(rename = "nodeMemoryGB")]
366 pub node_memory_gb: u64,
367 pub node_type: String,
368 pub platform_config_version: String,
369}
370
371#[derive(Debug, Deserialize)]
372pub enum ReplicationMode {
373 Async,
374 Sync,
375 SnapshotsOnly,
376}
377
378#[derive(Debug, Deserialize, IntoPoint)]
379#[serde(rename_all = "camelCase")]
380pub struct RemoteReplication {
381 pub mode: ReplicationMode,
382 pub pause_limit: u64,
383 #[serde(rename = "remoteServiceID")]
384 pub remote_service_id: u64,
385 pub resume_details: Option<String>,
386 pub snapshot_replication: SnapshotReplication,
387 pub state: String,
388 pub state_details: Option<String>,
389}
390
391#[derive(Debug, Deserialize)]
392pub enum RemoveStatus {
393 Present,
394 NotPresent,
395 Syncing,
396 Deleted,
397}
398
399#[derive(Debug, Deserialize, IntoPoint)]
400pub struct Snapshot {
401 pub attributes: HashMap<String, String>,
402 pub checksum: String,
403 pub create_time: String,
404 pub enable_remote_replication: String,
405 pub expiration_reason: String,
406 pub expiration_time: String,
407 #[serde(rename = "groupID")]
408 pub group_id: u64,
409 #[serde(rename = "groupSnapshotUUID")]
410 pub group_snapshot_uuid: Uuid,
411 pub name: String,
412 pub remote_status: RemoveStatus,
413 #[serde(rename = "snapshotID")]
414 pub snapshot_id: String,
415 #[serde(rename = "snapshotUUID")]
416 pub snapshot_uuid: Uuid,
417 pub status: SnapshotStatus,
418 pub total_size: u64,
419 #[serde(rename = "virtualVolumeID")]
420 pub virtual_volume_id: Uuid,
421 #[serde(rename = "volumeID")]
422 pub volume_id: u64,
423}
424
425#[derive(Debug, Deserialize, IntoPoint)]
426#[serde(rename_all = "camelCase")]
427pub struct SnapshotReplication {
428 pub state: String,
429 pub state_details: String,
430}
431
432#[derive(Debug, Deserialize, IntoPoint)]
433#[serde(rename_all = "camelCase")]
434pub struct StorageContainer {
435 #[serde(rename = "accountID")]
436 pub account_id: u64,
437 pub initiator_secret: String,
438 pub name: String,
439 pub protocol_endpoint_type: String,
440 pub status: StorageContainerStatus,
441 #[serde(rename = "storageContainerID")]
442 pub storage_container_id: Uuid,
443 pub target_secret: String,
444 pub virtual_volumes: Vec<Uuid>,
445}
446
447#[derive(Debug, Deserialize)]
448#[serde(rename_all = "camelCase")]
449pub enum SnapshotStatus {
450 Unknown,
451 Preparing,
452 RemoteSyncing,
453 Done,
454 Active,
455 Cloning,
456}
457
458#[derive(Debug, Deserialize)]
459#[serde(rename_all = "camelCase")]
460pub enum StorageContainerStatus {
461 Active,
462 Locked,
463}
464
465#[derive(Debug, Deserialize, IntoPoint)]
466pub struct VolumesAttributesImageInfo {
467 pub image_created_at: String,
468 pub image_id: String,
469 pub image_name: String,
470 pub image_updated_at: String,
471}
472
473#[derive(Debug, Deserialize, IntoPoint)]
482pub struct VolumesQosCurve {
483 #[serde(rename = "1048576")]
484 pub one_mb: i64,
485 #[serde(rename = "131072")]
486 pub onehundred_twentyeight_kb: i64,
487 #[serde(rename = "16384")]
488 pub sixteen_kb: i64,
489 #[serde(rename = "262144")]
490 pub twohundred_fiftysix_kb: i64,
491 #[serde(rename = "32768")]
492 pub thirtytwo_kb: i64,
493 #[serde(rename = "4096")]
494 pub four_kb: i64,
495 #[serde(rename = "524288")]
496 pub fivehundred_kb: i64,
497 #[serde(rename = "65536")]
498 pub sixtyfive_kb: i64,
499 #[serde(rename = "8192")]
500 pub eight_kb: i64,
501}
502
503#[derive(Debug, Deserialize, IntoPoint)]
504pub struct VolumesQos {
505 #[serde(rename = "burstIOPS")]
506 pub burst_iops: i64,
507 #[serde(rename = "burstTime")]
508 pub burst_time: i64,
509 pub curve: VolumesQosCurve,
510 #[serde(rename = "maxIOPS")]
511 pub max_iops: i64,
512 #[serde(rename = "minIOPS")]
513 pub min_iops: i64,
514}
515
516#[derive(Debug, Deserialize)]
517#[serde(rename_all = "camelCase")]
518pub enum VolumeAccess {
519 ReadOnly,
520 ReadWrite,
521 Locked,
522 ReplicationTarget,
523}
524
525#[derive(Debug, Deserialize)]
526#[serde(rename_all = "camelCase")]
527pub enum VolumeStatus {
528 Cloning,
529 Waiting,
530 Ready,
531}
532
533#[derive(Debug, Deserialize, IntoPoint)]
534pub struct VolumePair {
535 #[serde(rename = "clusterPairID")]
536 pub cluster_pair_id: i64,
537 pub remote_replication: RemoteReplication,
538 #[serde(rename = "remoteSliceID")]
539 pub remote_slice_id: u64,
540 #[serde(rename = "remoteVolumeID")]
541 pub remote_volume_id: u64,
542 pub remote_volume_name: String,
543 #[serde(rename = "volumePairUUID")]
544 pub volume_pair_uuid: String,
545}
546
547#[derive(Debug, Deserialize, IntoPoint)]
548#[serde(rename_all = "camelCase")]
549pub struct VirtualNetwork {
550 pub address_blocks: Vec<AddressBlock>,
551 pub attributes: HashMap<String, String>,
552 pub name: String,
553 pub netmask: String,
554 pub svip: String,
555 pub gateway: String,
556 #[serde(rename = "virtualNetworkID")]
557 pub virtual_network_id: u64,
558 pub virtual_network_tag: u64,
559}
560
561#[derive(Debug, Deserialize, IntoPoint)]
562#[serde(rename_all = "camelCase")]
563pub struct VirtualVolume {
564 pub bindings: Vec<Uuid>,
565 pub children: Vec<Uuid>,
566 pub descendants: Vec<Uuid>,
567 pub metadata: HashMap<String, String>,
568 #[serde(rename = "parentVirtualVolumeID")]
569 pub parent_virtual_volume_id: Uuid,
570 #[serde(rename = "snapshotID")]
571 pub snapshot_id: u64,
572 pub snapshot_info: Snapshot,
573 pub status: VolumeStatus,
574 pub storage_container: StorageContainer,
575 #[serde(rename = "virtualVolumeID")]
576 pub virtual_volume_id: Uuid,
577 pub virtual_volume_type: String,
578 #[serde(rename = "volumeID")]
579 pub volume_id: u64,
580 pub volume_info: Option<Volume>,
581}
582
583#[derive(Debug, Deserialize, IntoPoint)]
584#[serde(rename_all = "camelCase")]
585pub struct Volume {
586 pub access: VolumeAccess,
587 #[serde(rename = "accountID")]
588 pub account_id: u64,
589 pub attributes: HashMap<String, serde_json::Value>,
590 pub block_size: u64,
591 pub create_time: String,
592 pub delete_time: String,
593 pub enable512e: bool,
594 pub enable_snap_mirror_replication: bool,
595 pub iqn: String,
596 pub last_access_time: Option<String>,
597 #[serde(rename = "lastAccessTimeIO")]
598 pub last_access_time_io: Option<String>,
599 pub name: String,
600 pub purge_time: String,
601 pub qos: VolumesQos,
602 #[serde(rename = "qosPolicyID")]
603 pub qos_policy_id: Option<i64>,
604 #[serde(rename = "scsiEUIDeviceID")]
605 pub scsi_eui_device_id: String,
606 #[serde(rename = "scsiNAADeviceID")]
607 pub scsi_naa_device_id: String,
608 pub slice_count: i64,
609 pub status: String,
610 pub total_size: i64,
611 #[serde(rename = "virtualVolumeID")]
612 pub virtual_volume_id: Option<Uuid>,
613 pub volume_access_groups: Vec<u64>,
614 #[serde(rename = "volumeConsistencyGroupUUID")]
615 pub volume_consistency_group_uuid: Uuid,
616 #[serde(rename = "volumeID")]
617 pub volume_id: u64,
618 pub volume_pairs: Vec<VolumePair>,
619 #[serde(rename = "volumeUUID")]
620 pub volume_uuid: Uuid,
621}
622
623#[derive(Debug, Deserialize, IntoPoint)]
624#[serde(rename_all = "camelCase")]
625pub struct VolumeStats {
626 #[serde(rename = "accountID")]
627 pub account_id: u64,
628 #[serde(rename = "actualIOPS")]
629 pub actual_iops: u64,
630 pub async_delay: Option<String>,
631 #[serde(rename = "averageIOPSize")]
632 pub average_iop_size: u64,
633 #[serde(rename = "burstIOPSCredit")]
634 pub burst_iops_credit: u64,
635 pub client_queue_depth: u64,
636 pub cluster_utilization: Option<f64>,
637 pub desired_metadata_hosts: Option<serde_json::Value>,
638 pub latency_u_sec: u64,
639 pub metadata_hosts: MetadataHosts,
640 pub non_zero_blocks: u64,
641 pub read_bytes: u64,
642 pub read_bytes_last_sample: u64,
643 pub read_latency_u_sec: u64,
644 pub read_latency_u_sec_total: u64,
645 pub read_ops: u64,
646 pub read_ops_last_sample: u64,
647 pub sample_period_m_sec: u64,
648 pub throttle: f64,
649 pub timestamp: String,
650 pub unaligned_reads: u64,
651 pub unaligned_writes: u64,
652 pub volume_access_groups: Vec<u64>,
653 #[serde(rename = "volumeID")]
654 pub volume_id: u64,
655 pub volume_size: u64,
656 pub volume_utilization: f64,
657 pub write_bytes: u64,
658 pub write_bytes_last_sample: u64,
659 pub write_latency_u_sec: u64,
660 pub write_latency_u_sec_total: u64,
661 pub write_ops: u64,
662 pub write_ops_last_sample: u64,
663 pub zero_blocks: u64,
664}
665
666#[derive(Debug, Deserialize)]
667#[serde(rename_all = "camelCase")]
668pub struct VolumeStatsResult {
669 pub volume_stats: VolumeStats,
670}
671
672impl IntoPoint for VolumeStatsResult {
673 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
674 self.volume_stats.into_point(name, is_time_series)
675 }
676}
677
678#[derive(Debug, Deserialize)]
679#[serde(rename_all = "camelCase")]
680struct DriveHardwareResult {
681 nodes: Vec<HardwareNode>,
682}
683
684impl IntoPoint for DriveHardwareResult {
685 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
686 self.nodes
687 .iter()
688 .flat_map(|n| n.into_point(name, is_time_series))
689 .collect::<Vec<TsPoint>>()
690 }
691}
692
693#[test]
694fn test_get_drive_hardware() {
695 use std::fs::File;
696 use std::io::Read;
697
698 let mut f = File::open("tests/solidfire/list_drive_hardware.json").unwrap();
699 let mut buff = String::new();
700 f.read_to_string(&mut buff).unwrap();
701
702 let r: JsonResult<HardwareNodes> = serde_json::from_str(&buff).unwrap();
703 println!(
704 "JsonResult: {:?}",
705 r.result.into_point(Some("solidfire_drive_hardware"), true)
706 );
707}
708
709#[derive(Debug, Deserialize)]
710struct HardwareNodes {
711 nodes: Vec<HardwareNode>,
712}
713
714impl IntoPoint for HardwareNodes {
715 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
716 self.nodes
717 .iter()
718 .flat_map(|n| n.into_point(name, is_time_series))
719 .collect::<Vec<TsPoint>>()
720 }
721}
722
723#[derive(Debug, Deserialize)]
724#[serde(rename_all = "camelCase")]
725struct HardwareNode {
726 #[serde(rename = "nodeID")]
727 node_id: u64,
728 result: DriveHardware,
729}
730
731impl IntoPoint for HardwareNode {
732 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
733 self.result.into_point(name, is_time_series)
734 }
735}
736
737#[derive(Debug, Deserialize)]
738#[serde(rename_all = "camelCase")]
739struct DriveHardware {
740 drive_hardware: Vec<HardwareDrive>,
741}
742
743impl IntoPoint for DriveHardware {
744 fn into_point(&self, name: Option<&str>, is_time_series: bool) -> Vec<TsPoint> {
745 self.drive_hardware
746 .iter()
747 .flat_map(|n| n.into_point(name, is_time_series))
748 .collect::<Vec<TsPoint>>()
749 }
750}
751
752#[derive(Debug, Deserialize, IntoPoint)]
753#[serde(rename_all = "camelCase")]
754struct HardwareDrive {
755 canonical_name: String,
756 connected: bool,
757 dev: u64,
758 dev_path: String,
759 drive_type: String,
760 life_remaining_percent: u64,
761 lifetime_read_bytes: u64,
762 lifetime_write_bytes: u64,
763 name: String,
764 path: String,
765 path_link: String,
766 power_on_hours: u64,
767 product: String,
768 reallocated_sectors: u64,
769 reserve_capacity_percent: u64,
770 scsi_compat_id: String,
771 scsi_state: String,
772 security_at_maximum: bool,
773 security_enabled: bool,
774 security_frozen: bool,
775 security_locked: bool,
776 security_supported: bool,
777 serial: String,
778 size: u64,
779 slot: u64,
780 uncorrectable_errors: u64,
781 #[serde(rename = "uuid")]
782 hardware_uuid: Uuid,
783 vendor: String,
784 version: String,
785}
786
787#[derive(Debug, Deserialize)]
788pub struct Volumes {
789 pub volumes: Vec<Volume>,
790}
791
792#[derive(Debug, Deserialize)]
793pub struct JsonResult<T> {
794 pub id: Option<String>,
795 pub result: T,
796}
797
798#[test]
799fn test_get_cluster_capacity() {
800 use std::fs::File;
801 use std::io::Read;
802
803 let mut f = File::open("tests/solidfire/get_cluster_capacity.json").unwrap();
804 let mut buff = String::new();
805 f.read_to_string(&mut buff).unwrap();
806
807 let r: JsonResult<ClusterCapacityResult> = serde_json::from_str(&buff).unwrap();
808 println!("JsonResult: {:?}", r);
809}
810
811#[test]
812fn test_get_cluster_fullness() {
813 use std::fs::File;
814 use std::io::Read;
815
816 let mut f = File::open("tests/solidfire/get_cluster_full_threshold.json").unwrap();
817 let mut buff = String::new();
818 f.read_to_string(&mut buff).unwrap();
819
820 let r: JsonResult<ClusterFullThreshold> = serde_json::from_str(&buff).unwrap();
821 println!("JsonResult: {:?}", r);
822}
823
824#[test]
825fn test_get_cluster_info() {
826 use std::fs::File;
827 use std::io::Read;
828
829 let mut f = File::open("tests/solidfire/get_cluster_info.json").unwrap();
830 let mut buff = String::new();
831 f.read_to_string(&mut buff).unwrap();
832
833 let r: JsonResult<ClusterInfoResult> = serde_json::from_str(&buff).unwrap();
834 println!("JsonResult: {:?}", r);
835}
836
837#[test]
838fn test_get_cluster_stats() {
839 use std::fs::File;
840 use std::io::Read;
841
842 let mut f = File::open("tests/solidfire/get_cluster_stats.json").unwrap();
843 let mut buff = String::new();
844 f.read_to_string(&mut buff).unwrap();
845
846 let r: JsonResult<ClusterStatsResult> = serde_json::from_str(&buff).unwrap();
847 println!("JsonResult: {:?}", r);
848}
849
850#[test]
851fn test_get_volume_stats() {
852 use std::fs::File;
853 use std::io::Read;
854
855 let mut f = File::open("tests/solidfire/get_volume_stats.json").unwrap();
856 let mut buff = String::new();
857 f.read_to_string(&mut buff).unwrap();
858
859 let r: JsonResult<VolumeStatsResult> = serde_json::from_str(&buff).unwrap();
860 println!("JsonResult: {:?}", r);
861}
862
863#[test]
864fn test_list_sf_volumes() {
865 use std::fs::File;
866 use std::io::Read;
867
868 let mut f = File::open("tests/solidfire/list_volumes.json").unwrap();
869 let mut buff = String::new();
870 f.read_to_string(&mut buff).unwrap();
871
872 let r: JsonResult<Volumes> = serde_json::from_str(&buff).unwrap();
873 println!("JsonResult: {:?}", r);
874}
875
876#[test]
877fn test_list_sf_nodes() {
878 use std::fs::File;
879 use std::io::Read;
880
881 let mut f = File::open("tests/solidfire/list_active_nodes.json").unwrap();
882 let mut buff = String::new();
883 f.read_to_string(&mut buff).unwrap();
884
885 let r: JsonResult<Nodes> = serde_json::from_str(&buff).unwrap();
886 println!("JsonResult: {:?}", r);
887}
888
889impl Solidfire {
890 pub fn get<T>(
892 &self,
893 method: &str,
894 params: Option<HashMap<String, String>>,
895 force: bool,
896 ) -> MetricsResult<T>
897 where
898 T: DeserializeOwned + Debug,
899 {
900 let mut url = format!(
901 "https://{}/json-rpc/8.4?method={}",
902 self.config.endpoint, method
903 );
904 if force {
905 url.push_str("&force=true");
906 }
907 if let Some(p) = params {
908 url.push_str(
909 &p.into_iter()
910 .map(|(k, v)| format!("&{}={}", k, v))
911 .collect::<Vec<String>>()
912 .join(""),
913 );
914 }
915 let j: T = crate::get(
916 &self.client,
917 &url,
918 &self.config.user,
919 Some(&self.config.password),
920 )?;
921
922 Ok(j)
923 }
924
925 pub fn get_drive_hardware_info(&self, t: DateTime<Utc>) -> MetricsResult<Vec<TsPoint>> {
926 debug!("get_hardware_info");
927 let info = self.get::<JsonResult<HardwareNodes>>("ListDriveHardware", None, true)?;
928 Ok(info
929 .result
930 .into_point(Some("solidfire_drive_hardware"), true)
931 .into_iter()
932 .map(|mut p| {
933 p.timestamp = Some(t);
934 p
935 })
936 .collect::<Vec<TsPoint>>())
937 }
938
939 pub fn get_cluster_capacity(&self, t: DateTime<Utc>) -> MetricsResult<Vec<TsPoint>> {
940 debug!("get_cluster_capacity");
941 let info =
942 self.get::<JsonResult<ClusterCapacityResult>>("GetClusterCapacity", None, false)?;
943 Ok(info
944 .result
945 .into_point(Some("solidfire_cluster_capacity"), true)
946 .into_iter()
947 .map(|mut p| {
948 p.timestamp = Some(t);
949 p
950 })
951 .collect::<Vec<TsPoint>>())
952 }
953
954 pub fn get_cluster_fullness(&self, t: DateTime<Utc>) -> MetricsResult<Vec<TsPoint>> {
955 debug!("get_cluster_fullness");
956 let info =
957 self.get::<JsonResult<ClusterFullThreshold>>("GetClusterFullThreshold", None, false)?;
958 Ok(info
959 .result
960 .into_point(Some("solidfire_cluster_full_threshold"), true)
961 .into_iter()
962 .map(|mut p| {
963 p.timestamp = Some(t);
964 p
965 })
966 .collect::<Vec<TsPoint>>())
967 }
968
969 pub fn get_cluster_info(&self) -> MetricsResult<ClusterInfoResult> {
970 debug!("get_cluster_info");
971 let info = self.get::<JsonResult<ClusterInfoResult>>("GetClusterInfo", None, false)?;
972 Ok(info.result)
973 }
974
975 pub fn get_cluster_stats(&self, t: DateTime<Utc>) -> MetricsResult<Vec<TsPoint>> {
976 debug!("get_cluster_stats");
977 let info = self.get::<JsonResult<ClusterStatsResult>>("GetClusterStats", None, false)?;
978 Ok(info
979 .result
980 .into_point(Some("solidfire_cluster_stats"), true)
981 .into_iter()
982 .map(|mut p| {
983 p.timestamp = Some(t);
984 p
985 })
986 .collect::<Vec<TsPoint>>())
987 }
988
989 pub fn get_volume_stats(
990 &self,
991 volume_id: u64,
992 t: DateTime<Utc>,
993 ) -> MetricsResult<Vec<TsPoint>> {
994 let mut params = HashMap::new();
995 params.insert("volumeID".to_string(), volume_id.to_string());
996
997 debug!("get_volume_stats");
998 let info =
999 self.get::<JsonResult<VolumeStatsResult>>("GetVolumeStats", Some(params), false)?;
1000 Ok(info
1001 .result
1002 .into_point(Some("solidfire_volume_stats"), true)
1003 .into_iter()
1004 .map(|mut p| {
1005 p.timestamp = Some(t);
1006 p
1007 })
1008 .collect::<Vec<TsPoint>>())
1009 }
1010
1011 pub fn list_volumes(&self) -> MetricsResult<Volumes> {
1016 debug!("list_volumes");
1017 let info = self.get::<JsonResult<Volumes>>("ListVolumes", None, false)?;
1018 Ok(info.result)
1019 }
1020
1021 pub fn list_volume_ids(&self) -> MetricsResult<Vec<u64>> {
1022 debug!("list_volume_ids");
1023 let info = self.get::<JsonResult<Volumes>>("ListVolumes", None, false)?;
1024 Ok(info
1025 .result
1026 .volumes
1027 .into_iter()
1028 .map(|v| v.volume_id)
1029 .collect::<Vec<u64>>())
1030 }
1031}