Skip to main content

openstack_sdk_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 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/// Defines mandatory and optional attributes of a POST request.
106#[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    /// A list of simple strings assigned to the resource.
146    ///
147    /// **New in version 2.5**
148    #[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/// Defines mandatory and optional attributes of a POST request.
178#[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/// A JSON object specifying the session persistence for the pool or `null` for
255/// no session persistence. See
256/// [Pool Session Persistence](#session-persistence). Default is `null`.
257#[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/// 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<'a> RequestBuilder<'a> {
431    /// Add a single header to the Pool.
432    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    /// Add multiple headers.
445    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    /// Returns headers to be set into the request
488    fn request_headers(&self) -> Option<&HeaderMap> {
489        self._headers.as_ref()
490    }
491
492    /// Returns required API version
493    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}