1use derive_builder::Builder;
57use http::{HeaderMap, HeaderName, HeaderValue};
58
59use openstack_sdk_core::api::rest_endpoint_prelude::*;
60
61use serde::Deserialize;
62use serde::Serialize;
63use std::borrow::Cow;
64
65#[derive(Debug, Deserialize, Clone, Serialize)]
66pub enum HttpMethod {
67 #[serde(rename = "CONNECT")]
68 Connect,
69 #[serde(rename = "DELETE")]
70 Delete,
71 #[serde(rename = "GET")]
72 Get,
73 #[serde(rename = "HEAD")]
74 Head,
75 #[serde(rename = "OPTIONS")]
76 Options,
77 #[serde(rename = "PATCH")]
78 Patch,
79 #[serde(rename = "POST")]
80 Post,
81 #[serde(rename = "PUT")]
82 Put,
83 #[serde(rename = "TRACE")]
84 Trace,
85}
86
87#[derive(Debug, Deserialize, Clone, Serialize)]
88pub enum HealthmonitorType {
89 #[serde(rename = "HTTP")]
90 Http,
91 #[serde(rename = "HTTPS")]
92 Https,
93 #[serde(rename = "PING")]
94 Ping,
95 #[serde(rename = "SCTP")]
96 Sctp,
97 #[serde(rename = "TCP")]
98 Tcp,
99 #[serde(rename = "TLS-HELLO")]
100 TlsHello,
101 #[serde(rename = "UDP-CONNECT")]
102 UdpConnect,
103}
104
105#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
107#[builder(setter(strip_option))]
108pub struct Healthmonitor<'a> {
109 #[serde(skip_serializing_if = "Option::is_none")]
110 #[builder(default, setter(into))]
111 pub(crate) admin_state_up: Option<bool>,
112
113 #[serde()]
114 #[builder(setter(into))]
115 pub(crate) delay: i32,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
118 #[builder(default, setter(into))]
119 pub(crate) domain_name: Option<Cow<'a, str>>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
122 #[builder(default, setter(into))]
123 pub(crate) expected_codes: Option<Cow<'a, str>>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
126 #[builder(default)]
127 pub(crate) http_method: Option<HttpMethod>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
130 #[builder(default, setter(into))]
131 pub(crate) http_version: Option<f32>,
132
133 #[serde()]
134 #[builder(setter(into))]
135 pub(crate) max_retries: i32,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
138 #[builder(default, setter(into))]
139 pub(crate) max_retries_down: Option<i32>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
142 #[builder(default, setter(into))]
143 pub(crate) name: Option<Cow<'a, str>>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
149 #[builder(default, setter(into))]
150 pub(crate) tags: Option<Vec<Cow<'a, str>>>,
151
152 #[serde()]
153 #[builder(setter(into))]
154 pub(crate) timeout: i32,
155
156 #[serde(rename = "type")]
157 #[builder()]
158 pub(crate) _type: HealthmonitorType,
159
160 #[serde(skip_serializing_if = "Option::is_none")]
161 #[builder(default, setter(into))]
162 pub(crate) url_path: Option<Cow<'a, str>>,
163}
164
165#[derive(Debug, Deserialize, Clone, Serialize)]
166pub enum LbAlgorithm {
167 #[serde(rename = "LEAST_CONNECTIONS")]
168 LeastConnections,
169 #[serde(rename = "ROUND_ROBIN")]
170 RoundRobin,
171 #[serde(rename = "SOURCE_IP")]
172 SourceIp,
173 #[serde(rename = "SOURCE_IP_PORT")]
174 SourceIpPort,
175}
176
177#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
179#[builder(setter(strip_option))]
180pub struct Members<'a> {
181 #[serde()]
182 #[builder(setter(into))]
183 pub(crate) address: Cow<'a, str>,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
186 #[builder(default, setter(into))]
187 pub(crate) admin_state_up: Option<bool>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
190 #[builder(default, setter(into))]
191 pub(crate) backup: Option<bool>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
194 #[builder(default, setter(into))]
195 pub(crate) monitor_address: Option<Cow<'a, str>>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
198 #[builder(default, setter(into))]
199 pub(crate) monitor_port: Option<i32>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
202 #[builder(default, setter(into))]
203 pub(crate) name: Option<Cow<'a, str>>,
204
205 #[serde()]
206 #[builder(setter(into))]
207 pub(crate) protocol_port: i32,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
210 #[builder(default, setter(into))]
211 pub(crate) request_sriov: Option<bool>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
214 #[builder(default, setter(into))]
215 pub(crate) subnet_id: Option<Cow<'a, str>>,
216
217 #[serde(skip_serializing_if = "Option::is_none")]
218 #[builder(default, setter(into))]
219 pub(crate) tags: Option<Vec<Cow<'a, str>>>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
222 #[builder(default, setter(into))]
223 pub(crate) weight: Option<i32>,
224}
225
226#[derive(Debug, Deserialize, Clone, Serialize)]
227pub enum Protocol {
228 #[serde(rename = "HTTP")]
229 Http,
230 #[serde(rename = "HTTPS")]
231 Https,
232 #[serde(rename = "PROXY")]
233 Proxy,
234 #[serde(rename = "PROXYV2")]
235 Proxyv2,
236 #[serde(rename = "SCTP")]
237 Sctp,
238 #[serde(rename = "TCP")]
239 Tcp,
240 #[serde(rename = "UDP")]
241 Udp,
242}
243
244#[derive(Debug, Deserialize, Clone, Serialize)]
245pub enum SessionPersistenceType {
246 #[serde(rename = "APP_COOKIE")]
247 AppCookie,
248 #[serde(rename = "HTTP_COOKIE")]
249 HttpCookie,
250 #[serde(rename = "SOURCE_IP")]
251 SourceIp,
252}
253
254#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
258#[builder(setter(strip_option))]
259pub struct SessionPersistence<'a> {
260 #[serde(skip_serializing_if = "Option::is_none")]
261 #[builder(default, setter(into))]
262 pub(crate) cookie_name: Option<Cow<'a, str>>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
265 #[builder(default, setter(into))]
266 pub(crate) persistence_granularity: Option<Cow<'a, str>>,
267
268 #[serde(skip_serializing_if = "Option::is_none")]
269 #[builder(default, setter(into))]
270 pub(crate) persistence_timeout: Option<i32>,
271
272 #[serde(rename = "type")]
273 #[builder()]
274 pub(crate) _type: SessionPersistenceType,
275}
276
277#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
279#[builder(setter(strip_option))]
280pub struct Pool<'a> {
281 #[serde(skip_serializing_if = "Option::is_none")]
284 #[builder(default, setter(into))]
285 pub(crate) admin_state_up: Option<bool>,
286
287 #[serde(skip_serializing_if = "Option::is_none")]
291 #[builder(default, setter(into))]
292 pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
293
294 #[serde(skip_serializing_if = "Option::is_none")]
301 #[builder(default, setter(into))]
302 pub(crate) ca_tls_container_ref: Option<Cow<'a, str>>,
303
304 #[serde(skip_serializing_if = "Option::is_none")]
309 #[builder(default, setter(into))]
310 pub(crate) crl_container_ref: Option<Cow<'a, str>>,
311
312 #[serde(skip_serializing_if = "Option::is_none")]
314 #[builder(default, setter(into))]
315 pub(crate) description: Option<Cow<'a, str>>,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
319 #[builder(default, setter(into))]
320 pub(crate) healthmonitor: Option<Healthmonitor<'a>>,
321
322 #[serde()]
325 #[builder()]
326 pub(crate) lb_algorithm: LbAlgorithm,
327
328 #[serde(skip_serializing_if = "Option::is_none")]
333 #[builder(default, setter(into))]
334 pub(crate) listener_id: Option<Cow<'a, str>>,
335
336 #[serde(skip_serializing_if = "Option::is_none")]
339 #[builder(default, setter(into))]
340 pub(crate) loadbalancer_id: Option<Cow<'a, str>>,
341
342 #[serde(skip_serializing_if = "Option::is_none")]
343 #[builder(default, setter(into))]
344 pub(crate) members: Option<Vec<Members<'a>>>,
345
346 #[serde(skip_serializing_if = "Option::is_none")]
348 #[builder(default, setter(into))]
349 pub(crate) name: Option<Cow<'a, str>>,
350
351 #[serde(skip_serializing_if = "Option::is_none")]
353 #[builder(default, setter(into))]
354 pub(crate) project_id: Option<Cow<'a, str>>,
355
356 #[serde()]
359 #[builder()]
360 pub(crate) protocol: Protocol,
361
362 #[serde(skip_serializing_if = "Option::is_none")]
366 #[builder(default, setter(into))]
367 pub(crate) session_persistence: Option<SessionPersistence<'a>>,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
370 #[builder(default, setter(into))]
371 pub(crate) tags: Option<Vec<Cow<'a, str>>>,
372
373 #[serde(skip_serializing_if = "Option::is_none")]
374 #[builder(default, setter(into))]
375 pub(crate) tenant_id: Option<Cow<'a, str>>,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
382 #[builder(default, setter(into))]
383 pub(crate) tls_ciphers: Option<Cow<'a, str>>,
384
385 #[serde(skip_serializing_if = "Option::is_none")]
393 #[builder(default, setter(into))]
394 pub(crate) tls_container_ref: Option<Cow<'a, str>>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
401 #[builder(default, setter(into))]
402 pub(crate) tls_enabled: Option<bool>,
403
404 #[serde(skip_serializing_if = "Option::is_none")]
409 #[builder(default, setter(into))]
410 pub(crate) tls_versions: Option<Vec<Cow<'a, str>>>,
411}
412
413#[derive(Builder, Debug, Clone)]
414#[builder(setter(strip_option))]
415pub struct Request<'a> {
416 #[builder(setter(into))]
418 pub(crate) pool: Pool<'a>,
419
420 #[builder(setter(name = "_headers"), default, private)]
421 _headers: Option<HeaderMap>,
422}
423impl<'a> Request<'a> {
424 pub fn builder() -> RequestBuilder<'a> {
426 RequestBuilder::default()
427 }
428}
429
430impl<'a> RequestBuilder<'a> {
431 pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
433 where
434 K: Into<HeaderName>,
435 V: Into<HeaderValue>,
436 {
437 self._headers
438 .get_or_insert(None)
439 .get_or_insert_with(HeaderMap::new)
440 .insert(header_name.into(), header_value.into());
441 self
442 }
443
444 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
446 where
447 I: Iterator<Item = T>,
448 T: Into<(Option<HeaderName>, HeaderValue)>,
449 {
450 self._headers
451 .get_or_insert(None)
452 .get_or_insert_with(HeaderMap::new)
453 .extend(iter.map(Into::into));
454 self
455 }
456}
457
458impl RestEndpoint for Request<'_> {
459 fn method(&self) -> http::Method {
460 http::Method::POST
461 }
462
463 fn endpoint(&self) -> Cow<'static, str> {
464 "lbaas/pools".to_string().into()
465 }
466
467 fn parameters(&self) -> QueryParams<'_> {
468 QueryParams::default()
469 }
470
471 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
472 let mut params = JsonBodyParams::default();
473
474 params.push("pool", serde_json::to_value(&self.pool)?);
475
476 params.into_body()
477 }
478
479 fn service_type(&self) -> ServiceType {
480 ServiceType::LoadBalancer
481 }
482
483 fn response_key(&self) -> Option<Cow<'static, str>> {
484 Some("pool".into())
485 }
486
487 fn request_headers(&self) -> Option<&HeaderMap> {
489 self._headers.as_ref()
490 }
491
492 fn api_version(&self) -> Option<ApiVersion> {
494 Some(ApiVersion::new(2, 0))
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501 use http::{HeaderName, HeaderValue};
502 use httpmock::MockServer;
503 #[cfg(feature = "sync")]
504 use openstack_sdk_core::api::Query;
505 use openstack_sdk_core::test::client::FakeOpenStackClient;
506 use openstack_sdk_core::types::ServiceType;
507 use serde_json::json;
508
509 #[test]
510 fn test_service_type() {
511 assert_eq!(
512 Request::builder()
513 .pool(
514 PoolBuilder::default()
515 .lb_algorithm(LbAlgorithm::LeastConnections)
516 .protocol(Protocol::Http)
517 .build()
518 .unwrap()
519 )
520 .build()
521 .unwrap()
522 .service_type(),
523 ServiceType::LoadBalancer
524 );
525 }
526
527 #[test]
528 fn test_response_key() {
529 assert_eq!(
530 Request::builder()
531 .pool(
532 PoolBuilder::default()
533 .lb_algorithm(LbAlgorithm::LeastConnections)
534 .protocol(Protocol::Http)
535 .build()
536 .unwrap()
537 )
538 .build()
539 .unwrap()
540 .response_key()
541 .unwrap(),
542 "pool"
543 );
544 }
545
546 #[cfg(feature = "sync")]
547 #[test]
548 fn endpoint() {
549 let server = MockServer::start();
550 let client = FakeOpenStackClient::new(server.base_url());
551 let mock = server.mock(|when, then| {
552 when.method(httpmock::Method::POST)
553 .path("/lbaas/pools".to_string());
554
555 then.status(200)
556 .header("content-type", "application/json")
557 .json_body(json!({ "pool": {} }));
558 });
559
560 let endpoint = Request::builder()
561 .pool(
562 PoolBuilder::default()
563 .lb_algorithm(LbAlgorithm::LeastConnections)
564 .protocol(Protocol::Http)
565 .build()
566 .unwrap(),
567 )
568 .build()
569 .unwrap();
570 let _: serde_json::Value = endpoint.query(&client).unwrap();
571 mock.assert();
572 }
573
574 #[cfg(feature = "sync")]
575 #[test]
576 fn endpoint_headers() {
577 let server = MockServer::start();
578 let client = FakeOpenStackClient::new(server.base_url());
579 let mock = server.mock(|when, then| {
580 when.method(httpmock::Method::POST)
581 .path("/lbaas/pools".to_string())
582 .header("foo", "bar")
583 .header("not_foo", "not_bar");
584 then.status(200)
585 .header("content-type", "application/json")
586 .json_body(json!({ "pool": {} }));
587 });
588
589 let endpoint = Request::builder()
590 .pool(
591 PoolBuilder::default()
592 .lb_algorithm(LbAlgorithm::LeastConnections)
593 .protocol(Protocol::Http)
594 .build()
595 .unwrap(),
596 )
597 .headers(
598 [(
599 Some(HeaderName::from_static("foo")),
600 HeaderValue::from_static("bar"),
601 )]
602 .into_iter(),
603 )
604 .header(
605 HeaderName::from_static("not_foo"),
606 HeaderValue::from_static("not_bar"),
607 )
608 .build()
609 .unwrap();
610 let _: serde_json::Value = endpoint.query(&client).unwrap();
611 mock.assert();
612 }
613}