Skip to main content

openstack_sdk_load_balancer/v2/healthmonitor/
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 health monitor on a pool.
19//!
20//! Health monitors define how the load balancer monitors backend servers to
21//! determine if they are available to service requests.
22//!
23//! This operation provisions a new health monitor by using the configuration
24//! that you define in the request object. After the API validates the request
25//! and starts the provisioning process, the API returns a response object that
26//! contains a unique ID and the status of provisioning the health monitor.
27//!
28//! In the response, the health monitor [provisioning status](#prov-status) is
29//! `ACTIVE`, `PENDING_CREATE`, or `ERROR`.
30//!
31//! If the status is `PENDING_CREATE`, issue GET
32//! `/v2/lbaas/healthmonitors/{healthmonitor_id}` to view the progress of the
33//! provisioning operation. When the health monitor status changes to `ACTIVE`,
34//! the health monitor is successfully provisioned and is ready for further
35//! configuration.
36//!
37//! If the API cannot fulfill the request due to insufficient data or data that
38//! is not valid, the service returns the HTTP `Bad Request (400)` response
39//! code with information about the failure in the response body. Validation
40//! errors require that you correct the error and submit the request again.
41//!
42//! Specifying a project_id is deprecated. The health monitor will inherit the
43//! project_id of the parent load balancer.
44//!
45//! At a minimum, you must specify these health monitor attributes:
46//!
47//! Some attributes receive default values if you omit them from the request:
48//!
49//! To create a health monitor, the parent load balancer must have an `ACTIVE`
50//! provisioning status.
51//!
52use derive_builder::Builder;
53use http::{HeaderMap, HeaderName, HeaderValue};
54
55use openstack_sdk_core::api::rest_endpoint_prelude::*;
56
57use serde::Deserialize;
58use serde::Serialize;
59use std::borrow::Cow;
60
61#[derive(Debug, Deserialize, Clone, Serialize)]
62pub enum HttpMethod {
63    #[serde(rename = "CONNECT")]
64    Connect,
65    #[serde(rename = "DELETE")]
66    Delete,
67    #[serde(rename = "GET")]
68    Get,
69    #[serde(rename = "HEAD")]
70    Head,
71    #[serde(rename = "OPTIONS")]
72    Options,
73    #[serde(rename = "PATCH")]
74    Patch,
75    #[serde(rename = "POST")]
76    Post,
77    #[serde(rename = "PUT")]
78    Put,
79    #[serde(rename = "TRACE")]
80    Trace,
81}
82
83#[derive(Debug, Deserialize, Clone, Serialize)]
84pub enum Type {
85    #[serde(rename = "HTTP")]
86    Http,
87    #[serde(rename = "HTTPS")]
88    Https,
89    #[serde(rename = "PING")]
90    Ping,
91    #[serde(rename = "SCTP")]
92    Sctp,
93    #[serde(rename = "TCP")]
94    Tcp,
95    #[serde(rename = "TLS-HELLO")]
96    TlsHello,
97    #[serde(rename = "UDP-CONNECT")]
98    UdpConnect,
99}
100
101/// Defines mandatory and optional attributes of a POST request.
102#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
103#[builder(setter(strip_option))]
104pub struct Healthmonitor<'a> {
105    /// The administrative state of the resource, which is up (`true`) or down
106    /// (`false`). Default is `true`.
107    #[serde(skip_serializing_if = "Option::is_none")]
108    #[builder(default, setter(into))]
109    pub(crate) admin_state_up: Option<bool>,
110
111    /// The time, in seconds, between sending probes to members.
112    #[serde()]
113    #[builder(setter(into))]
114    pub(crate) delay: i32,
115
116    /// The domain name, which be injected into the HTTP Host Header to the
117    /// backend server for HTTP health check.
118    ///
119    /// **New in version 2.10**
120    #[serde(skip_serializing_if = "Option::is_none")]
121    #[builder(default, setter(into))]
122    pub(crate) domain_name: Option<Cow<'a, str>>,
123
124    /// The list of HTTP status codes expected in response from the member to
125    /// declare it healthy. Specify one of the following values:
126    ///
127    /// - A single value, such as `200`
128    /// - A list, such as `200, 202`
129    /// - A range, such as `200-204`
130    ///
131    /// The default is 200.
132    #[serde(skip_serializing_if = "Option::is_none")]
133    #[builder(default, setter(into))]
134    pub(crate) expected_codes: Option<Cow<'a, str>>,
135
136    /// The HTTP method that the health monitor uses for requests. One of
137    /// `CONNECT`, `DELETE`, `GET`, `HEAD`, `OPTIONS`, `PATCH`, `POST`, `PUT`,
138    /// or `TRACE`. The default is `GET`.
139    #[serde(skip_serializing_if = "Option::is_none")]
140    #[builder(default)]
141    pub(crate) http_method: Option<HttpMethod>,
142
143    /// The HTTP version. One of `1.0` or `1.1`. The default is `1.0`.
144    ///
145    /// **New in version 2.10**
146    #[serde(skip_serializing_if = "Option::is_none")]
147    #[builder(default, setter(into))]
148    pub(crate) http_version: Option<f32>,
149
150    /// The number of successful checks before changing the `operating status`
151    /// of the member to `ONLINE`. A valid value is from `1` to `10`.
152    #[serde()]
153    #[builder(setter(into))]
154    pub(crate) max_retries: i32,
155
156    /// The number of allowed check failures before changing the
157    /// `operating status` of the member to `ERROR`. A valid value is from `1`
158    /// to `10`. The default is `3`.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    #[builder(default, setter(into))]
161    pub(crate) max_retries_down: Option<i32>,
162
163    /// Human-readable name of the resource.
164    #[serde(skip_serializing_if = "Option::is_none")]
165    #[builder(default, setter(into))]
166    pub(crate) name: Option<Cow<'a, str>>,
167
168    /// The ID of the pool.
169    #[serde()]
170    #[builder(setter(into))]
171    pub(crate) pool_id: Cow<'a, str>,
172
173    /// The ID of the project owning this resource. (deprecated)
174    #[serde(skip_serializing_if = "Option::is_none")]
175    #[builder(default, setter(into))]
176    pub(crate) project_id: Option<Cow<'a, str>>,
177
178    /// A list of simple strings assigned to the resource.
179    ///
180    /// **New in version 2.5**
181    #[serde(skip_serializing_if = "Option::is_none")]
182    #[builder(default, setter(into))]
183    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
184
185    #[serde(skip_serializing_if = "Option::is_none")]
186    #[builder(default, setter(into))]
187    pub(crate) tenant_id: Option<Cow<'a, str>>,
188
189    /// The maximum time, in seconds, that a monitor waits to connect before it
190    /// times out. This value must be less than the delay value.
191    #[serde()]
192    #[builder(setter(into))]
193    pub(crate) timeout: i32,
194
195    /// The type of health monitor. One of `HTTP`, `HTTPS`, `PING`, `SCTP`,
196    /// `TCP`, `TLS-HELLO`, or `UDP-CONNECT`.
197    #[serde(rename = "type")]
198    #[builder()]
199    pub(crate) _type: Type,
200
201    /// The HTTP URL path of the request sent by the monitor to test the health
202    /// of a backend member. Must be a string that begins with a forward slash
203    /// (`/`). The default URL path is `/`.
204    #[serde(skip_serializing_if = "Option::is_none")]
205    #[builder(default, setter(into))]
206    pub(crate) url_path: Option<Cow<'a, str>>,
207}
208
209#[derive(Builder, Debug, Clone)]
210#[builder(setter(strip_option))]
211pub struct Request<'a> {
212    /// Defines mandatory and optional attributes of a POST request.
213    #[builder(setter(into))]
214    pub(crate) healthmonitor: Healthmonitor<'a>,
215
216    #[builder(setter(name = "_headers"), default, private)]
217    _headers: Option<HeaderMap>,
218}
219impl<'a> Request<'a> {
220    /// Create a builder for the endpoint.
221    pub fn builder() -> RequestBuilder<'a> {
222        RequestBuilder::default()
223    }
224}
225
226impl<'a> RequestBuilder<'a> {
227    /// Add a single header to the Healthmonitor.
228    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
229    where
230        K: Into<HeaderName>,
231        V: Into<HeaderValue>,
232    {
233        self._headers
234            .get_or_insert(None)
235            .get_or_insert_with(HeaderMap::new)
236            .insert(header_name.into(), header_value.into());
237        self
238    }
239
240    /// Add multiple headers.
241    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
242    where
243        I: Iterator<Item = T>,
244        T: Into<(Option<HeaderName>, HeaderValue)>,
245    {
246        self._headers
247            .get_or_insert(None)
248            .get_or_insert_with(HeaderMap::new)
249            .extend(iter.map(Into::into));
250        self
251    }
252}
253
254impl RestEndpoint for Request<'_> {
255    fn method(&self) -> http::Method {
256        http::Method::POST
257    }
258
259    fn endpoint(&self) -> Cow<'static, str> {
260        "lbaas/healthmonitors".to_string().into()
261    }
262
263    fn parameters(&self) -> QueryParams<'_> {
264        QueryParams::default()
265    }
266
267    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
268        let mut params = JsonBodyParams::default();
269
270        params.push("healthmonitor", serde_json::to_value(&self.healthmonitor)?);
271
272        params.into_body()
273    }
274
275    fn service_type(&self) -> ServiceType {
276        ServiceType::LoadBalancer
277    }
278
279    fn response_key(&self) -> Option<Cow<'static, str>> {
280        Some("healthmonitor".into())
281    }
282
283    /// Returns headers to be set into the request
284    fn request_headers(&self) -> Option<&HeaderMap> {
285        self._headers.as_ref()
286    }
287
288    /// Returns required API version
289    fn api_version(&self) -> Option<ApiVersion> {
290        Some(ApiVersion::new(2, 0))
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297    use http::{HeaderName, HeaderValue};
298    use httpmock::MockServer;
299    #[cfg(feature = "sync")]
300    use openstack_sdk_core::api::Query;
301    use openstack_sdk_core::test::client::FakeOpenStackClient;
302    use openstack_sdk_core::types::ServiceType;
303    use serde_json::json;
304
305    #[test]
306    fn test_service_type() {
307        assert_eq!(
308            Request::builder()
309                .healthmonitor(
310                    HealthmonitorBuilder::default()
311                        ._type(Type::Http)
312                        .delay(123)
313                        .max_retries(123)
314                        .pool_id("foo")
315                        .timeout(123)
316                        .build()
317                        .unwrap()
318                )
319                .build()
320                .unwrap()
321                .service_type(),
322            ServiceType::LoadBalancer
323        );
324    }
325
326    #[test]
327    fn test_response_key() {
328        assert_eq!(
329            Request::builder()
330                .healthmonitor(
331                    HealthmonitorBuilder::default()
332                        ._type(Type::Http)
333                        .delay(123)
334                        .max_retries(123)
335                        .pool_id("foo")
336                        .timeout(123)
337                        .build()
338                        .unwrap()
339                )
340                .build()
341                .unwrap()
342                .response_key()
343                .unwrap(),
344            "healthmonitor"
345        );
346    }
347
348    #[cfg(feature = "sync")]
349    #[test]
350    fn endpoint() {
351        let server = MockServer::start();
352        let client = FakeOpenStackClient::new(server.base_url());
353        let mock = server.mock(|when, then| {
354            when.method(httpmock::Method::POST)
355                .path("/lbaas/healthmonitors".to_string());
356
357            then.status(200)
358                .header("content-type", "application/json")
359                .json_body(json!({ "healthmonitor": {} }));
360        });
361
362        let endpoint = Request::builder()
363            .healthmonitor(
364                HealthmonitorBuilder::default()
365                    ._type(Type::Http)
366                    .delay(123)
367                    .max_retries(123)
368                    .pool_id("foo")
369                    .timeout(123)
370                    .build()
371                    .unwrap(),
372            )
373            .build()
374            .unwrap();
375        let _: serde_json::Value = endpoint.query(&client).unwrap();
376        mock.assert();
377    }
378
379    #[cfg(feature = "sync")]
380    #[test]
381    fn endpoint_headers() {
382        let server = MockServer::start();
383        let client = FakeOpenStackClient::new(server.base_url());
384        let mock = server.mock(|when, then| {
385            when.method(httpmock::Method::POST)
386                .path("/lbaas/healthmonitors".to_string())
387                .header("foo", "bar")
388                .header("not_foo", "not_bar");
389            then.status(200)
390                .header("content-type", "application/json")
391                .json_body(json!({ "healthmonitor": {} }));
392        });
393
394        let endpoint = Request::builder()
395            .healthmonitor(
396                HealthmonitorBuilder::default()
397                    ._type(Type::Http)
398                    .delay(123)
399                    .max_retries(123)
400                    .pool_id("foo")
401                    .timeout(123)
402                    .build()
403                    .unwrap(),
404            )
405            .headers(
406                [(
407                    Some(HeaderName::from_static("foo")),
408                    HeaderValue::from_static("bar"),
409                )]
410                .into_iter(),
411            )
412            .header(
413                HeaderName::from_static("not_foo"),
414                HeaderValue::from_static("not_bar"),
415            )
416            .build()
417            .unwrap();
418        let _: serde_json::Value = endpoint.query(&client).unwrap();
419        mock.assert();
420    }
421}