Skip to main content

openstack_sdk_load_balancer/v2/pool/member/
replace.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//! Set the state of members for a pool in one API call. This may include
19//! creating new members, deleting old members, and updating existing members.
20//! Existing members are matched based on address/port combination.
21//!
22//! For example, assume a pool currently has two members. These members have
23//! the following address/port combinations: ‘192.0.2.15:80’ and
24//! ‘192.0.2.16:80’. Now assume a PUT request is made that includes members
25//! with address/port combinations: ‘192.0.2.16:80’ and ‘192.0.2.17:80’.
26//!
27//! The member ‘192.0.2.15:80’ will be deleted, because it was not in the
28//! request.
29//!
30//! The member ‘192.0.2.16:80’ will be updated to match the request data for
31//! that member, because it was matched.
32//!
33//! The member ‘192.0.2.17:80’ will be created, because no such member existed.
34//!
35//! The optional parameter `additive_only` when defined as `true` will skip
36//! deletions for members missing from the provided list. If this were set in
37//! the above example, the member ‘192.0.2.15:80’ would have remained in the
38//! pool.
39//!
40//! If the request is valid, the service returns the `Accepted (202)` response
41//! code. To confirm the updates, check that the member provisioning statuses
42//! are `ACTIVE` for new or updated members, and that any unspecified members
43//! were correctly deleted. If the statuses are `PENDING_UPDATE` or
44//! `PENDING_DELETE`, use GET to poll the member objects for changes.
45//!
46use derive_builder::Builder;
47use http::{HeaderMap, HeaderName, HeaderValue};
48
49use openstack_sdk_core::api::rest_endpoint_prelude::*;
50
51use serde::Deserialize;
52use serde::Serialize;
53use std::borrow::Cow;
54
55/// Defines mandatory and optional attributes of a POST request.
56#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
57#[builder(setter(strip_option))]
58pub struct Members<'a> {
59    /// The IP address of the resource.
60    #[serde()]
61    #[builder(setter(into))]
62    pub(crate) address: Cow<'a, str>,
63
64    /// The administrative state of the resource, which is up (`true`) or down
65    /// (`false`). Default is `true`.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[builder(default, setter(into))]
68    pub(crate) admin_state_up: Option<bool>,
69
70    /// Is the member a backup? Backup members only receive traffic when all
71    /// non-backup members are down.
72    ///
73    /// **New in version 2.1**
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[builder(default, setter(into))]
76    pub(crate) backup: Option<bool>,
77
78    /// An alternate IP address used for health monitoring a backend member.
79    /// Default is `null` which monitors the member `address`.
80    #[serde(skip_serializing_if = "Option::is_none")]
81    #[builder(default, setter(into))]
82    pub(crate) monitor_address: Option<Cow<'a, str>>,
83
84    /// An alternate protocol port used for health monitoring a backend member.
85    /// Default is `null` which monitors the member `protocol_port`.
86    #[serde(skip_serializing_if = "Option::is_none")]
87    #[builder(default, setter(into))]
88    pub(crate) monitor_port: Option<i32>,
89
90    /// Human-readable name of the resource.
91    #[serde(skip_serializing_if = "Option::is_none")]
92    #[builder(default, setter(into))]
93    pub(crate) name: Option<Cow<'a, str>>,
94
95    /// The ID of the project owning this resource. (deprecated)
96    #[serde(skip_serializing_if = "Option::is_none")]
97    #[builder(default, setter(into))]
98    pub(crate) project_id: Option<Cow<'a, str>>,
99
100    /// The protocol port number for the resource.
101    #[serde()]
102    #[builder(setter(into))]
103    pub(crate) protocol_port: i32,
104
105    /// Request that an SR-IOV VF be used for the member network port. Defaults
106    /// to `false`.
107    ///
108    /// **New in version 2.29**
109    #[serde(skip_serializing_if = "Option::is_none")]
110    #[builder(default, setter(into))]
111    pub(crate) request_sriov: Option<bool>,
112
113    /// The subnet ID the member service is accessible from.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    #[builder(default, setter(into))]
116    pub(crate) subnet_id: Option<Cow<'a, str>>,
117
118    /// A list of simple strings assigned to the resource.
119    ///
120    /// **New in version 2.5**
121    #[serde(skip_serializing_if = "Option::is_none")]
122    #[builder(default, setter(into))]
123    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
124
125    #[serde(skip_serializing_if = "Option::is_none")]
126    #[builder(default, setter(into))]
127    pub(crate) tenant_id: Option<Cow<'a, str>>,
128
129    /// The weight of a member determines the portion of requests or
130    /// connections it services compared to the other members of the pool. For
131    /// example, a member with a weight of 10 receives five times as many
132    /// requests as a member with a weight of 2. A value of 0 means the member
133    /// does not receive new connections but continues to service existing
134    /// connections. A valid value is from `0` to `256`. Default is `1`.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    #[builder(default, setter(into))]
137    pub(crate) weight: Option<i32>,
138}
139
140#[derive(Builder, Debug, Clone)]
141#[builder(setter(strip_option))]
142pub struct Request<'a> {
143    #[builder(setter(into))]
144    pub(crate) members: Vec<Members<'a>>,
145
146    /// pool_id parameter for /v2/lbaas/pools/{pool_id}/members/{member_id} API
147    #[builder(default, setter(into))]
148    pool_id: Cow<'a, str>,
149
150    #[builder(setter(name = "_headers"), default, private)]
151    _headers: Option<HeaderMap>,
152}
153impl<'a> Request<'a> {
154    /// Create a builder for the endpoint.
155    pub fn builder() -> RequestBuilder<'a> {
156        RequestBuilder::default()
157    }
158}
159
160impl<'a> RequestBuilder<'a> {
161    /// Add a single header to the Member.
162    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
163    where
164        K: Into<HeaderName>,
165        V: Into<HeaderValue>,
166    {
167        self._headers
168            .get_or_insert(None)
169            .get_or_insert_with(HeaderMap::new)
170            .insert(header_name.into(), header_value.into());
171        self
172    }
173
174    /// Add multiple headers.
175    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
176    where
177        I: Iterator<Item = T>,
178        T: Into<(Option<HeaderName>, HeaderValue)>,
179    {
180        self._headers
181            .get_or_insert(None)
182            .get_or_insert_with(HeaderMap::new)
183            .extend(iter.map(Into::into));
184        self
185    }
186}
187
188impl RestEndpoint for Request<'_> {
189    fn method(&self) -> http::Method {
190        http::Method::PUT
191    }
192
193    fn endpoint(&self) -> Cow<'static, str> {
194        format!(
195            "lbaas/pools/{pool_id}/members",
196            pool_id = self.pool_id.as_ref(),
197        )
198        .into()
199    }
200
201    fn parameters(&self) -> QueryParams<'_> {
202        QueryParams::default()
203    }
204
205    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
206        let mut params = JsonBodyParams::default();
207
208        params.push("members", serde_json::to_value(&self.members)?);
209
210        params.into_body()
211    }
212
213    fn service_type(&self) -> ServiceType {
214        ServiceType::LoadBalancer
215    }
216
217    fn response_key(&self) -> Option<Cow<'static, str>> {
218        None
219    }
220
221    /// Returns headers to be set into the request
222    fn request_headers(&self) -> Option<&HeaderMap> {
223        self._headers.as_ref()
224    }
225
226    /// Returns required API version
227    fn api_version(&self) -> Option<ApiVersion> {
228        Some(ApiVersion::new(2, 0))
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235    use http::{HeaderName, HeaderValue};
236    use httpmock::MockServer;
237    #[cfg(feature = "sync")]
238    use openstack_sdk_core::api::Query;
239    use openstack_sdk_core::test::client::FakeOpenStackClient;
240    use openstack_sdk_core::types::ServiceType;
241    use serde_json::json;
242
243    #[test]
244    fn test_service_type() {
245        assert_eq!(
246            Request::builder()
247                .members(Vec::from([MembersBuilder::default()
248                    .address("foo")
249                    .protocol_port(123)
250                    .build()
251                    .unwrap()]))
252                .build()
253                .unwrap()
254                .service_type(),
255            ServiceType::LoadBalancer
256        );
257    }
258
259    #[test]
260    fn test_response_key() {
261        assert!(
262            Request::builder()
263                .members(Vec::from([MembersBuilder::default()
264                    .address("foo")
265                    .protocol_port(123)
266                    .build()
267                    .unwrap()]))
268                .build()
269                .unwrap()
270                .response_key()
271                .is_none()
272        )
273    }
274
275    #[cfg(feature = "sync")]
276    #[test]
277    fn endpoint() {
278        let server = MockServer::start();
279        let client = FakeOpenStackClient::new(server.base_url());
280        let mock = server.mock(|when, then| {
281            when.method(httpmock::Method::PUT).path(format!(
282                "/lbaas/pools/{pool_id}/members",
283                pool_id = "pool_id",
284            ));
285
286            then.status(200)
287                .header("content-type", "application/json")
288                .json_body(json!({ "dummy": {} }));
289        });
290
291        let endpoint = Request::builder()
292            .pool_id("pool_id")
293            .members(Vec::from([MembersBuilder::default()
294                .address("foo")
295                .protocol_port(123)
296                .build()
297                .unwrap()]))
298            .build()
299            .unwrap();
300        let _: serde_json::Value = endpoint.query(&client).unwrap();
301        mock.assert();
302    }
303
304    #[cfg(feature = "sync")]
305    #[test]
306    fn endpoint_headers() {
307        let server = MockServer::start();
308        let client = FakeOpenStackClient::new(server.base_url());
309        let mock = server.mock(|when, then| {
310            when.method(httpmock::Method::PUT)
311                .path(format!(
312                    "/lbaas/pools/{pool_id}/members",
313                    pool_id = "pool_id",
314                ))
315                .header("foo", "bar")
316                .header("not_foo", "not_bar");
317            then.status(200)
318                .header("content-type", "application/json")
319                .json_body(json!({ "dummy": {} }));
320        });
321
322        let endpoint = Request::builder()
323            .pool_id("pool_id")
324            .members(Vec::from([MembersBuilder::default()
325                .address("foo")
326                .protocol_port(123)
327                .build()
328                .unwrap()]))
329            .headers(
330                [(
331                    Some(HeaderName::from_static("foo")),
332                    HeaderValue::from_static("bar"),
333                )]
334                .into_iter(),
335            )
336            .header(
337                HeaderName::from_static("not_foo"),
338                HeaderValue::from_static("not_bar"),
339            )
340            .build()
341            .unwrap();
342        let _: serde_json::Value = endpoint.query(&client).unwrap();
343        mock.assert();
344    }
345}