osc_cost/oapi/
volumes.rs

1use std::error;
2
3use log::{debug, info, warn};
4use outscale_api::{
5    apis::volume_api::read_volumes,
6    models::{FiltersVolume, ReadVolumesRequest, ReadVolumesResponse},
7};
8
9pub type VolumeId = String;
10const RESOURCE_NAME: &str = "Volume";
11
12use crate::{
13    core::{volumes::Volume, Resource, Resources},
14    VERSION,
15};
16
17use super::Input;
18
19impl Input {
20    pub fn fetch_volumes(&mut self) -> Result<(), Box<dyn error::Error>> {
21        if self.skip_fetch(RESOURCE_NAME) {
22            return Ok(());
23        }
24        let result: ReadVolumesResponse = {
25            let filter_volumes: FiltersVolume = match &self.filters {
26                Some(filter) => FiltersVolume {
27                    tag_keys: Some(filter.tag_keys.clone()),
28                    tag_values: Some(filter.tag_values.clone()),
29                    tags: Some(filter.tags.clone()),
30                    ..Default::default()
31                },
32                None => FiltersVolume::new(),
33            };
34            let request = ReadVolumesRequest {
35                filters: Some(Box::new(filter_volumes)),
36                ..Default::default()
37            };
38            read_volumes(&self.config, Some(request))?
39        };
40        debug!("{:#?}", result);
41
42        let volumes = match result.volumes {
43            None => {
44                warn!("no volume available");
45                return Ok(());
46            }
47            Some(volumes) => volumes,
48        };
49        self.volumes.clear();
50        for volume in volumes {
51            let volume_id = volume.volume_id.clone().unwrap_or_else(|| String::from(""));
52            self.volumes.insert(volume_id, volume);
53        }
54        info!("fetched {} volumes", self.volumes.len());
55        Ok(())
56    }
57
58    pub fn fill_resource_volume(&self, resources: &mut Resources) {
59        if self.volumes.is_empty() && self.need_default_resource {
60            resources.resources.push(Resource::Volume(Volume {
61                account_id: self.account_id(),
62                read_date_rfc3339: self.fetch_date.map(|date| date.to_rfc3339()),
63                region: self.region.clone(),
64                ..Default::default()
65            }));
66        }
67        for (volume_id, volume) in &self.volumes {
68            let specs = match VolumeSpecs::new(volume, self) {
69                Some(s) => s,
70                None => continue,
71            };
72            let core_volume = Volume {
73                osc_cost_version: Some(String::from(VERSION)),
74                account_id: self.account_id(),
75                read_date_rfc3339: self.fetch_date.map(|date| date.to_rfc3339()),
76                region: self.region.clone(),
77                resource_id: Some(volume_id.clone()),
78                price_per_hour: None,
79                price_per_month: None,
80                volume_type: Some(specs.volume_type.clone()),
81                volume_iops: Some(specs.iops),
82                volume_size: Some(specs.size),
83                price_gb_per_month: specs.price_gb_per_month,
84                price_iops_per_month: specs.price_iops_per_month,
85            };
86            resources.resources.push(Resource::Volume(core_volume));
87        }
88    }
89}
90
91struct VolumeSpecs {
92    volume_type: String,
93    size: i32,
94    iops: i32,
95    price_gb_per_month: f32,
96    price_iops_per_month: f32,
97}
98
99impl VolumeSpecs {
100    fn new(volume: &outscale_api::models::Volume, input: &Input) -> Option<Self> {
101        let volume_type = match &volume.volume_type {
102            Some(volume_type) => volume_type,
103            None => {
104                warn!("warning: cannot get volume type in volume details");
105                return None;
106            }
107        };
108
109        let iops = volume.iops.unwrap_or_else(|| {
110            if volume_type == "io1" {
111                warn!("cannot get iops in volume details");
112            }
113            0
114        });
115
116        let size = match &volume.size {
117            Some(size) => *size,
118            None => {
119                warn!("cannot get size in volume details");
120                return None;
121            }
122        };
123        let out = VolumeSpecs {
124            volume_type: volume_type.clone(),
125            iops,
126            size,
127            price_gb_per_month: 0_f32,
128            price_iops_per_month: 0_f32,
129        };
130
131        out.parse_volume_prices(input)
132    }
133
134    fn parse_volume_prices(mut self, input: &Input) -> Option<VolumeSpecs> {
135        let price_gb_per_month = input.catalog_entry(
136            "TinaOS-FCU",
137            &format!("BSU:VolumeUsage:{}", self.volume_type),
138            "CreateVolume",
139        )?;
140        if self.volume_type == "io1" {
141            self.price_iops_per_month = input.catalog_entry(
142                "TinaOS-FCU",
143                &format!("BSU:VolumeIOPS:{}", self.volume_type),
144                "CreateVolume",
145            )?;
146        }
147        self.price_gb_per_month = price_gb_per_month;
148        Some(self)
149    }
150}