Skip to main content

openstack_sdk_load_balancer/v2/loadbalancer/
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 load balancer.
19//!
20//! This operation provisions a new load balancer by using the configuration
21//! that you define in the request object. After the API validates the request
22//! and starts the provisioning process, the API returns a response object that
23//! contains a unique ID and the status of provisioning the load balancer.
24//!
25//! In the response, the load balancer [provisioning status](#prov-status) is
26//! `ACTIVE`, `PENDING_CREATE`, or `ERROR`.
27//!
28//! If the status is `PENDING_CREATE`, issue GET
29//! `/v2/lbaas/loadbalancers/{loadbalancer_id}` to view the progress of the
30//! provisioning operation. When the load balancer status changes to `ACTIVE`,
31//! the load balancer is successfully provisioned and is ready for further
32//! configuration.
33//!
34//! If the API cannot fulfill the request due to insufficient data or data that
35//! is not valid, the service returns the HTTP `Bad Request (400)` response
36//! code with information about the failure in the response body. Validation
37//! errors require that you correct the error and submit the request again.
38//!
39//! Administrative users can specify a project ID that is different than their
40//! own to create load balancers for other projects.
41//!
42//! An optional `flavor_id` attribute can be used to create the load balancer
43//! using a pre-configured octavia flavor. Flavors are created by the operator
44//! to allow custom load balancer configurations, such as allocating more
45//! memory for the load balancer.
46//!
47//! An optional `vip_qos_policy_id` attribute from Neutron can be used to apply
48//! QoS policies on a loadbalancer VIP, also could pass a ‘null’ value to
49//! remove QoS policies.
50//!
51//! You can also specify the `provider` attribute when you create a load
52//! balancer. The `provider` attribute specifies which backend should be used
53//! to create the load balancer. This could be the default provider (`octavia`)
54//! or a vendor supplied `provider` if one has been installed. Setting both a
55//! flavor_id and a provider will result in a conflict error if the provider
56//! does not match the provider of the configured flavor profiles.
57//!
58//! Specifying a Virtual IP (VIP) is mandatory. There are three ways to specify
59//! a VIP network for the load balancer:
60//!
61//! Additional VIPs may also be specified in the `additional_vips` field, by
62//! providing a list of JSON objects containing a `subnet_id` and optionally an
63//! `ip_address`. All additional subnets must be part of the same network as
64//! the primary VIP.
65//!
66//! An optional `vip_sg_ids` attribute can be used to set custom Neutron
67//! Security Groups that are applied on the VIP port of the Load Balancer. When
68//! this option is used, Octavia does not manage the security of the Listeners,
69//! the user must set Security Group Rules to allow the network traffic on the
70//! VIP port. `vip_sg_ids` are incompatible with SR-IOV load balancer and
71//! cannot be set if the load balancer has a listener that uses
72//! `allowed_cidrs`.
73//!
74use derive_builder::Builder;
75use http::{HeaderMap, HeaderName, HeaderValue};
76
77use openstack_sdk_core::api::rest_endpoint_prelude::*;
78
79use serde::Deserialize;
80use serde::Serialize;
81use std::borrow::Cow;
82use std::collections::BTreeMap;
83
84/// Type for additional vips
85#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
86#[builder(setter(strip_option))]
87pub struct AdditionalVips<'a> {
88    #[serde(skip_serializing_if = "Option::is_none")]
89    #[builder(default, setter(into))]
90    pub(crate) ip_address: Option<Cow<'a, str>>,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    #[builder(default, setter(into))]
94    pub(crate) port_id: Option<Cow<'a, str>>,
95
96    #[serde()]
97    #[builder(setter(into))]
98    pub(crate) subnet_id: Cow<'a, str>,
99}
100
101#[derive(Debug, Deserialize, Clone, Serialize)]
102pub enum ClientAuthentication {
103    #[serde(rename = "MANDATORY")]
104    Mandatory,
105    #[serde(rename = "NONE")]
106    None,
107    #[serde(rename = "OPTIONAL")]
108    Optional,
109}
110
111#[derive(Debug, Deserialize, Clone, Serialize)]
112pub enum HttpMethod {
113    #[serde(rename = "CONNECT")]
114    Connect,
115    #[serde(rename = "DELETE")]
116    Delete,
117    #[serde(rename = "GET")]
118    Get,
119    #[serde(rename = "HEAD")]
120    Head,
121    #[serde(rename = "OPTIONS")]
122    Options,
123    #[serde(rename = "PATCH")]
124    Patch,
125    #[serde(rename = "POST")]
126    Post,
127    #[serde(rename = "PUT")]
128    Put,
129    #[serde(rename = "TRACE")]
130    Trace,
131}
132
133#[derive(Debug, Deserialize, Clone, Serialize)]
134pub enum HealthmonitorType {
135    #[serde(rename = "HTTP")]
136    Http,
137    #[serde(rename = "HTTPS")]
138    Https,
139    #[serde(rename = "PING")]
140    Ping,
141    #[serde(rename = "SCTP")]
142    Sctp,
143    #[serde(rename = "TCP")]
144    Tcp,
145    #[serde(rename = "TLS-HELLO")]
146    TlsHello,
147    #[serde(rename = "UDP-CONNECT")]
148    UdpConnect,
149}
150
151/// Defines mandatory and optional attributes of a POST request.
152#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
153#[builder(setter(strip_option))]
154pub struct Healthmonitor<'a> {
155    #[serde(skip_serializing_if = "Option::is_none")]
156    #[builder(default, setter(into))]
157    pub(crate) admin_state_up: Option<bool>,
158
159    #[serde()]
160    #[builder(setter(into))]
161    pub(crate) delay: i32,
162
163    #[serde(skip_serializing_if = "Option::is_none")]
164    #[builder(default, setter(into))]
165    pub(crate) domain_name: Option<Cow<'a, str>>,
166
167    #[serde(skip_serializing_if = "Option::is_none")]
168    #[builder(default, setter(into))]
169    pub(crate) expected_codes: Option<Cow<'a, str>>,
170
171    #[serde(skip_serializing_if = "Option::is_none")]
172    #[builder(default)]
173    pub(crate) http_method: Option<HttpMethod>,
174
175    #[serde(skip_serializing_if = "Option::is_none")]
176    #[builder(default, setter(into))]
177    pub(crate) http_version: Option<f32>,
178
179    #[serde()]
180    #[builder(setter(into))]
181    pub(crate) max_retries: i32,
182
183    #[serde(skip_serializing_if = "Option::is_none")]
184    #[builder(default, setter(into))]
185    pub(crate) max_retries_down: Option<i32>,
186
187    #[serde(skip_serializing_if = "Option::is_none")]
188    #[builder(default, setter(into))]
189    pub(crate) name: Option<Cow<'a, str>>,
190
191    /// A list of simple strings assigned to the resource.
192    ///
193    /// **New in version 2.5**
194    #[serde(skip_serializing_if = "Option::is_none")]
195    #[builder(default, setter(into))]
196    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
197
198    #[serde()]
199    #[builder(setter(into))]
200    pub(crate) timeout: i32,
201
202    #[serde(rename = "type")]
203    #[builder()]
204    pub(crate) _type: HealthmonitorType,
205
206    #[serde(skip_serializing_if = "Option::is_none")]
207    #[builder(default, setter(into))]
208    pub(crate) url_path: Option<Cow<'a, str>>,
209}
210
211#[derive(Debug, Deserialize, Clone, Serialize)]
212pub enum LbAlgorithm {
213    #[serde(rename = "LEAST_CONNECTIONS")]
214    LeastConnections,
215    #[serde(rename = "ROUND_ROBIN")]
216    RoundRobin,
217    #[serde(rename = "SOURCE_IP")]
218    SourceIp,
219    #[serde(rename = "SOURCE_IP_PORT")]
220    SourceIpPort,
221}
222
223/// Defines mandatory and optional attributes of a POST request.
224#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
225#[builder(setter(strip_option))]
226pub struct Members<'a> {
227    #[serde()]
228    #[builder(setter(into))]
229    pub(crate) address: Cow<'a, str>,
230
231    #[serde(skip_serializing_if = "Option::is_none")]
232    #[builder(default, setter(into))]
233    pub(crate) admin_state_up: Option<bool>,
234
235    #[serde(skip_serializing_if = "Option::is_none")]
236    #[builder(default, setter(into))]
237    pub(crate) backup: Option<bool>,
238
239    #[serde(skip_serializing_if = "Option::is_none")]
240    #[builder(default, setter(into))]
241    pub(crate) monitor_address: Option<Cow<'a, str>>,
242
243    #[serde(skip_serializing_if = "Option::is_none")]
244    #[builder(default, setter(into))]
245    pub(crate) monitor_port: Option<i32>,
246
247    #[serde(skip_serializing_if = "Option::is_none")]
248    #[builder(default, setter(into))]
249    pub(crate) name: Option<Cow<'a, str>>,
250
251    #[serde()]
252    #[builder(setter(into))]
253    pub(crate) protocol_port: i32,
254
255    #[serde(skip_serializing_if = "Option::is_none")]
256    #[builder(default, setter(into))]
257    pub(crate) request_sriov: Option<bool>,
258
259    #[serde(skip_serializing_if = "Option::is_none")]
260    #[builder(default, setter(into))]
261    pub(crate) subnet_id: Option<Cow<'a, str>>,
262
263    #[serde(skip_serializing_if = "Option::is_none")]
264    #[builder(default, setter(into))]
265    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
266
267    #[serde(skip_serializing_if = "Option::is_none")]
268    #[builder(default, setter(into))]
269    pub(crate) weight: Option<i32>,
270}
271
272#[derive(Debug, Deserialize, Clone, Serialize)]
273pub enum DefaultPoolProtocol {
274    #[serde(rename = "HTTP")]
275    Http,
276    #[serde(rename = "HTTPS")]
277    Https,
278    #[serde(rename = "PROXY")]
279    Proxy,
280    #[serde(rename = "PROXYV2")]
281    Proxyv2,
282    #[serde(rename = "SCTP")]
283    Sctp,
284    #[serde(rename = "TCP")]
285    Tcp,
286    #[serde(rename = "UDP")]
287    Udp,
288}
289
290#[derive(Debug, Deserialize, Clone, Serialize)]
291pub enum SessionPersistenceType {
292    #[serde(rename = "APP_COOKIE")]
293    AppCookie,
294    #[serde(rename = "HTTP_COOKIE")]
295    HttpCookie,
296    #[serde(rename = "SOURCE_IP")]
297    SourceIp,
298}
299
300/// Defines mandatory and optional attributes of a POST request.
301#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
302#[builder(setter(strip_option))]
303pub struct SessionPersistence<'a> {
304    #[serde(skip_serializing_if = "Option::is_none")]
305    #[builder(default, setter(into))]
306    pub(crate) cookie_name: Option<Cow<'a, str>>,
307
308    #[serde(skip_serializing_if = "Option::is_none")]
309    #[builder(default, setter(into))]
310    pub(crate) persistence_granularity: Option<Cow<'a, str>>,
311
312    #[serde(skip_serializing_if = "Option::is_none")]
313    #[builder(default, setter(into))]
314    pub(crate) persistence_timeout: Option<i32>,
315
316    #[serde(rename = "type")]
317    #[builder()]
318    pub(crate) _type: SessionPersistenceType,
319}
320
321/// Defines mandatory and optional attributes of a POST request.
322#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
323#[builder(setter(strip_option))]
324pub struct DefaultPool<'a> {
325    #[serde(skip_serializing_if = "Option::is_none")]
326    #[builder(default, setter(into))]
327    pub(crate) admin_state_up: Option<bool>,
328
329    #[serde(skip_serializing_if = "Option::is_none")]
330    #[builder(default, setter(into))]
331    pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
332
333    #[serde(skip_serializing_if = "Option::is_none")]
334    #[builder(default, setter(into))]
335    pub(crate) ca_tls_container_ref: Option<Cow<'a, str>>,
336
337    #[serde(skip_serializing_if = "Option::is_none")]
338    #[builder(default, setter(into))]
339    pub(crate) crl_container_ref: Option<Cow<'a, str>>,
340
341    #[serde(skip_serializing_if = "Option::is_none")]
342    #[builder(default, setter(into))]
343    pub(crate) description: Option<Cow<'a, str>>,
344
345    /// Defines mandatory and optional attributes of a POST request.
346    #[serde(skip_serializing_if = "Option::is_none")]
347    #[builder(default, setter(into))]
348    pub(crate) healthmonitor: Option<Healthmonitor<'a>>,
349
350    #[serde(skip_serializing_if = "Option::is_none")]
351    #[builder(default)]
352    pub(crate) lb_algorithm: Option<LbAlgorithm>,
353
354    #[serde(skip_serializing_if = "Option::is_none")]
355    #[builder(default, setter(into))]
356    pub(crate) members: Option<Vec<Members<'a>>>,
357
358    #[serde(skip_serializing_if = "Option::is_none")]
359    #[builder(default, setter(into))]
360    pub(crate) name: Option<Cow<'a, str>>,
361
362    #[serde(skip_serializing_if = "Option::is_none")]
363    #[builder(default)]
364    pub(crate) protocol: Option<DefaultPoolProtocol>,
365
366    /// Defines mandatory and optional attributes of a POST request.
367    #[serde(skip_serializing_if = "Option::is_none")]
368    #[builder(default, setter(into))]
369    pub(crate) session_persistence: Option<SessionPersistence<'a>>,
370
371    #[serde(skip_serializing_if = "Option::is_none")]
372    #[builder(default, setter(into))]
373    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
374
375    #[serde(skip_serializing_if = "Option::is_none")]
376    #[builder(default, setter(into))]
377    pub(crate) tls_ciphers: Option<Cow<'a, str>>,
378
379    #[serde(skip_serializing_if = "Option::is_none")]
380    #[builder(default, setter(into))]
381    pub(crate) tls_container_ref: Option<Cow<'a, str>>,
382
383    #[serde(skip_serializing_if = "Option::is_none")]
384    #[builder(default, setter(into))]
385    pub(crate) tls_enabled: Option<bool>,
386
387    #[serde(skip_serializing_if = "Option::is_none")]
388    #[builder(default, setter(into))]
389    pub(crate) tls_versions: Option<Vec<Cow<'a, str>>>,
390}
391
392#[derive(Debug, Deserialize, Clone, Serialize)]
393pub enum Action {
394    #[serde(rename = "REDIRECT_PREFIX")]
395    RedirectPrefix,
396    #[serde(rename = "REDIRECT_TO_POOL")]
397    RedirectToPool,
398    #[serde(rename = "REDIRECT_TO_URL")]
399    RedirectToUrl,
400    #[serde(rename = "REJECT")]
401    Reject,
402}
403
404#[derive(Debug, Deserialize, Clone, Serialize)]
405pub enum RedirectPoolHealthmonitorType {
406    #[serde(rename = "HTTP")]
407    Http,
408    #[serde(rename = "HTTPS")]
409    Https,
410    #[serde(rename = "PING")]
411    Ping,
412    #[serde(rename = "SCTP")]
413    Sctp,
414    #[serde(rename = "TCP")]
415    Tcp,
416    #[serde(rename = "TLS-HELLO")]
417    TlsHello,
418    #[serde(rename = "UDP-CONNECT")]
419    UdpConnect,
420}
421
422/// Defines mandatory and optional attributes of a POST request.
423#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
424#[builder(setter(strip_option))]
425pub struct RedirectPoolHealthmonitor<'a> {
426    #[serde(skip_serializing_if = "Option::is_none")]
427    #[builder(default, setter(into))]
428    pub(crate) admin_state_up: Option<bool>,
429
430    #[serde()]
431    #[builder(setter(into))]
432    pub(crate) delay: i32,
433
434    #[serde(skip_serializing_if = "Option::is_none")]
435    #[builder(default, setter(into))]
436    pub(crate) domain_name: Option<Cow<'a, str>>,
437
438    #[serde(skip_serializing_if = "Option::is_none")]
439    #[builder(default, setter(into))]
440    pub(crate) expected_codes: Option<Cow<'a, str>>,
441
442    #[serde(skip_serializing_if = "Option::is_none")]
443    #[builder(default)]
444    pub(crate) http_method: Option<HttpMethod>,
445
446    #[serde(skip_serializing_if = "Option::is_none")]
447    #[builder(default, setter(into))]
448    pub(crate) http_version: Option<f32>,
449
450    #[serde()]
451    #[builder(setter(into))]
452    pub(crate) max_retries: i32,
453
454    #[serde(skip_serializing_if = "Option::is_none")]
455    #[builder(default, setter(into))]
456    pub(crate) max_retries_down: Option<i32>,
457
458    #[serde(skip_serializing_if = "Option::is_none")]
459    #[builder(default, setter(into))]
460    pub(crate) name: Option<Cow<'a, str>>,
461
462    #[serde(skip_serializing_if = "Option::is_none")]
463    #[builder(default, setter(into))]
464    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
465
466    #[serde()]
467    #[builder(setter(into))]
468    pub(crate) timeout: i32,
469
470    #[serde(rename = "type")]
471    #[builder()]
472    pub(crate) _type: RedirectPoolHealthmonitorType,
473
474    #[serde(skip_serializing_if = "Option::is_none")]
475    #[builder(default, setter(into))]
476    pub(crate) url_path: Option<Cow<'a, str>>,
477}
478
479#[derive(Debug, Deserialize, Clone, Serialize)]
480pub enum RedirectPoolProtocol {
481    #[serde(rename = "HTTP")]
482    Http,
483    #[serde(rename = "HTTPS")]
484    Https,
485    #[serde(rename = "PROXY")]
486    Proxy,
487    #[serde(rename = "PROXYV2")]
488    Proxyv2,
489    #[serde(rename = "SCTP")]
490    Sctp,
491    #[serde(rename = "TCP")]
492    Tcp,
493    #[serde(rename = "UDP")]
494    Udp,
495}
496
497#[derive(Debug, Deserialize, Clone, Serialize)]
498pub enum Type {
499    #[serde(rename = "APP_COOKIE")]
500    AppCookie,
501    #[serde(rename = "HTTP_COOKIE")]
502    HttpCookie,
503    #[serde(rename = "SOURCE_IP")]
504    SourceIp,
505}
506
507/// Defines mandatory and optional attributes of a POST request.
508#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
509#[builder(setter(strip_option))]
510pub struct RedirectPool<'a> {
511    #[serde(skip_serializing_if = "Option::is_none")]
512    #[builder(default, setter(into))]
513    pub(crate) admin_state_up: Option<bool>,
514
515    #[serde(skip_serializing_if = "Option::is_none")]
516    #[builder(default, setter(into))]
517    pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
518
519    #[serde(skip_serializing_if = "Option::is_none")]
520    #[builder(default, setter(into))]
521    pub(crate) ca_tls_container_ref: Option<Cow<'a, str>>,
522
523    #[serde(skip_serializing_if = "Option::is_none")]
524    #[builder(default, setter(into))]
525    pub(crate) crl_container_ref: Option<Cow<'a, str>>,
526
527    #[serde(skip_serializing_if = "Option::is_none")]
528    #[builder(default, setter(into))]
529    pub(crate) description: Option<Cow<'a, str>>,
530
531    /// Defines mandatory and optional attributes of a POST request.
532    #[serde(skip_serializing_if = "Option::is_none")]
533    #[builder(default, setter(into))]
534    pub(crate) healthmonitor: Option<RedirectPoolHealthmonitor<'a>>,
535
536    #[serde(skip_serializing_if = "Option::is_none")]
537    #[builder(default)]
538    pub(crate) lb_algorithm: Option<LbAlgorithm>,
539
540    #[serde(skip_serializing_if = "Option::is_none")]
541    #[builder(default, setter(into))]
542    pub(crate) members: Option<Vec<Members<'a>>>,
543
544    #[serde(skip_serializing_if = "Option::is_none")]
545    #[builder(default, setter(into))]
546    pub(crate) name: Option<Cow<'a, str>>,
547
548    #[serde(skip_serializing_if = "Option::is_none")]
549    #[builder(default)]
550    pub(crate) protocol: Option<RedirectPoolProtocol>,
551
552    /// Defines mandatory and optional attributes of a POST request.
553    #[serde(skip_serializing_if = "Option::is_none")]
554    #[builder(default, setter(into))]
555    pub(crate) session_persistence: Option<SessionPersistence<'a>>,
556
557    #[serde(skip_serializing_if = "Option::is_none")]
558    #[builder(default, setter(into))]
559    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
560
561    #[serde(skip_serializing_if = "Option::is_none")]
562    #[builder(default, setter(into))]
563    pub(crate) tls_ciphers: Option<Cow<'a, str>>,
564
565    #[serde(skip_serializing_if = "Option::is_none")]
566    #[builder(default, setter(into))]
567    pub(crate) tls_container_ref: Option<Cow<'a, str>>,
568
569    #[serde(skip_serializing_if = "Option::is_none")]
570    #[builder(default, setter(into))]
571    pub(crate) tls_enabled: Option<bool>,
572
573    #[serde(skip_serializing_if = "Option::is_none")]
574    #[builder(default, setter(into))]
575    pub(crate) tls_versions: Option<Vec<Cow<'a, str>>>,
576}
577
578#[derive(Debug, Deserialize, Clone, Serialize)]
579pub enum CompareType {
580    #[serde(rename = "CONTAINS")]
581    Contains,
582    #[serde(rename = "ENDS_WITH")]
583    EndsWith,
584    #[serde(rename = "EQUAL_TO")]
585    EqualTo,
586    #[serde(rename = "REGEX")]
587    Regex,
588    #[serde(rename = "STARTS_WITH")]
589    StartsWith,
590}
591
592#[derive(Debug, Deserialize, Clone, Serialize)]
593pub enum RulesType {
594    #[serde(rename = "COOKIE")]
595    Cookie,
596    #[serde(rename = "FILE_TYPE")]
597    FileType,
598    #[serde(rename = "HEADER")]
599    Header,
600    #[serde(rename = "HOST_NAME")]
601    HostName,
602    #[serde(rename = "PATH")]
603    Path,
604    #[serde(rename = "SSL_CONN_HAS_CERT")]
605    SslConnHasCert,
606    #[serde(rename = "SSL_DN_FIELD")]
607    SslDnField,
608    #[serde(rename = "SSL_VERIFY_RESULT")]
609    SslVerifyResult,
610}
611
612/// Defines mandatory and optional attributes of a POST request.
613#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
614#[builder(setter(strip_option))]
615pub struct Rules<'a> {
616    #[serde(skip_serializing_if = "Option::is_none")]
617    #[builder(default, setter(into))]
618    pub(crate) admin_state_up: Option<bool>,
619
620    #[serde()]
621    #[builder()]
622    pub(crate) compare_type: CompareType,
623
624    #[serde(skip_serializing_if = "Option::is_none")]
625    #[builder(default, setter(into))]
626    pub(crate) invert: Option<bool>,
627
628    #[serde(skip_serializing_if = "Option::is_none")]
629    #[builder(default, setter(into))]
630    pub(crate) key: Option<Cow<'a, str>>,
631
632    #[serde(skip_serializing_if = "Option::is_none")]
633    #[builder(default, setter(into))]
634    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
635
636    #[serde(rename = "type")]
637    #[builder()]
638    pub(crate) _type: RulesType,
639
640    #[serde()]
641    #[builder(setter(into))]
642    pub(crate) value: Cow<'a, str>,
643}
644
645/// Defines mandatory and optional attributes of a POST request.
646#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
647#[builder(setter(strip_option))]
648pub struct L7policies<'a> {
649    #[serde()]
650    #[builder()]
651    pub(crate) action: Action,
652
653    #[serde(skip_serializing_if = "Option::is_none")]
654    #[builder(default, setter(into))]
655    pub(crate) admin_state_up: Option<bool>,
656
657    #[serde(skip_serializing_if = "Option::is_none")]
658    #[builder(default, setter(into))]
659    pub(crate) description: Option<Cow<'a, str>>,
660
661    #[serde(skip_serializing_if = "Option::is_none")]
662    #[builder(default, setter(into))]
663    pub(crate) name: Option<Cow<'a, str>>,
664
665    #[serde(skip_serializing_if = "Option::is_none")]
666    #[builder(default, setter(into))]
667    pub(crate) position: Option<i32>,
668
669    #[serde(skip_serializing_if = "Option::is_none")]
670    #[builder(default, setter(into))]
671    pub(crate) redirect_http_code: Option<i32>,
672
673    /// Defines mandatory and optional attributes of a POST request.
674    #[serde(skip_serializing_if = "Option::is_none")]
675    #[builder(default, setter(into))]
676    pub(crate) redirect_pool: Option<RedirectPool<'a>>,
677
678    #[serde(skip_serializing_if = "Option::is_none")]
679    #[builder(default, setter(into))]
680    pub(crate) redirect_prefix: Option<Cow<'a, str>>,
681
682    #[serde(skip_serializing_if = "Option::is_none")]
683    #[builder(default, setter(into))]
684    pub(crate) redirect_url: Option<Cow<'a, str>>,
685
686    #[serde(skip_serializing_if = "Option::is_none")]
687    #[builder(default, setter(into))]
688    pub(crate) rules: Option<Vec<Rules<'a>>>,
689
690    #[serde(skip_serializing_if = "Option::is_none")]
691    #[builder(default, setter(into))]
692    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
693}
694
695#[derive(Debug, Deserialize, Clone, Serialize)]
696pub enum ListenersProtocol {
697    #[serde(rename = "HTTP")]
698    Http,
699    #[serde(rename = "HTTPS")]
700    Https,
701    #[serde(rename = "PROMETHEUS")]
702    Prometheus,
703    #[serde(rename = "SCTP")]
704    Sctp,
705    #[serde(rename = "TCP")]
706    Tcp,
707    #[serde(rename = "TERMINATED_HTTPS")]
708    TerminatedHttps,
709    #[serde(rename = "UDP")]
710    Udp,
711}
712
713/// Defines mandatory and optional attributes of a POST request.
714#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
715#[builder(setter(strip_option))]
716pub struct Listeners<'a> {
717    #[serde(skip_serializing_if = "Option::is_none")]
718    #[builder(default, setter(into))]
719    pub(crate) admin_state_up: Option<bool>,
720
721    #[serde(skip_serializing_if = "Option::is_none")]
722    #[builder(default, setter(into))]
723    pub(crate) allowed_cidrs: Option<Vec<Cow<'a, str>>>,
724
725    #[serde(skip_serializing_if = "Option::is_none")]
726    #[builder(default, setter(into))]
727    pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
728
729    #[serde(skip_serializing_if = "Option::is_none")]
730    #[builder(default)]
731    pub(crate) client_authentication: Option<ClientAuthentication>,
732
733    #[serde(skip_serializing_if = "Option::is_none")]
734    #[builder(default, setter(into))]
735    pub(crate) client_ca_tls_container_ref: Option<Cow<'a, str>>,
736
737    #[serde(skip_serializing_if = "Option::is_none")]
738    #[builder(default, setter(into))]
739    pub(crate) client_crl_container_ref: Option<Cow<'a, str>>,
740
741    #[serde(skip_serializing_if = "Option::is_none")]
742    #[builder(default, setter(into))]
743    pub(crate) connection_limit: Option<i32>,
744
745    /// Defines mandatory and optional attributes of a POST request.
746    #[serde(skip_serializing_if = "Option::is_none")]
747    #[builder(default, setter(into))]
748    pub(crate) default_pool: Option<DefaultPool<'a>>,
749
750    #[serde(skip_serializing_if = "Option::is_none")]
751    #[builder(default, setter(into))]
752    pub(crate) default_pool_id: Option<Cow<'a, str>>,
753
754    #[serde(skip_serializing_if = "Option::is_none")]
755    #[builder(default, setter(into))]
756    pub(crate) default_tls_container_ref: Option<Cow<'a, str>>,
757
758    #[serde(skip_serializing_if = "Option::is_none")]
759    #[builder(default, setter(into))]
760    pub(crate) description: Option<Cow<'a, str>>,
761
762    #[serde(skip_serializing_if = "Option::is_none")]
763    #[builder(default, setter(into))]
764    pub(crate) hsts_include_subdomains: Option<bool>,
765
766    #[serde(skip_serializing_if = "Option::is_none")]
767    #[builder(default, setter(into))]
768    pub(crate) hsts_max_age: Option<i32>,
769
770    #[serde(skip_serializing_if = "Option::is_none")]
771    #[builder(default, setter(into))]
772    pub(crate) hsts_preload: Option<bool>,
773
774    #[serde(skip_serializing_if = "Option::is_none")]
775    #[builder(default, private, setter(into, name = "_insert_headers"))]
776    pub(crate) insert_headers: Option<BTreeMap<Cow<'a, str>, Cow<'a, str>>>,
777
778    #[serde(skip_serializing_if = "Option::is_none")]
779    #[builder(default, setter(into))]
780    pub(crate) l7policies: Option<Vec<L7policies<'a>>>,
781
782    #[serde(skip_serializing_if = "Option::is_none")]
783    #[builder(default, setter(into))]
784    pub(crate) name: Option<Cow<'a, str>>,
785
786    #[serde()]
787    #[builder()]
788    pub(crate) protocol: ListenersProtocol,
789
790    #[serde()]
791    #[builder(setter(into))]
792    pub(crate) protocol_port: i32,
793
794    #[serde(skip_serializing_if = "Option::is_none")]
795    #[builder(default, setter(into))]
796    pub(crate) sni_container_refs: Option<Vec<Cow<'a, str>>>,
797
798    #[serde(skip_serializing_if = "Option::is_none")]
799    #[builder(default, setter(into))]
800    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
801
802    #[serde(skip_serializing_if = "Option::is_none")]
803    #[builder(default, setter(into))]
804    pub(crate) timeout_client_data: Option<i32>,
805
806    #[serde(skip_serializing_if = "Option::is_none")]
807    #[builder(default, setter(into))]
808    pub(crate) timeout_member_connect: Option<i32>,
809
810    #[serde(skip_serializing_if = "Option::is_none")]
811    #[builder(default, setter(into))]
812    pub(crate) timeout_member_data: Option<i32>,
813
814    #[serde(skip_serializing_if = "Option::is_none")]
815    #[builder(default, setter(into))]
816    pub(crate) timeout_tcp_inspect: Option<i32>,
817
818    #[serde(skip_serializing_if = "Option::is_none")]
819    #[builder(default, setter(into))]
820    pub(crate) tls_ciphers: Option<Cow<'a, str>>,
821
822    #[serde(skip_serializing_if = "Option::is_none")]
823    #[builder(default, setter(into))]
824    pub(crate) tls_versions: Option<Vec<Cow<'a, str>>>,
825}
826
827impl<'a> ListenersBuilder<'a> {
828    pub fn insert_headers<I, K, V>(&mut self, iter: I) -> &mut Self
829    where
830        I: Iterator<Item = (K, V)>,
831        K: Into<Cow<'a, str>>,
832        V: Into<Cow<'a, str>>,
833    {
834        self.insert_headers
835            .get_or_insert(None)
836            .get_or_insert_with(BTreeMap::new)
837            .extend(iter.map(|(k, v)| (k.into(), v.into())));
838        self
839    }
840}
841
842#[derive(Debug, Deserialize, Clone, Serialize)]
843pub enum PoolsHealthmonitorType {
844    #[serde(rename = "HTTP")]
845    Http,
846    #[serde(rename = "HTTPS")]
847    Https,
848    #[serde(rename = "PING")]
849    Ping,
850    #[serde(rename = "SCTP")]
851    Sctp,
852    #[serde(rename = "TCP")]
853    Tcp,
854    #[serde(rename = "TLS-HELLO")]
855    TlsHello,
856    #[serde(rename = "UDP-CONNECT")]
857    UdpConnect,
858}
859
860/// Defines mandatory and optional attributes of a POST request.
861#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
862#[builder(setter(strip_option))]
863pub struct PoolsHealthmonitor<'a> {
864    #[serde(skip_serializing_if = "Option::is_none")]
865    #[builder(default, setter(into))]
866    pub(crate) admin_state_up: Option<bool>,
867
868    #[serde()]
869    #[builder(setter(into))]
870    pub(crate) delay: i32,
871
872    #[serde(skip_serializing_if = "Option::is_none")]
873    #[builder(default, setter(into))]
874    pub(crate) domain_name: Option<Cow<'a, str>>,
875
876    #[serde(skip_serializing_if = "Option::is_none")]
877    #[builder(default, setter(into))]
878    pub(crate) expected_codes: Option<Cow<'a, str>>,
879
880    #[serde(skip_serializing_if = "Option::is_none")]
881    #[builder(default)]
882    pub(crate) http_method: Option<HttpMethod>,
883
884    #[serde(skip_serializing_if = "Option::is_none")]
885    #[builder(default, setter(into))]
886    pub(crate) http_version: Option<f32>,
887
888    #[serde()]
889    #[builder(setter(into))]
890    pub(crate) max_retries: i32,
891
892    #[serde(skip_serializing_if = "Option::is_none")]
893    #[builder(default, setter(into))]
894    pub(crate) max_retries_down: Option<i32>,
895
896    #[serde(skip_serializing_if = "Option::is_none")]
897    #[builder(default, setter(into))]
898    pub(crate) name: Option<Cow<'a, str>>,
899
900    #[serde(skip_serializing_if = "Option::is_none")]
901    #[builder(default, setter(into))]
902    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
903
904    #[serde()]
905    #[builder(setter(into))]
906    pub(crate) timeout: i32,
907
908    #[serde(rename = "type")]
909    #[builder()]
910    pub(crate) _type: PoolsHealthmonitorType,
911
912    #[serde(skip_serializing_if = "Option::is_none")]
913    #[builder(default, setter(into))]
914    pub(crate) url_path: Option<Cow<'a, str>>,
915}
916
917#[derive(Debug, Deserialize, Clone, Serialize)]
918pub enum PoolsProtocol {
919    #[serde(rename = "HTTP")]
920    Http,
921    #[serde(rename = "HTTPS")]
922    Https,
923    #[serde(rename = "PROXY")]
924    Proxy,
925    #[serde(rename = "PROXYV2")]
926    Proxyv2,
927    #[serde(rename = "SCTP")]
928    Sctp,
929    #[serde(rename = "TCP")]
930    Tcp,
931    #[serde(rename = "UDP")]
932    Udp,
933}
934
935/// Defines mandatory and optional attributes of a POST request.
936#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
937#[builder(setter(strip_option))]
938pub struct Pools<'a> {
939    #[serde(skip_serializing_if = "Option::is_none")]
940    #[builder(default, setter(into))]
941    pub(crate) admin_state_up: Option<bool>,
942
943    #[serde(skip_serializing_if = "Option::is_none")]
944    #[builder(default, setter(into))]
945    pub(crate) alpn_protocols: Option<Vec<Cow<'a, str>>>,
946
947    #[serde(skip_serializing_if = "Option::is_none")]
948    #[builder(default, setter(into))]
949    pub(crate) ca_tls_container_ref: Option<Cow<'a, str>>,
950
951    #[serde(skip_serializing_if = "Option::is_none")]
952    #[builder(default, setter(into))]
953    pub(crate) crl_container_ref: Option<Cow<'a, str>>,
954
955    #[serde(skip_serializing_if = "Option::is_none")]
956    #[builder(default, setter(into))]
957    pub(crate) description: Option<Cow<'a, str>>,
958
959    /// Defines mandatory and optional attributes of a POST request.
960    #[serde(skip_serializing_if = "Option::is_none")]
961    #[builder(default, setter(into))]
962    pub(crate) healthmonitor: Option<PoolsHealthmonitor<'a>>,
963
964    #[serde(skip_serializing_if = "Option::is_none")]
965    #[builder(default)]
966    pub(crate) lb_algorithm: Option<LbAlgorithm>,
967
968    #[serde(skip_serializing_if = "Option::is_none")]
969    #[builder(default, setter(into))]
970    pub(crate) members: Option<Vec<Members<'a>>>,
971
972    #[serde(skip_serializing_if = "Option::is_none")]
973    #[builder(default, setter(into))]
974    pub(crate) name: Option<Cow<'a, str>>,
975
976    #[serde(skip_serializing_if = "Option::is_none")]
977    #[builder(default)]
978    pub(crate) protocol: Option<PoolsProtocol>,
979
980    /// Defines mandatory and optional attributes of a POST request.
981    #[serde(skip_serializing_if = "Option::is_none")]
982    #[builder(default, setter(into))]
983    pub(crate) session_persistence: Option<SessionPersistence<'a>>,
984
985    #[serde(skip_serializing_if = "Option::is_none")]
986    #[builder(default, setter(into))]
987    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
988
989    #[serde(skip_serializing_if = "Option::is_none")]
990    #[builder(default, setter(into))]
991    pub(crate) tls_ciphers: Option<Cow<'a, str>>,
992
993    #[serde(skip_serializing_if = "Option::is_none")]
994    #[builder(default, setter(into))]
995    pub(crate) tls_container_ref: Option<Cow<'a, str>>,
996
997    #[serde(skip_serializing_if = "Option::is_none")]
998    #[builder(default, setter(into))]
999    pub(crate) tls_enabled: Option<bool>,
1000
1001    #[serde(skip_serializing_if = "Option::is_none")]
1002    #[builder(default, setter(into))]
1003    pub(crate) tls_versions: Option<Vec<Cow<'a, str>>>,
1004}
1005
1006/// A load balancer object.
1007#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
1008#[builder(setter(strip_option))]
1009pub struct Loadbalancer<'a> {
1010    /// A list of JSON objects defining “additional VIPs”. The format for these
1011    /// is `{"subnet_id": <subnet_id>, "ip_address": <ip_address>}`, where the
1012    /// `subnet_id` field is mandatory and the `ip_address` field is optional.
1013    /// Additional VIP subnets must all belong to the same network as the
1014    /// primary VIP.
1015    ///
1016    /// **New in version 2.26**
1017    #[serde(skip_serializing_if = "Option::is_none")]
1018    #[builder(default, setter(into))]
1019    pub(crate) additional_vips: Option<Vec<AdditionalVips<'a>>>,
1020
1021    /// The administrative state of the resource, which is up (`true`) or down
1022    /// (`false`). Default is `true`.
1023    #[serde(skip_serializing_if = "Option::is_none")]
1024    #[builder(default, setter(into))]
1025    pub(crate) admin_state_up: Option<bool>,
1026
1027    /// An availability zone name.
1028    #[serde(skip_serializing_if = "Option::is_none")]
1029    #[builder(default, setter(into))]
1030    pub(crate) availability_zone: Option<Cow<'a, str>>,
1031
1032    /// A human-readable description for the resource.
1033    #[serde(skip_serializing_if = "Option::is_none")]
1034    #[builder(default, setter(into))]
1035    pub(crate) description: Option<Cow<'a, str>>,
1036
1037    /// The ID of the flavor.
1038    #[serde(skip_serializing_if = "Option::is_none")]
1039    #[builder(default, setter(into))]
1040    pub(crate) flavor_id: Option<Cow<'a, str>>,
1041
1042    /// The associated listener IDs, if any.
1043    #[serde(skip_serializing_if = "Option::is_none")]
1044    #[builder(default, setter(into))]
1045    pub(crate) listeners: Option<Vec<Listeners<'a>>>,
1046
1047    /// Human-readable name of the resource.
1048    #[serde(skip_serializing_if = "Option::is_none")]
1049    #[builder(default, setter(into))]
1050    pub(crate) name: Option<Cow<'a, str>>,
1051
1052    #[serde(skip_serializing_if = "Option::is_none")]
1053    #[builder(default, setter(into))]
1054    pub(crate) pools: Option<Vec<Pools<'a>>>,
1055
1056    /// The ID of the project owning this resource.
1057    #[serde(skip_serializing_if = "Option::is_none")]
1058    #[builder(default, setter(into))]
1059    pub(crate) project_id: Option<Cow<'a, str>>,
1060
1061    /// Provider name for the load balancer. Default is `octavia`.
1062    #[serde(skip_serializing_if = "Option::is_none")]
1063    #[builder(default, setter(into))]
1064    pub(crate) provider: Option<Cow<'a, str>>,
1065
1066    #[serde(skip_serializing_if = "Option::is_none")]
1067    #[builder(default, setter(into))]
1068    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
1069
1070    #[serde(skip_serializing_if = "Option::is_none")]
1071    #[builder(default, setter(into))]
1072    pub(crate) tenant_id: Option<Cow<'a, str>>,
1073
1074    /// The IP address of the Virtual IP (VIP).
1075    #[serde(skip_serializing_if = "Option::is_none")]
1076    #[builder(default, setter(into))]
1077    pub(crate) vip_address: Option<Cow<'a, str>>,
1078
1079    /// The ID of the network for the Virtual IP (VIP). One of
1080    /// `vip_network_id`, `vip_port_id`, or `vip_subnet_id` must be specified.
1081    #[serde(skip_serializing_if = "Option::is_none")]
1082    #[builder(default, setter(into))]
1083    pub(crate) vip_network_id: Option<Cow<'a, str>>,
1084
1085    /// The ID of the Virtual IP (VIP) port. One of `vip_network_id`,
1086    /// `vip_port_id`, or `vip_subnet_id` must be specified.
1087    #[serde(skip_serializing_if = "Option::is_none")]
1088    #[builder(default, setter(into))]
1089    pub(crate) vip_port_id: Option<Cow<'a, str>>,
1090
1091    /// The ID of the QoS Policy which will apply to the Virtual IP (VIP).
1092    #[serde(skip_serializing_if = "Option::is_none")]
1093    #[builder(default, setter(into))]
1094    pub(crate) vip_qos_policy_id: Option<Cow<'a, str>>,
1095
1096    /// The list of Security Group IDs of the Virtual IP (VIP) port of the Load
1097    /// Balancer.
1098    ///
1099    /// **New in version 2.29**
1100    #[serde(skip_serializing_if = "Option::is_none")]
1101    #[builder(default, setter(into))]
1102    pub(crate) vip_sg_ids: Option<Vec<Cow<'a, str>>>,
1103
1104    /// The ID of the subnet for the Virtual IP (VIP). One of `vip_network_id`,
1105    /// `vip_port_id`, or `vip_subnet_id` must be specified.
1106    #[serde(skip_serializing_if = "Option::is_none")]
1107    #[builder(default, setter(into))]
1108    pub(crate) vip_subnet_id: Option<Cow<'a, str>>,
1109}
1110
1111#[derive(Builder, Debug, Clone)]
1112#[builder(setter(strip_option))]
1113pub struct Request<'a> {
1114    /// A load balancer object.
1115    #[builder(setter(into))]
1116    pub(crate) loadbalancer: Loadbalancer<'a>,
1117
1118    #[builder(setter(name = "_headers"), default, private)]
1119    _headers: Option<HeaderMap>,
1120}
1121impl<'a> Request<'a> {
1122    /// Create a builder for the endpoint.
1123    pub fn builder() -> RequestBuilder<'a> {
1124        RequestBuilder::default()
1125    }
1126}
1127
1128impl<'a> RequestBuilder<'a> {
1129    /// Add a single header to the Loadbalancer.
1130    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
1131    where
1132        K: Into<HeaderName>,
1133        V: Into<HeaderValue>,
1134    {
1135        self._headers
1136            .get_or_insert(None)
1137            .get_or_insert_with(HeaderMap::new)
1138            .insert(header_name.into(), header_value.into());
1139        self
1140    }
1141
1142    /// Add multiple headers.
1143    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
1144    where
1145        I: Iterator<Item = T>,
1146        T: Into<(Option<HeaderName>, HeaderValue)>,
1147    {
1148        self._headers
1149            .get_or_insert(None)
1150            .get_or_insert_with(HeaderMap::new)
1151            .extend(iter.map(Into::into));
1152        self
1153    }
1154}
1155
1156impl RestEndpoint for Request<'_> {
1157    fn method(&self) -> http::Method {
1158        http::Method::POST
1159    }
1160
1161    fn endpoint(&self) -> Cow<'static, str> {
1162        "lbaas/loadbalancers".to_string().into()
1163    }
1164
1165    fn parameters(&self) -> QueryParams<'_> {
1166        QueryParams::default()
1167    }
1168
1169    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
1170        let mut params = JsonBodyParams::default();
1171
1172        params.push("loadbalancer", serde_json::to_value(&self.loadbalancer)?);
1173
1174        params.into_body()
1175    }
1176
1177    fn service_type(&self) -> ServiceType {
1178        ServiceType::LoadBalancer
1179    }
1180
1181    fn response_key(&self) -> Option<Cow<'static, str>> {
1182        Some("loadbalancer".into())
1183    }
1184
1185    /// Returns headers to be set into the request
1186    fn request_headers(&self) -> Option<&HeaderMap> {
1187        self._headers.as_ref()
1188    }
1189
1190    /// Returns required API version
1191    fn api_version(&self) -> Option<ApiVersion> {
1192        Some(ApiVersion::new(2, 0))
1193    }
1194}
1195
1196#[cfg(test)]
1197mod tests {
1198    use super::*;
1199    use http::{HeaderName, HeaderValue};
1200    use httpmock::MockServer;
1201    #[cfg(feature = "sync")]
1202    use openstack_sdk_core::api::Query;
1203    use openstack_sdk_core::test::client::FakeOpenStackClient;
1204    use openstack_sdk_core::types::ServiceType;
1205    use serde_json::json;
1206
1207    #[test]
1208    fn test_service_type() {
1209        assert_eq!(
1210            Request::builder()
1211                .loadbalancer(LoadbalancerBuilder::default().build().unwrap())
1212                .build()
1213                .unwrap()
1214                .service_type(),
1215            ServiceType::LoadBalancer
1216        );
1217    }
1218
1219    #[test]
1220    fn test_response_key() {
1221        assert_eq!(
1222            Request::builder()
1223                .loadbalancer(LoadbalancerBuilder::default().build().unwrap())
1224                .build()
1225                .unwrap()
1226                .response_key()
1227                .unwrap(),
1228            "loadbalancer"
1229        );
1230    }
1231
1232    #[cfg(feature = "sync")]
1233    #[test]
1234    fn endpoint() {
1235        let server = MockServer::start();
1236        let client = FakeOpenStackClient::new(server.base_url());
1237        let mock = server.mock(|when, then| {
1238            when.method(httpmock::Method::POST)
1239                .path("/lbaas/loadbalancers".to_string());
1240
1241            then.status(200)
1242                .header("content-type", "application/json")
1243                .json_body(json!({ "loadbalancer": {} }));
1244        });
1245
1246        let endpoint = Request::builder()
1247            .loadbalancer(LoadbalancerBuilder::default().build().unwrap())
1248            .build()
1249            .unwrap();
1250        let _: serde_json::Value = endpoint.query(&client).unwrap();
1251        mock.assert();
1252    }
1253
1254    #[cfg(feature = "sync")]
1255    #[test]
1256    fn endpoint_headers() {
1257        let server = MockServer::start();
1258        let client = FakeOpenStackClient::new(server.base_url());
1259        let mock = server.mock(|when, then| {
1260            when.method(httpmock::Method::POST)
1261                .path("/lbaas/loadbalancers".to_string())
1262                .header("foo", "bar")
1263                .header("not_foo", "not_bar");
1264            then.status(200)
1265                .header("content-type", "application/json")
1266                .json_body(json!({ "loadbalancer": {} }));
1267        });
1268
1269        let endpoint = Request::builder()
1270            .loadbalancer(LoadbalancerBuilder::default().build().unwrap())
1271            .headers(
1272                [(
1273                    Some(HeaderName::from_static("foo")),
1274                    HeaderValue::from_static("bar"),
1275                )]
1276                .into_iter(),
1277            )
1278            .header(
1279                HeaderName::from_static("not_foo"),
1280                HeaderValue::from_static("not_bar"),
1281            )
1282            .build()
1283            .unwrap();
1284        let _: serde_json::Value = endpoint.query(&client).unwrap();
1285        mock.assert();
1286    }
1287}