openstack_sdk/api/load_balancer/v2/pool/
create.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Creates a pool for a load balancer.
19//!
20//! The pool defines how requests should be balanced across the backend member
21//! servers.
22//!
23//! This operation provisions a pool by using the configuration that you define
24//! in the request object. After the API validates the request and starts the
25//! provisioning process, the API returns a response object, which contains a
26//! unique ID.
27//!
28//! In the response, the pool [provisioning status](#prov-status) is `ACTIVE`,
29//! `PENDING_CREATE`, or `ERROR`.
30//!
31//! If the status is `PENDING_CREATE`, issue GET `/v2/lbaas/pools/{pool_id}` to
32//! view the progress of the provisioning operation. When the pool status
33//! changes to `ACTIVE`, the pool is successfully provisioned and is ready for
34//! further configuration.
35//!
36//! At a minimum, you must specify these pool attributes:
37//!
38//! Some attributes receive default values if you omit them from the request:
39//!
40//! If the API cannot fulfill the request due to insufficient data or data that
41//! is not valid, the service returns the HTTP `Bad Request (400)` response
42//! code with information about the failure in the response body. Validation
43//! errors require that you correct the error and submit the request again.
44//!
45//! Specifying a project_id is deprecated. The pool will inherit the project_id
46//! of the parent load balancer.
47//!
48//! You can configure all documented features of the pool at creation time by
49//! specifying the additional elements or attributes in the request.
50//!
51//! To create a pool, the parent load balancer must have an `ACTIVE`
52//! provisioning status.
53//!
54//! `SOURCE_IP_PORT` algorithm is available from version 2.13.
55//!
56use derive_builder::Builder;
57use http::{HeaderMap, HeaderName, HeaderValue};
58
59use crate::api::rest_endpoint_prelude::*;
60
61use serde::Deserialize;
62use serde::Serialize;
63use std::borrow::Cow;
64
65#[derive(Debug, Deserialize, Clone, Serialize)]
66pub enum Protocol {
67    #[serde(rename = "HTTP")]
68    Http,
69    #[serde(rename = "HTTPS")]
70    Https,
71    #[serde(rename = "PROXY")]
72    Proxy,
73    #[serde(rename = "PROXYV2")]
74    Proxyv2,
75    #[serde(rename = "SCTP")]
76    Sctp,
77    #[serde(rename = "TCP")]
78    Tcp,
79    #[serde(rename = "UDP")]
80    Udp,
81}
82
83#[derive(Debug, Deserialize, Clone, Serialize)]
84pub enum LbAlgorithm {
85    #[serde(rename = "LEAST_CONNECTIONS")]
86    LeastConnections,
87    #[serde(rename = "ROUND_ROBIN")]
88    RoundRobin,
89    #[serde(rename = "SOURCE_IP")]
90    SourceIp,
91    #[serde(rename = "SOURCE_IP_PORT")]
92    SourceIpPort,
93}
94
95#[derive(Debug, Deserialize, Clone, Serialize)]
96pub enum Type {
97    #[serde(rename = "APP_COOKIE")]
98    AppCookie,
99    #[serde(rename = "HTTP_COOKIE")]
100    HttpCookie,
101    #[serde(rename = "SOURCE_IP")]
102    SourceIp,
103}
104
105/// A JSON object specifying the session persistence for the pool or `null` for
106/// no session persistence. See
107/// [Pool Session Persistence](#session-persistence). Default is `null`.
108#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
109#[builder(setter(strip_option))]
110pub struct SessionPersistence<'a> {
111    #[serde(skip_serializing_if = "Option::is_none")]
112    #[builder(default, setter(into))]
113    pub(crate) cookie_name: Option<Cow<'a, str>>,
114
115    #[serde(skip_serializing_if = "Option::is_none")]
116    #[builder(default, setter(into))]
117    pub(crate) persistence_granularity: Option<Cow<'a, str>>,
118
119    #[serde(skip_serializing_if = "Option::is_none")]
120    #[builder(default, setter(into))]
121    pub(crate) persistence_timeout: Option<i32>,
122
123    #[serde(rename = "type")]
124    #[builder()]
125    pub(crate) _type: Type,
126}
127
128#[derive(Debug, Deserialize, Clone, Serialize)]
129pub enum HealthmonitorType {
130    #[serde(rename = "HTTP")]
131    Http,
132    #[serde(rename = "HTTPS")]
133    Https,
134    #[serde(rename = "PING")]
135    Ping,
136    #[serde(rename = "SCTP")]
137    Sctp,
138    #[serde(rename = "TCP")]
139    Tcp,
140    #[serde(rename = "TLS-HELLO")]
141    TlsHello,
142    #[serde(rename = "UDP-CONNECT")]
143    UdpConnect,
144}
145
146#[derive(Debug, Deserialize, Clone, Serialize)]
147pub enum HttpMethod {
148    #[serde(rename = "CONNECT")]
149    Connect,
150    #[serde(rename = "DELETE")]
151    Delete,
152    #[serde(rename = "GET")]
153    Get,
154    #[serde(rename = "HEAD")]
155    Head,
156    #[serde(rename = "OPTIONS")]
157    Options,
158    #[serde(rename = "PATCH")]
159    Patch,
160    #[serde(rename = "POST")]
161    Post,
162    #[serde(rename = "PUT")]
163    Put,
164    #[serde(rename = "TRACE")]
165    Trace,
166}
167
168/// Defines mandatory and optional attributes of a POST request.
169#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
170#[builder(setter(strip_option))]
171pub struct Healthmonitor<'a> {
172    #[serde(skip_serializing_if = "Option::is_none")]
173    #[builder(default, setter(into))]
174    pub(crate) admin_state_up: Option<bool>,
175
176    #[serde()]
177    #[builder(setter(into))]
178    pub(crate) delay: i32,
179
180    #[serde(skip_serializing_if = "Option::is_none")]
181    #[builder(default, setter(into))]
182    pub(crate) domain_name: Option<Cow<'a, str>>,
183
184    #[serde(skip_serializing_if = "Option::is_none")]
185    #[builder(default, setter(into))]
186    pub(crate) expected_codes: Option<Cow<'a, str>>,
187
188    #[serde(skip_serializing_if = "Option::is_none")]
189    #[builder(default)]
190    pub(crate) http_method: Option<HttpMethod>,
191
192    #[serde(skip_serializing_if = "Option::is_none")]
193    #[builder(default, setter(into))]
194    pub(crate) http_version: Option<f32>,
195
196    #[serde()]
197    #[builder(setter(into))]
198    pub(crate) max_retries: i32,
199
200    #[serde(skip_serializing_if = "Option::is_none")]
201    #[builder(default, setter(into))]
202    pub(crate) max_retries_down: Option<i32>,
203
204    #[serde(skip_serializing_if = "Option::is_none")]
205    #[builder(default, setter(into))]
206    pub(crate) name: Option<Cow<'a, str>>,
207
208    /// A list of simple strings assigned to the resource.
209    ///
210    /// **New in version 2.5**
211    #[serde(skip_serializing_if = "Option::is_none")]
212    #[builder(default, setter(into))]
213    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
214
215    #[serde()]
216    #[builder(setter(into))]
217    pub(crate) timeout: i32,
218
219    #[serde(rename = "type")]
220    #[builder()]
221    pub(crate) _type: HealthmonitorType,
222
223    #[serde(skip_serializing_if = "Option::is_none")]
224    #[builder(default, setter(into))]
225    pub(crate) url_path: Option<Cow<'a, str>>,
226}
227
228/// Defines mandatory and optional attributes of a POST request.
229#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
230#[builder(setter(strip_option))]
231pub struct Members<'a> {
232    #[serde()]
233    #[builder(setter(into))]
234    pub(crate) address: Cow<'a, str>,
235
236    #[serde(skip_serializing_if = "Option::is_none")]
237    #[builder(default, setter(into))]
238    pub(crate) admin_state_up: Option<bool>,
239
240    #[serde(skip_serializing_if = "Option::is_none")]
241    #[builder(default, setter(into))]
242    pub(crate) backup: Option<bool>,
243
244    #[serde(skip_serializing_if = "Option::is_none")]
245    #[builder(default, setter(into))]
246    pub(crate) monitor_address: Option<Cow<'a, str>>,
247
248    #[serde(skip_serializing_if = "Option::is_none")]
249    #[builder(default, setter(into))]
250    pub(crate) monitor_port: Option<i32>,
251
252    #[serde(skip_serializing_if = "Option::is_none")]
253    #[builder(default, setter(into))]
254    pub(crate) name: Option<Cow<'a, str>>,
255
256    #[serde()]
257    #[builder(setter(into))]
258    pub(crate) protocol_port: i32,
259
260    #[serde(skip_serializing_if = "Option::is_none")]
261    #[builder(default, setter(into))]
262    pub(crate) request_sriov: Option<bool>,
263
264    #[serde(skip_serializing_if = "Option::is_none")]
265    #[builder(default, setter(into))]
266    pub(crate) subnet_id: Option<Cow<'a, str>>,
267
268    #[serde(skip_serializing_if = "Option::is_none")]
269    #[builder(default, setter(into))]
270    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
271
272    #[serde(skip_serializing_if = "Option::is_none")]
273    #[builder(default, setter(into))]
274    pub(crate) weight: Option<i32>,
275}
276
277/// Defines mandatory and optional attributes of a POST request.
278#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
279#[builder(setter(strip_option))]
280pub struct Pool<'a> {
281    /// The administrative state of the resource, which is up (`true`) or down
282    /// (`false`). Default is `true`.
283    #[serde(skip_serializing_if = "Option::is_none")]
284    #[builder(default, setter(into))]
285    pub(crate) admin_state_up: Option<bool>,
286
287    /// A list of ALPN protocols. Available protocols: http/1.0, http/1.1, h2
288    ///
289    /// **New in version 2.24**
290    #[serde(skip_serializing_if = "Option::is_none")]
291    #[builder(default, setter(into))]
292    pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
293
294    /// The reference of the
295    /// [key manager service](https://docs.openstack.org/castellan/latest/)
296    /// secret containing a PEM format CA certificate bundle for `tls_enabled`
297    /// pools.
298    ///
299    /// **New in version 2.8**
300    #[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    /// The reference of the
305    /// [key manager service](https://docs.openstack.org/castellan/latest/)
306    /// secret containing a PEM format CA revocation list file for
307    /// `tls_enabled` pools.
308    #[serde(skip_serializing_if = "Option::is_none")]
309    #[builder(default, setter(into))]
310    pub(crate) crl_container_ref: Option<Cow<'a, str>>,
311
312    /// A human-readable description for the resource.
313    #[serde(skip_serializing_if = "Option::is_none")]
314    #[builder(default, setter(into))]
315    pub(crate) description: Option<Cow<'a, str>>,
316
317    /// Defines mandatory and optional attributes of a POST request.
318    #[serde(skip_serializing_if = "Option::is_none")]
319    #[builder(default, setter(into))]
320    pub(crate) healthmonitor: Option<Healthmonitor<'a>>,
321
322    /// The load balancing algorithm for the pool. One of `LEAST_CONNECTIONS`,
323    /// `ROUND_ROBIN`, `SOURCE_IP`, or `SOURCE_IP_PORT`.
324    #[serde()]
325    #[builder()]
326    pub(crate) lb_algorithm: LbAlgorithm,
327
328    /// The ID of the listener for the pool. Either `listener_id` or
329    /// `loadbalancer_id` must be specified. The listener has some
330    /// restrictions, See
331    /// [Protocol Combinations (Listener/Pool)](#valid-protocol).
332    #[serde(skip_serializing_if = "Option::is_none")]
333    #[builder(default, setter(into))]
334    pub(crate) listener_id: Option<Cow<'a, str>>,
335
336    /// The ID of the load balancer for the pool. Either `listener_id` or
337    /// `loadbalancer_id` must be specified.
338    #[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    /// Human-readable name of the resource.
347    #[serde(skip_serializing_if = "Option::is_none")]
348    #[builder(default, setter(into))]
349    pub(crate) name: Option<Cow<'a, str>>,
350
351    /// The ID of the project owning this resource. (deprecated)
352    #[serde(skip_serializing_if = "Option::is_none")]
353    #[builder(default, setter(into))]
354    pub(crate) project_id: Option<Cow<'a, str>>,
355
356    /// The protocol for the resource. One of `HTTP`, `HTTPS`, `PROXY`,
357    /// `PROXYV2`, `SCTP`, `TCP`, or `UDP`.
358    #[serde()]
359    #[builder()]
360    pub(crate) protocol: Protocol,
361
362    /// A JSON object specifying the session persistence for the pool or `null`
363    /// for no session persistence. See
364    /// [Pool Session Persistence](#session-persistence). Default is `null`.
365    #[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    /// List of ciphers in OpenSSL format (colon-separated). See
378    /// <https://www.openssl.org/docs/man1.1.1/man1/ciphers.html>
379    ///
380    /// **New in version 2.15**
381    #[serde(skip_serializing_if = "Option::is_none")]
382    #[builder(default, setter(into))]
383    pub(crate) tls_ciphers: Option<Cow<'a, str>>,
384
385    /// The reference to the
386    /// [key manager service](https://docs.openstack.org/castellan/latest/)
387    /// secret containing a PKCS12 format certificate/key bundle for
388    /// `tls_enabled` pools for TLS client authentication to the member
389    /// servers.
390    ///
391    /// **New in version 2.8**
392    #[serde(skip_serializing_if = "Option::is_none")]
393    #[builder(default, setter(into))]
394    pub(crate) tls_container_ref: Option<Cow<'a, str>>,
395
396    /// When `true` connections to backend member servers will use TLS
397    /// encryption. Default is `false`.
398    ///
399    /// **New in version 2.8**
400    #[serde(skip_serializing_if = "Option::is_none")]
401    #[builder(default, setter(into))]
402    pub(crate) tls_enabled: Option<bool>,
403
404    /// A list of TLS protocol versions. Available versions: SSLv3, TLSv1,
405    /// TLSv1.1, TLSv1.2, TLSv1.3
406    ///
407    /// **New in version 2.17**
408    #[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    /// Defines mandatory and optional attributes of a POST request.
417    #[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    /// Create a builder for the endpoint.
425    pub fn builder() -> RequestBuilder<'a> {
426        RequestBuilder::default()
427    }
428}
429
430impl RequestBuilder<'_> {
431    /// Add a single header to the Pool.
432    pub fn header(&mut self, header_name: &'static str, header_value: &'static str) -> &mut Self
433where {
434        self._headers
435            .get_or_insert(None)
436            .get_or_insert_with(HeaderMap::new)
437            .insert(header_name, HeaderValue::from_static(header_value));
438        self
439    }
440
441    /// Add multiple headers.
442    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
443    where
444        I: Iterator<Item = T>,
445        T: Into<(Option<HeaderName>, HeaderValue)>,
446    {
447        self._headers
448            .get_or_insert(None)
449            .get_or_insert_with(HeaderMap::new)
450            .extend(iter.map(Into::into));
451        self
452    }
453}
454
455impl RestEndpoint for Request<'_> {
456    fn method(&self) -> http::Method {
457        http::Method::POST
458    }
459
460    fn endpoint(&self) -> Cow<'static, str> {
461        "lbaas/pools".to_string().into()
462    }
463
464    fn parameters(&self) -> QueryParams {
465        QueryParams::default()
466    }
467
468    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
469        let mut params = JsonBodyParams::default();
470
471        params.push("pool", serde_json::to_value(&self.pool)?);
472
473        params.into_body()
474    }
475
476    fn service_type(&self) -> ServiceType {
477        ServiceType::LoadBalancer
478    }
479
480    fn response_key(&self) -> Option<Cow<'static, str>> {
481        Some("pool".into())
482    }
483
484    /// Returns headers to be set into the request
485    fn request_headers(&self) -> Option<&HeaderMap> {
486        self._headers.as_ref()
487    }
488
489    /// Returns required API version
490    fn api_version(&self) -> Option<ApiVersion> {
491        Some(ApiVersion::new(2, 0))
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498    #[cfg(feature = "sync")]
499    use crate::api::Query;
500    use crate::test::client::FakeOpenStackClient;
501    use crate::types::ServiceType;
502    use http::{HeaderName, HeaderValue};
503    use httpmock::MockServer;
504    use serde_json::json;
505
506    #[test]
507    fn test_service_type() {
508        assert_eq!(
509            Request::builder()
510                .pool(
511                    PoolBuilder::default()
512                        .lb_algorithm(LbAlgorithm::LeastConnections)
513                        .protocol(Protocol::Http)
514                        .build()
515                        .unwrap()
516                )
517                .build()
518                .unwrap()
519                .service_type(),
520            ServiceType::LoadBalancer
521        );
522    }
523
524    #[test]
525    fn test_response_key() {
526        assert_eq!(
527            Request::builder()
528                .pool(
529                    PoolBuilder::default()
530                        .lb_algorithm(LbAlgorithm::LeastConnections)
531                        .protocol(Protocol::Http)
532                        .build()
533                        .unwrap()
534                )
535                .build()
536                .unwrap()
537                .response_key()
538                .unwrap(),
539            "pool"
540        );
541    }
542
543    #[cfg(feature = "sync")]
544    #[test]
545    fn endpoint() {
546        let server = MockServer::start();
547        let client = FakeOpenStackClient::new(server.base_url());
548        let mock = server.mock(|when, then| {
549            when.method(httpmock::Method::POST)
550                .path("/lbaas/pools".to_string());
551
552            then.status(200)
553                .header("content-type", "application/json")
554                .json_body(json!({ "pool": {} }));
555        });
556
557        let endpoint = Request::builder()
558            .pool(
559                PoolBuilder::default()
560                    .lb_algorithm(LbAlgorithm::LeastConnections)
561                    .protocol(Protocol::Http)
562                    .build()
563                    .unwrap(),
564            )
565            .build()
566            .unwrap();
567        let _: serde_json::Value = endpoint.query(&client).unwrap();
568        mock.assert();
569    }
570
571    #[cfg(feature = "sync")]
572    #[test]
573    fn endpoint_headers() {
574        let server = MockServer::start();
575        let client = FakeOpenStackClient::new(server.base_url());
576        let mock = server.mock(|when, then| {
577            when.method(httpmock::Method::POST)
578                .path("/lbaas/pools".to_string())
579                .header("foo", "bar")
580                .header("not_foo", "not_bar");
581            then.status(200)
582                .header("content-type", "application/json")
583                .json_body(json!({ "pool": {} }));
584        });
585
586        let endpoint = Request::builder()
587            .pool(
588                PoolBuilder::default()
589                    .lb_algorithm(LbAlgorithm::LeastConnections)
590                    .protocol(Protocol::Http)
591                    .build()
592                    .unwrap(),
593            )
594            .headers(
595                [(
596                    Some(HeaderName::from_static("foo")),
597                    HeaderValue::from_static("bar"),
598                )]
599                .into_iter(),
600            )
601            .header("not_foo", "not_bar")
602            .build()
603            .unwrap();
604        let _: serde_json::Value = endpoint.query(&client).unwrap();
605        mock.assert();
606    }
607}