1use std::convert::{From, TryInto};
5use std::io;
6
7use serde::{Deserialize, Serialize};
8
9use crate::rate_limiter::{BucketUpdate, RateLimiter, TokenBucket};
10
11pub mod balloon;
13pub mod boot_source;
15pub mod drive;
17pub mod entropy;
19pub mod instance_info;
21pub mod machine_config;
23pub mod memory_hotplug;
25pub mod metrics;
27pub mod mmds;
29pub mod net;
31pub mod pmem;
33pub mod serial;
35pub mod snapshot;
36pub mod vsock;
38
39#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
51pub struct TokenBucketConfig {
52 pub size: u64,
54 pub one_time_burst: Option<u64>,
56 pub refill_time: u64,
58}
59
60impl From<&TokenBucket> for TokenBucketConfig {
61 fn from(tb: &TokenBucket) -> Self {
62 let one_time_burst = match tb.initial_one_time_burst() {
63 0 => None,
64 v => Some(v),
65 };
66 TokenBucketConfig {
67 size: tb.capacity(),
68 one_time_burst,
69 refill_time: tb.refill_time_ms(),
70 }
71 }
72}
73
74#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
77#[serde(deny_unknown_fields)]
78pub struct RateLimiterConfig {
79 pub bandwidth: Option<TokenBucketConfig>,
81 pub ops: Option<TokenBucketConfig>,
83}
84
85#[derive(Debug)]
87pub struct RateLimiterUpdate {
88 pub bandwidth: BucketUpdate,
90 pub ops: BucketUpdate,
92}
93
94fn get_bucket_update(tb_cfg: &Option<TokenBucketConfig>) -> BucketUpdate {
95 match tb_cfg {
96 Some(tb_cfg) => {
98 TokenBucket::new(
99 tb_cfg.size,
100 tb_cfg.one_time_burst.unwrap_or(0),
101 tb_cfg.refill_time,
102 )
103 .map(BucketUpdate::Update)
105 .unwrap_or(BucketUpdate::Disabled)
107 }
108 None => BucketUpdate::None,
110 }
111}
112
113impl From<Option<RateLimiterConfig>> for RateLimiterUpdate {
114 fn from(cfg: Option<RateLimiterConfig>) -> Self {
115 if let Some(cfg) = cfg {
116 RateLimiterUpdate {
117 bandwidth: get_bucket_update(&cfg.bandwidth),
118 ops: get_bucket_update(&cfg.ops),
119 }
120 } else {
121 RateLimiterUpdate {
123 bandwidth: BucketUpdate::None,
124 ops: BucketUpdate::None,
125 }
126 }
127 }
128}
129
130impl TryInto<RateLimiter> for RateLimiterConfig {
131 type Error = io::Error;
132
133 fn try_into(self) -> Result<RateLimiter, Self::Error> {
134 let bw = self.bandwidth.unwrap_or_default();
135 let ops = self.ops.unwrap_or_default();
136 RateLimiter::new(
137 bw.size,
138 bw.one_time_burst.unwrap_or(0),
139 bw.refill_time,
140 ops.size,
141 ops.one_time_burst.unwrap_or(0),
142 ops.refill_time,
143 )
144 }
145}
146
147impl From<&RateLimiter> for RateLimiterConfig {
148 fn from(rl: &RateLimiter) -> Self {
149 RateLimiterConfig {
150 bandwidth: rl.bandwidth().map(TokenBucketConfig::from),
151 ops: rl.ops().map(TokenBucketConfig::from),
152 }
153 }
154}
155
156impl RateLimiterConfig {
157 pub fn into_option(self) -> Option<RateLimiterConfig> {
160 if self.bandwidth.is_some() || self.ops.is_some() {
161 Some(self)
162 } else {
163 None
164 }
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 const SIZE: u64 = 1024 * 1024;
173 const ONE_TIME_BURST: u64 = 1024;
174 const REFILL_TIME: u64 = 1000;
175
176 #[test]
177 fn test_rate_limiter_configs() {
178 let rlconf = RateLimiterConfig {
179 bandwidth: Some(TokenBucketConfig {
180 size: SIZE,
181 one_time_burst: Some(ONE_TIME_BURST),
182 refill_time: REFILL_TIME,
183 }),
184 ops: Some(TokenBucketConfig {
185 size: SIZE * 2,
186 one_time_burst: None,
187 refill_time: REFILL_TIME * 2,
188 }),
189 };
190 let rl: RateLimiter = rlconf.try_into().unwrap();
191 assert_eq!(rl.bandwidth().unwrap().capacity(), SIZE);
192 assert_eq!(rl.bandwidth().unwrap().one_time_burst(), ONE_TIME_BURST);
193 assert_eq!(rl.bandwidth().unwrap().refill_time_ms(), REFILL_TIME);
194 assert_eq!(rl.ops().unwrap().capacity(), SIZE * 2);
195 assert_eq!(rl.ops().unwrap().one_time_burst(), 0);
196 assert_eq!(rl.ops().unwrap().refill_time_ms(), REFILL_TIME * 2);
197 }
198
199 #[test]
200 fn test_generate_configs() {
201 let bw_tb_cfg = TokenBucketConfig {
202 size: SIZE,
203 one_time_burst: Some(ONE_TIME_BURST),
204 refill_time: REFILL_TIME,
205 };
206 let bw_tb = TokenBucket::new(SIZE, ONE_TIME_BURST, REFILL_TIME).unwrap();
207 let generated_bw_tb_cfg = TokenBucketConfig::from(&bw_tb);
208 assert_eq!(generated_bw_tb_cfg, bw_tb_cfg);
209
210 let rl_conf = RateLimiterConfig {
211 bandwidth: Some(bw_tb_cfg),
212 ops: None,
213 };
214 let rl: RateLimiter = rl_conf.try_into().unwrap();
215 let generated_rl_conf = RateLimiterConfig::from(&rl);
216 assert_eq!(generated_rl_conf, rl_conf);
217 assert_eq!(generated_rl_conf.into_option(), Some(rl_conf));
218 }
219}