openstack_sdk/api/compute/v2/server/create_257.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 server.
19//!
20//! The progress of this operation depends on the location of the requested
21//! image, network I/O, host load, selected flavor, and other factors.
22//!
23//! To check the progress of the request, make a `GET /servers/{id}` request.
24//! This call returns a progress attribute, which is a percentage value from 0
25//! to 100.
26//!
27//! The `Location` header returns the full URL to the newly created server and
28//! is available as a `self` and `bookmark` link in the server representation.
29//!
30//! When you create a server, the response shows only the server ID, its links,
31//! and the admin password. You can get additional attributes through
32//! subsequent `GET` requests on the server.
33//!
34//! Include the `block_device_mapping_v2` parameter in the create request body
35//! to boot a server from a volume.
36//!
37//! Include the `key_name` parameter in the create request body to add a
38//! keypair to the server when you create it. To create a keypair, make a
39//! [create keypair](https://docs.openstack.org/api-ref/compute/#create-or-import-keypair)
40//! request.
41//!
42//! **Preconditions**
43//!
44//! **Asynchronous postconditions**
45//!
46//! **Troubleshooting**
47//!
48//! Normal response codes: 202
49//!
50//! Error response codes: badRequest(400), unauthorized(401), forbidden(403),
51//! itemNotFound(404), conflict(409)
52//!
53use derive_builder::Builder;
54use http::{HeaderMap, HeaderName, HeaderValue};
55
56use crate::api::rest_endpoint_prelude::*;
57
58use serde::Deserialize;
59use serde::Serialize;
60use serde_json::Value;
61use std::borrow::Cow;
62use std::collections::BTreeMap;
63
64#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
65#[builder(setter(strip_option))]
66pub struct Networks<'a> {
67 #[serde(skip_serializing_if = "Option::is_none")]
68 #[builder(default, setter(into))]
69 pub(crate) fixed_ip: Option<Cow<'a, str>>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
72 #[builder(default, setter(into))]
73 pub(crate) port: Option<Option<Cow<'a, str>>>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
76 #[builder(default, setter(into))]
77 pub(crate) tag: Option<Cow<'a, str>>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
80 #[builder(default, setter(into))]
81 pub(crate) uuid: Option<Cow<'a, str>>,
82}
83
84#[derive(Debug, Deserialize, Clone, Serialize)]
85pub enum NetworksStringEnum {
86 #[serde(rename = "auto")]
87 Auto,
88 #[serde(rename = "none")]
89 None,
90}
91
92#[derive(Debug, Deserialize, Clone, Serialize)]
93#[serde(untagged)]
94pub enum ServerNetworks<'a> {
95 F1(Vec<Networks<'a>>),
96 F2(NetworksStringEnum),
97}
98
99#[derive(Debug, Deserialize, Clone, Serialize)]
100pub enum OsDcfDiskConfig {
101 #[serde(rename = "AUTO")]
102 Auto,
103 #[serde(rename = "MANUAL")]
104 Manual,
105}
106
107#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
108#[builder(setter(strip_option))]
109pub struct BlockDeviceMapping<'a> {
110 #[serde(skip_serializing_if = "Option::is_none")]
111 #[builder(default, setter(into))]
112 pub(crate) connection_info: Option<Cow<'a, str>>,
113
114 /// Indicates whether a config drive enables metadata injection. The
115 /// config_drive setting provides information about a drive that the
116 /// instance can mount at boot time. The instance reads files from the
117 /// drive to get information that is normally available through the
118 /// metadata service. This metadata is different from the user data. Not
119 /// all cloud providers enable the `config_drive`. Read more in the
120 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
121 #[serde(skip_serializing_if = "Option::is_none")]
122 #[builder(default, setter(into))]
123 pub(crate) delete_on_termination: Option<bool>,
124
125 #[serde(skip_serializing_if = "Option::is_none")]
126 #[builder(default, setter(into))]
127 pub(crate) device_name: Option<Cow<'a, str>>,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
130 #[builder(default, setter(into))]
131 pub(crate) no_device: Option<Value>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 #[builder(default, setter(into))]
135 pub(crate) snapshot_id: Option<Cow<'a, str>>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
138 #[builder(default, setter(into))]
139 pub(crate) virtual_name: Option<Cow<'a, str>>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
142 #[builder(default, setter(into))]
143 pub(crate) volume_id: Option<Cow<'a, str>>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
146 #[builder(default, setter(into))]
147 pub(crate) volume_size: Option<i32>,
148}
149
150#[derive(Debug, Deserialize, Clone, Serialize)]
151pub enum SourceType {
152 #[serde(rename = "blank")]
153 Blank,
154 #[serde(rename = "image")]
155 Image,
156 #[serde(rename = "snapshot")]
157 Snapshot,
158 #[serde(rename = "volume")]
159 Volume,
160}
161
162#[derive(Debug, Deserialize, Clone, Serialize)]
163pub enum DestinationType {
164 #[serde(rename = "local")]
165 Local,
166 #[serde(rename = "volume")]
167 Volume,
168}
169
170#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
171#[builder(setter(strip_option))]
172pub struct BlockDeviceMappingV2<'a> {
173 #[serde(skip_serializing_if = "Option::is_none")]
174 #[builder(default, setter(into))]
175 pub(crate) boot_index: Option<Option<Cow<'a, str>>>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
178 #[builder(default, setter(into))]
179 pub(crate) connection_info: Option<Cow<'a, str>>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
182 #[builder(default, setter(into))]
183 pub(crate) delete_on_termination: Option<bool>,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
186 #[builder(default)]
187 pub(crate) destination_type: Option<DestinationType>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
190 #[builder(default, setter(into))]
191 pub(crate) device_name: Option<Cow<'a, str>>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
194 #[builder(default, setter(into))]
195 pub(crate) device_type: Option<Cow<'a, str>>,
196
197 #[serde(skip_serializing_if = "Option::is_none")]
198 #[builder(default, setter(into))]
199 pub(crate) disk_bus: Option<Cow<'a, str>>,
200
201 #[serde(skip_serializing_if = "Option::is_none")]
202 #[builder(default, setter(into))]
203 pub(crate) guest_format: Option<Cow<'a, str>>,
204
205 #[serde(skip_serializing_if = "Option::is_none")]
206 #[builder(default, setter(into))]
207 pub(crate) image_id: Option<Cow<'a, str>>,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
210 #[builder(default, setter(into))]
211 pub(crate) no_device: Option<Value>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
214 #[builder(default, setter(into))]
215 pub(crate) snapshot_id: Option<Cow<'a, str>>,
216
217 #[serde(skip_serializing_if = "Option::is_none")]
218 #[builder(default)]
219 pub(crate) source_type: Option<SourceType>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
222 #[builder(default, setter(into))]
223 pub(crate) tag: Option<Cow<'a, str>>,
224
225 #[serde(skip_serializing_if = "Option::is_none")]
226 #[builder(default, setter(into))]
227 pub(crate) uuid: Option<Cow<'a, str>>,
228
229 #[serde(skip_serializing_if = "Option::is_none")]
230 #[builder(default, setter(into))]
231 pub(crate) virtual_name: Option<Cow<'a, str>>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
234 #[builder(default, setter(into))]
235 pub(crate) volume_id: Option<Cow<'a, str>>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
238 #[builder(default, setter(into))]
239 pub(crate) volume_size: Option<i32>,
240}
241
242#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
243#[builder(setter(strip_option))]
244pub struct SecurityGroups<'a> {
245 /// A target cell name. Schedule the server in a host in the cell
246 /// specified. It is available when `TargetCellFilter` is available on
247 /// cloud side that is cell v1 environment.
248 #[serde(skip_serializing_if = "Option::is_none")]
249 #[builder(default, setter(into))]
250 pub(crate) name: Option<Cow<'a, str>>,
251}
252
253/// A `server` object.
254#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
255#[builder(setter(strip_option))]
256pub struct Server<'a> {
257 /// IPv4 address that should be used to access this server.
258 #[serde(rename = "accessIPv4", skip_serializing_if = "Option::is_none")]
259 #[builder(default, setter(into))]
260 pub(crate) access_ipv4: Option<Cow<'a, str>>,
261
262 /// IPv6 address that should be used to access this server.
263 #[serde(rename = "accessIPv6", skip_serializing_if = "Option::is_none")]
264 #[builder(default, setter(into))]
265 pub(crate) access_ipv6: Option<Cow<'a, str>>,
266
267 /// The administrative password of the server. If you omit this parameter,
268 /// the operation generates a new password.
269 #[serde(rename = "adminPass", skip_serializing_if = "Option::is_none")]
270 #[builder(default, setter(into))]
271 pub(crate) admin_pass: Option<Cow<'a, str>>,
272
273 /// A target cell name. Schedule the server in a host in the cell
274 /// specified. It is available when `TargetCellFilter` is available on
275 /// cloud side that is cell v1 environment.
276 #[serde(skip_serializing_if = "Option::is_none")]
277 #[builder(default, setter(into))]
278 pub(crate) availability_zone: Option<Cow<'a, str>>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
281 #[builder(default, setter(into))]
282 pub(crate) block_device_mapping: Option<Vec<BlockDeviceMapping<'a>>>,
283
284 /// Enables fine grained control of the block device mapping for an
285 /// instance. This is typically used for booting servers from volumes. An
286 /// example format would look as follows:
287 ///
288 /// > ```text
289 /// > "block_device_mapping_v2": [{
290 /// > "boot_index": "0",
291 /// > "uuid": "ac408821-c95a-448f-9292-73986c790911",
292 /// > "source_type": "image",
293 /// > "volume_size": "25",
294 /// > "destination_type": "volume",
295 /// > "delete_on_termination": true,
296 /// > "tag": "disk1",
297 /// > "disk_bus": "scsi"}]
298 /// >
299 /// > ```
300 ///
301 /// In microversion 2.32, `tag` is an optional string attribute that can be
302 /// used to assign a tag to the block device. This tag is then exposed to
303 /// the guest in the metadata API and the config drive and is associated to
304 /// hardware metadata for that block device, such as bus (ex: SCSI), bus
305 /// address (ex: 1:0:2:0), and serial.
306 ///
307 /// A bug has caused the `tag` attribute to no longer be accepted starting
308 /// with version 2.33. It has been restored in version 2.42.
309 #[serde(skip_serializing_if = "Option::is_none")]
310 #[builder(default, setter(into))]
311 pub(crate) block_device_mapping_v2: Option<Vec<BlockDeviceMappingV2<'a>>>,
312
313 /// Indicates whether a config drive enables metadata injection. The
314 /// config_drive setting provides information about a drive that the
315 /// instance can mount at boot time. The instance reads files from the
316 /// drive to get information that is normally available through the
317 /// metadata service. This metadata is different from the user data. Not
318 /// all cloud providers enable the `config_drive`. Read more in the
319 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
320 #[serde(skip_serializing_if = "Option::is_none")]
321 #[builder(default, setter(into))]
322 pub(crate) config_drive: Option<bool>,
323
324 /// A free form description of the server. Limited to 255 characters in
325 /// length. Before microversion 2.19 this was set to the server name.
326 ///
327 /// **New in version 2.19**
328 #[serde(skip_serializing_if = "Option::is_none")]
329 #[builder(default, setter(into))]
330 pub(crate) description: Option<Option<Cow<'a, str>>>,
331
332 /// The flavor reference, as an ID (including a UUID) or full URL, for the
333 /// flavor for your server instance.
334 #[serde(rename = "flavorRef")]
335 #[builder(setter(into))]
336 pub(crate) flavor_ref: Cow<'a, str>,
337
338 /// The UUID of the image to use for your server instance. This is not
339 /// required in case of boot from volume. In all other cases it is required
340 /// and must be a valid UUID otherwise API will return 400.
341 #[serde(rename = "imageRef", skip_serializing_if = "Option::is_none")]
342 #[builder(default, setter(into))]
343 pub(crate) image_ref: Option<Cow<'a, str>>,
344
345 /// A target cell name. Schedule the server in a host in the cell
346 /// specified. It is available when `TargetCellFilter` is available on
347 /// cloud side that is cell v1 environment.
348 #[serde(skip_serializing_if = "Option::is_none")]
349 #[builder(default, setter(into))]
350 pub(crate) key_name: Option<Cow<'a, str>>,
351
352 #[serde(skip_serializing_if = "Option::is_none")]
353 #[builder(default, setter(into))]
354 pub(crate) max_count: Option<i32>,
355
356 /// Metadata key and value pairs. The maximum size of the metadata key and
357 /// value is 255 bytes each.
358 #[serde(skip_serializing_if = "Option::is_none")]
359 #[builder(default, private, setter(into, name = "_metadata"))]
360 pub(crate) metadata: Option<BTreeMap<Cow<'a, str>, Cow<'a, str>>>,
361
362 #[serde(skip_serializing_if = "Option::is_none")]
363 #[builder(default, setter(into))]
364 pub(crate) min_count: Option<i32>,
365
366 /// A target cell name. Schedule the server in a host in the cell
367 /// specified. It is available when `TargetCellFilter` is available on
368 /// cloud side that is cell v1 environment.
369 #[serde()]
370 #[builder(setter(into))]
371 pub(crate) name: Cow<'a, str>,
372
373 /// A list of `network` object. Required parameter when there are multiple
374 /// networks defined for the tenant. When you do not specify the networks
375 /// parameter, the server attaches to the only network created for the
376 /// current tenant. Optionally, you can create one or more NICs on the
377 /// server. To provision the server instance with a NIC for a network,
378 /// specify the UUID of the network in the `uuid` attribute in a `networks`
379 /// object. To provision the server instance with a NIC for an already
380 /// existing port, specify the port-id in the `port` attribute in a
381 /// `networks` object.
382 ///
383 /// If multiple networks are defined, the order in which they appear in the
384 /// guest operating system will not necessarily reflect the order in which
385 /// they are given in the server boot request. Guests should therefore not
386 /// depend on device order to deduce any information about their network
387 /// devices. Instead, device role tags should be used: introduced in 2.32,
388 /// broken in 2.37, and re-introduced and fixed in 2.42, the `tag` is an
389 /// optional, string attribute that can be used to assign a tag to a
390 /// virtual network interface. This tag is then exposed to the guest in the
391 /// metadata API and the config drive and is associated to hardware
392 /// metadata for that network interface, such as bus (ex: PCI), bus address
393 /// (ex: 0000:00:02.0), and MAC address.
394 ///
395 /// A bug has caused the `tag` attribute to no longer be accepted starting
396 /// with version 2.37. Therefore, network interfaces could only be tagged
397 /// in versions 2.32 to 2.36 inclusively. Version 2.42 has restored the
398 /// `tag` attribute.
399 ///
400 /// Starting with microversion 2.37, this field is required and the special
401 /// string values *auto* and *none* can be specified for networks. *auto*
402 /// tells the Compute service to use a network that is available to the
403 /// project, if one exists. If one does not exist, the Compute service will
404 /// attempt to automatically allocate a network for the project (if
405 /// possible). *none* tells the Compute service to not allocate a network
406 /// for the instance. The *auto* and *none* values cannot be used with any
407 /// other network values, including other network uuids, ports, fixed IPs
408 /// or device tags. These are requested as strings for the networks value,
409 /// not in a list. See the associated example.
410 #[serde()]
411 #[builder(setter(into))]
412 pub(crate) networks: ServerNetworks<'a>,
413
414 /// Controls how the API partitions the disk when you create, rebuild, or
415 /// resize servers. A server inherits the `OS-DCF:diskConfig` value from
416 /// the image from which it was created, and an image inherits the
417 /// `OS-DCF:diskConfig` value from the server from which it was created. To
418 /// override the inherited setting, you can include this attribute in the
419 /// request body of a server create, rebuild, or resize request. If the
420 /// `OS-DCF:diskConfig` value for an image is `MANUAL`, you cannot create a
421 /// server from that image and set its `OS-DCF:diskConfig` value to `AUTO`.
422 /// A valid value is:
423 ///
424 /// - `AUTO`. The API builds the server with a single partition the size of
425 /// the target flavor disk. The API automatically adjusts the file system
426 /// to fit the entire partition.
427 /// - `MANUAL`. The API builds the server by using whatever partition
428 /// scheme and file system is in the source image. If the target flavor
429 /// disk is larger, the API does not partition the remaining disk space.
430 #[serde(rename = "OS-DCF:diskConfig", skip_serializing_if = "Option::is_none")]
431 #[builder(default)]
432 pub(crate) os_dcf_disk_config: Option<OsDcfDiskConfig>,
433
434 /// Indicates whether a config drive enables metadata injection. The
435 /// config_drive setting provides information about a drive that the
436 /// instance can mount at boot time. The instance reads files from the
437 /// drive to get information that is normally available through the
438 /// metadata service. This metadata is different from the user data. Not
439 /// all cloud providers enable the `config_drive`. Read more in the
440 /// [OpenStack End User Guide](https://docs.openstack.org/nova/latest/user/config-drive.html).
441 #[serde(skip_serializing_if = "Option::is_none")]
442 #[builder(default, setter(into))]
443 pub(crate) return_reservation_id: Option<bool>,
444
445 /// One or more security groups. Specify the name of the security group in
446 /// the `name` attribute. If you omit this attribute, the API creates the
447 /// server in the `default` security group. Requested security groups are
448 /// not applied to pre-existing ports.
449 #[serde(skip_serializing_if = "Option::is_none")]
450 #[builder(default, setter(into))]
451 pub(crate) security_groups: Option<Vec<SecurityGroups<'a>>>,
452
453 /// A list of tags. Tags have the following restrictions:
454 ///
455 /// - Tag is a Unicode bytestring no longer than 60 characters.
456 /// - Tag is a non-empty string.
457 /// - ‘/’ is not allowed to be in a tag name
458 /// - Comma is not allowed to be in a tag name in order to simplify
459 /// requests that specify lists of tags
460 /// - All other characters are allowed to be in a tag name
461 /// - Each server can have up to 50 tags.
462 ///
463 /// **New in version 2.52**
464 #[serde(skip_serializing_if = "Option::is_none")]
465 #[builder(default, setter(into))]
466 pub(crate) tags: Option<Vec<Cow<'a, str>>>,
467
468 /// Configuration information or scripts to use upon launch. Must be Base64
469 /// encoded. Restricted to 65535 bytes.
470 ///
471 /// Note
472 ///
473 /// The `null` value allowed in Nova legacy v2 API, but due to the strict
474 /// input validation, it isn’t allowed in Nova v2.1 API.
475 #[serde(skip_serializing_if = "Option::is_none")]
476 #[builder(default, setter(into))]
477 pub(crate) user_data: Option<Cow<'a, str>>,
478}
479
480impl<'a> ServerBuilder<'a> {
481 /// Metadata key and value pairs. The maximum size of the metadata key and
482 /// value is 255 bytes each.
483 pub fn metadata<I, K, V>(&mut self, iter: I) -> &mut Self
484 where
485 I: Iterator<Item = (K, V)>,
486 K: Into<Cow<'a, str>>,
487 V: Into<Cow<'a, str>>,
488 {
489 self.metadata
490 .get_or_insert(None)
491 .get_or_insert_with(BTreeMap::new)
492 .extend(iter.map(|(k, v)| (k.into(), v.into())));
493 self
494 }
495}
496
497/// The dictionary of data to send to the scheduler. Alternatively, you can
498/// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
499///
500/// Note
501///
502/// This is a top-level key in the request body, not part of the server portion
503/// of the request body.
504///
505/// There are a few caveats with scheduler hints:
506///
507/// - The request validation schema is per hint. For example, some require a
508/// single string value, and some accept a list of values.
509/// - Hints are only used based on the cloud scheduler configuration, which
510/// varies per deployment.
511/// - Hints are pluggable per deployment, meaning that a cloud can have custom
512/// hints which may not be available in another cloud.
513///
514/// For these reasons, it is important to consult each cloud’s user
515/// documentation to know what is available for scheduler hints.
516#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
517#[builder(setter(strip_option))]
518pub struct OsSchedulerHints<'a> {
519 /// Schedule the server on a host in the network specified with this
520 /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
521 /// `SimpleCIDRAffinityFilter` is available on cloud side.
522 #[serde(skip_serializing_if = "Option::is_none")]
523 #[builder(default, setter(into))]
524 pub(crate) build_near_host_ip: Option<Cow<'a, str>>,
525
526 /// Schedule the server on a host in the network specified with an IP
527 /// address (`os:scheduler_hints:build_near_host_ip`) and this parameter.
528 /// If `os:scheduler_hints:build_near_host_ip` is specified and this
529 /// parameter is omitted, `/24` is used. It is available when
530 /// `SimpleCIDRAffinityFilter` is available on cloud side.
531 #[serde(skip_serializing_if = "Option::is_none")]
532 #[builder(default, setter(into))]
533 pub(crate) cidr: Option<Cow<'a, str>>,
534
535 /// A list of cell routes or a cell route (string). Schedule the server in
536 /// a cell that is not specified. It is available when
537 /// `DifferentCellFilter` is available on cloud side that is cell v1
538 /// environment.
539 #[serde(skip_serializing_if = "Option::is_none")]
540 #[builder(default, setter(into))]
541 pub(crate) different_cell: Option<Vec<Cow<'a, str>>>,
542
543 /// A list of server UUIDs or a server UUID. Schedule the server on a
544 /// different host from a set of servers. It is available when
545 /// `DifferentHostFilter` is available on cloud side.
546 #[serde(skip_serializing_if = "Option::is_none")]
547 #[builder(default, setter(into))]
548 pub(crate) different_host: Option<Vec<Cow<'a, str>>>,
549
550 /// The server group UUID. Schedule the server according to a policy of the
551 /// server group (`anti-affinity`, `affinity`, `soft-anti-affinity` or
552 /// `soft-affinity`). It is available when `ServerGroupAffinityFilter`,
553 /// `ServerGroupAntiAffinityFilter`, `ServerGroupSoftAntiAffinityWeigher`,
554 /// `ServerGroupSoftAffinityWeigher` are available on cloud side.
555 #[serde(skip_serializing_if = "Option::is_none")]
556 #[builder(default, setter(into))]
557 pub(crate) group: Option<Cow<'a, str>>,
558
559 /// Schedule the server by using a custom filter in JSON format. For
560 /// example:
561 ///
562 /// ```text
563 /// "query": "[\">=\",\"$free_ram_mb\",1024]"
564 ///
565 /// ```
566 ///
567 /// It is available when `JsonFilter` is available on cloud side.
568 #[serde(skip_serializing_if = "Option::is_none")]
569 #[builder(default, setter(into))]
570 pub(crate) query: Option<Value>,
571
572 /// A list of server UUIDs or a server UUID. Schedule the server on the
573 /// same host as another server in a set of servers. It is available when
574 /// `SameHostFilter` is available on cloud side.
575 #[serde(skip_serializing_if = "Option::is_none")]
576 #[builder(default, setter(into))]
577 pub(crate) same_host: Option<Vec<Cow<'a, str>>>,
578
579 /// A target cell name. Schedule the server in a host in the cell
580 /// specified. It is available when `TargetCellFilter` is available on
581 /// cloud side that is cell v1 environment.
582 #[serde(skip_serializing_if = "Option::is_none")]
583 #[builder(default, setter(into))]
584 pub(crate) target_cell: Option<Cow<'a, str>>,
585
586 #[builder(setter(name = "_properties"), default, private)]
587 #[serde(flatten)]
588 _properties: BTreeMap<Cow<'a, str>, Value>,
589}
590
591impl<'a> OsSchedulerHintsBuilder<'a> {
592 pub fn properties<I, K, V>(&mut self, iter: I) -> &mut Self
593 where
594 I: Iterator<Item = (K, V)>,
595 K: Into<Cow<'a, str>>,
596 V: Into<Value>,
597 {
598 self._properties
599 .get_or_insert_with(BTreeMap::new)
600 .extend(iter.map(|(k, v)| (k.into(), v.into())));
601 self
602 }
603}
604
605#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
606#[builder(setter(strip_option))]
607pub struct OsSchHntSchedulerHints<'a> {
608 /// Schedule the server on a host in the network specified with this
609 /// parameter and a cidr (`os:scheduler_hints.cidr`). It is available when
610 /// `SimpleCIDRAffinityFilter` is available on cloud side.
611 #[serde(skip_serializing_if = "Option::is_none")]
612 #[builder(default, setter(into))]
613 pub(crate) build_near_host_ip: Option<Cow<'a, str>>,
614
615 /// Schedule the server on a host in the network specified with an IP
616 /// address (`os:scheduler_hints:build_near_host_ip`) and this parameter.
617 /// If `os:scheduler_hints:build_near_host_ip` is specified and this
618 /// parameter is omitted, `/24` is used. It is available when
619 /// `SimpleCIDRAffinityFilter` is available on cloud side.
620 #[serde(skip_serializing_if = "Option::is_none")]
621 #[builder(default, setter(into))]
622 pub(crate) cidr: Option<Cow<'a, str>>,
623
624 /// A list of cell routes or a cell route (string). Schedule the server in
625 /// a cell that is not specified. It is available when
626 /// `DifferentCellFilter` is available on cloud side that is cell v1
627 /// environment.
628 #[serde(skip_serializing_if = "Option::is_none")]
629 #[builder(default, setter(into))]
630 pub(crate) different_cell: Option<Vec<Cow<'a, str>>>,
631
632 /// A list of server UUIDs or a server UUID. Schedule the server on a
633 /// different host from a set of servers. It is available when
634 /// `DifferentHostFilter` is available on cloud side.
635 #[serde(skip_serializing_if = "Option::is_none")]
636 #[builder(default, setter(into))]
637 pub(crate) different_host: Option<Vec<Cow<'a, str>>>,
638
639 /// The server group UUID. Schedule the server according to a policy of the
640 /// server group (`anti-affinity`, `affinity`, `soft-anti-affinity` or
641 /// `soft-affinity`). It is available when `ServerGroupAffinityFilter`,
642 /// `ServerGroupAntiAffinityFilter`, `ServerGroupSoftAntiAffinityWeigher`,
643 /// `ServerGroupSoftAffinityWeigher` are available on cloud side.
644 #[serde(skip_serializing_if = "Option::is_none")]
645 #[builder(default, setter(into))]
646 pub(crate) group: Option<Cow<'a, str>>,
647
648 /// Schedule the server by using a custom filter in JSON format. For
649 /// example:
650 ///
651 /// ```text
652 /// "query": "[\">=\",\"$free_ram_mb\",1024]"
653 ///
654 /// ```
655 ///
656 /// It is available when `JsonFilter` is available on cloud side.
657 #[serde(skip_serializing_if = "Option::is_none")]
658 #[builder(default, setter(into))]
659 pub(crate) query: Option<Value>,
660
661 /// A list of server UUIDs or a server UUID. Schedule the server on the
662 /// same host as another server in a set of servers. It is available when
663 /// `SameHostFilter` is available on cloud side.
664 #[serde(skip_serializing_if = "Option::is_none")]
665 #[builder(default, setter(into))]
666 pub(crate) same_host: Option<Vec<Cow<'a, str>>>,
667
668 /// A target cell name. Schedule the server in a host in the cell
669 /// specified. It is available when `TargetCellFilter` is available on
670 /// cloud side that is cell v1 environment.
671 #[serde(skip_serializing_if = "Option::is_none")]
672 #[builder(default, setter(into))]
673 pub(crate) target_cell: Option<Cow<'a, str>>,
674
675 #[builder(setter(name = "_properties"), default, private)]
676 #[serde(flatten)]
677 _properties: BTreeMap<Cow<'a, str>, Value>,
678}
679
680impl<'a> OsSchHntSchedulerHintsBuilder<'a> {
681 pub fn properties<I, K, V>(&mut self, iter: I) -> &mut Self
682 where
683 I: Iterator<Item = (K, V)>,
684 K: Into<Cow<'a, str>>,
685 V: Into<Value>,
686 {
687 self._properties
688 .get_or_insert_with(BTreeMap::new)
689 .extend(iter.map(|(k, v)| (k.into(), v.into())));
690 self
691 }
692}
693
694#[derive(Builder, Debug, Clone)]
695#[builder(setter(strip_option))]
696pub struct Request<'a> {
697 #[builder(default, setter(into))]
698 pub(crate) os_sch_hnt_scheduler_hints: Option<OsSchHntSchedulerHints<'a>>,
699
700 /// The dictionary of data to send to the scheduler. Alternatively, you can
701 /// specify `OS-SCH-HNT:scheduler_hints` as the key in the request body.
702 ///
703 /// Note
704 ///
705 /// This is a top-level key in the request body, not part of the server
706 /// portion of the request body.
707 ///
708 /// There are a few caveats with scheduler hints:
709 ///
710 /// - The request validation schema is per hint. For example, some require
711 /// a single string value, and some accept a list of values.
712 /// - Hints are only used based on the cloud scheduler configuration, which
713 /// varies per deployment.
714 /// - Hints are pluggable per deployment, meaning that a cloud can have
715 /// custom hints which may not be available in another cloud.
716 ///
717 /// For these reasons, it is important to consult each cloud’s user
718 /// documentation to know what is available for scheduler hints.
719 #[builder(default, setter(into))]
720 pub(crate) os_scheduler_hints: Option<OsSchedulerHints<'a>>,
721
722 /// A `server` object.
723 #[builder(setter(into))]
724 pub(crate) server: Server<'a>,
725
726 #[builder(setter(name = "_headers"), default, private)]
727 _headers: Option<HeaderMap>,
728}
729impl<'a> Request<'a> {
730 /// Create a builder for the endpoint.
731 pub fn builder() -> RequestBuilder<'a> {
732 RequestBuilder::default()
733 }
734}
735
736impl RequestBuilder<'_> {
737 /// Add a single header to the Server.
738 pub fn header(&mut self, header_name: &'static str, header_value: &'static str) -> &mut Self
739where {
740 self._headers
741 .get_or_insert(None)
742 .get_or_insert_with(HeaderMap::new)
743 .insert(header_name, HeaderValue::from_static(header_value));
744 self
745 }
746
747 /// Add multiple headers.
748 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
749 where
750 I: Iterator<Item = T>,
751 T: Into<(Option<HeaderName>, HeaderValue)>,
752 {
753 self._headers
754 .get_or_insert(None)
755 .get_or_insert_with(HeaderMap::new)
756 .extend(iter.map(Into::into));
757 self
758 }
759}
760
761impl RestEndpoint for Request<'_> {
762 fn method(&self) -> http::Method {
763 http::Method::POST
764 }
765
766 fn endpoint(&self) -> Cow<'static, str> {
767 "servers".to_string().into()
768 }
769
770 fn parameters(&self) -> QueryParams {
771 QueryParams::default()
772 }
773
774 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
775 let mut params = JsonBodyParams::default();
776
777 params.push("server", serde_json::to_value(&self.server)?);
778 if let Some(val) = &self.os_scheduler_hints {
779 params.push("os:scheduler_hints", serde_json::to_value(val)?);
780 }
781 if let Some(val) = &self.os_sch_hnt_scheduler_hints {
782 params.push("OS-SCH-HNT:scheduler_hints", serde_json::to_value(val)?);
783 }
784
785 params.into_body()
786 }
787
788 fn service_type(&self) -> ServiceType {
789 ServiceType::Compute
790 }
791
792 fn response_key(&self) -> Option<Cow<'static, str>> {
793 Some("server".into())
794 }
795
796 /// Returns headers to be set into the request
797 fn request_headers(&self) -> Option<&HeaderMap> {
798 self._headers.as_ref()
799 }
800
801 /// Returns required API version
802 fn api_version(&self) -> Option<ApiVersion> {
803 Some(ApiVersion::new(2, 57))
804 }
805}
806
807#[cfg(test)]
808mod tests {
809 use super::*;
810 #[cfg(feature = "sync")]
811 use crate::api::Query;
812 use crate::test::client::FakeOpenStackClient;
813 use crate::types::ServiceType;
814 use http::{HeaderName, HeaderValue};
815 use httpmock::MockServer;
816 use serde_json::json;
817
818 #[test]
819 fn test_service_type() {
820 assert_eq!(
821 Request::builder()
822 .server(
823 ServerBuilder::default()
824 .flavor_ref("foo")
825 .name("foo")
826 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
827 .build()
828 .unwrap()])))
829 .build()
830 .unwrap()
831 )
832 .build()
833 .unwrap()
834 .service_type(),
835 ServiceType::Compute
836 );
837 }
838
839 #[test]
840 fn test_response_key() {
841 assert_eq!(
842 Request::builder()
843 .server(
844 ServerBuilder::default()
845 .flavor_ref("foo")
846 .name("foo")
847 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
848 .build()
849 .unwrap()])))
850 .build()
851 .unwrap()
852 )
853 .build()
854 .unwrap()
855 .response_key()
856 .unwrap(),
857 "server"
858 );
859 }
860
861 #[cfg(feature = "sync")]
862 #[test]
863 fn endpoint() {
864 let server = MockServer::start();
865 let client = FakeOpenStackClient::new(server.base_url());
866 let mock = server.mock(|when, then| {
867 when.method(httpmock::Method::POST)
868 .path("/servers".to_string());
869
870 then.status(200)
871 .header("content-type", "application/json")
872 .json_body(json!({ "server": {} }));
873 });
874
875 let endpoint = Request::builder()
876 .server(
877 ServerBuilder::default()
878 .flavor_ref("foo")
879 .name("foo")
880 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
881 .build()
882 .unwrap()])))
883 .build()
884 .unwrap(),
885 )
886 .build()
887 .unwrap();
888 let _: serde_json::Value = endpoint.query(&client).unwrap();
889 mock.assert();
890 }
891
892 #[cfg(feature = "sync")]
893 #[test]
894 fn endpoint_headers() {
895 let server = MockServer::start();
896 let client = FakeOpenStackClient::new(server.base_url());
897 let mock = server.mock(|when, then| {
898 when.method(httpmock::Method::POST)
899 .path("/servers".to_string())
900 .header("foo", "bar")
901 .header("not_foo", "not_bar");
902 then.status(200)
903 .header("content-type", "application/json")
904 .json_body(json!({ "server": {} }));
905 });
906
907 let endpoint = Request::builder()
908 .server(
909 ServerBuilder::default()
910 .flavor_ref("foo")
911 .name("foo")
912 .networks(ServerNetworks::F1(Vec::from([NetworksBuilder::default()
913 .build()
914 .unwrap()])))
915 .build()
916 .unwrap(),
917 )
918 .headers(
919 [(
920 Some(HeaderName::from_static("foo")),
921 HeaderValue::from_static("bar"),
922 )]
923 .into_iter(),
924 )
925 .header("not_foo", "not_bar")
926 .build()
927 .unwrap();
928 let _: serde_json::Value = endpoint.query(&client).unwrap();
929 mock.assert();
930 }
931}