1#![allow(non_snake_case)]
18#![allow(missing_docs)]
19
20use std::marker::PhantomData;
21use std::net;
22use std::ops::Not;
23
24use chrono::{DateTime, FixedOffset};
25use osauth::common::empty_as_default;
26use serde::{Deserialize, Deserializer, Serialize, Serializer};
27use serde_json::Value;
28
29use super::super::common::{NetworkRef, SecurityGroupRef};
30use super::super::Result;
31use crate::session::Session;
32
33protocol_enum! {
34 #[doc = "IP protocol version."]
35 enum IpVersion: u8 {
36 V4 = 4,
37 V6 = 6
38 }
39}
40
41protocol_enum! {
42 #[doc = "Network IP protocol."]
43 enum NetworkProtocol {
44 TCP = "tcp",
45 UDP = "udp"
46 }
47}
48
49protocol_enum! {
50 #[doc = "Possible network statuses."]
51 enum NetworkStatus {
52 Active = "ACTIVE",
53 Down = "DOWN",
54 Building = "BUILD",
55 Error = "ERROR"
56 }
57}
58
59protocol_enum! {
60 #[doc = "Available sort keys."]
61 enum NetworkSortKey {
62 CreatedAt = "created_at",
63 Id = "id",
64 Name = "name",
65 UpdatedAt = "updated_at"
66 }
67}
68
69protocol_enum! {
70 #[doc = "Possible floating IP statuses."]
71 enum FloatingIpStatus {
72 Active = "ACTIVE",
73 Down = "DOWN",
74 Error = "ERROR"
75 }
76}
77
78protocol_enum! {
79 #[doc = "Available sort keys."]
80 enum FloatingIpSortKey {
81 FixedIpAddress = "fixed_ip_address",
82 FloatingIpAddress = "floating_ip_address",
83 FloatingNetworkId = "floating_network_id",
84 Id = "id",
85 RouterId = "router_id",
86 Status = "status"
87 }
88}
89
90impl Default for NetworkSortKey {
91 fn default() -> NetworkSortKey {
92 NetworkSortKey::CreatedAt
93 }
94}
95
96protocol_enum! {
97 #[doc = "Available sort keys."]
98 enum PortSortKey {
99 AdminStateUp = "admin_state_up",
100 DeviceId = "device_id",
101 DeviceOwner = "device_owner",
102 Id = "id",
103 MacAddress = "mac_address",
104 Name = "name",
105 NetworkId = "network_id",
106 Status = "status"
107 }
108}
109
110protocol_enum! {
111 #[doc = "Available sort keys."]
112 enum RouterSortKey {
113 AdminStateUp = "admin_state_up",
114 FlavorId = "flavor_id",
115 Id = "id",
116 Name = "name",
117 ProjectId = "project_id",
118 Status = "status"
119 }
120}
121
122protocol_enum! {
123 #[doc = "Possible router statuses."]
124 enum RouterStatus {
125 Active = "ACTIVE",
126 Allocating = "ALLOCATING",
127 Error = "ERROR"
128 }
129}
130
131protocol_enum! {
132 #[doc = "Available sort keys."]
133 enum SubnetSortKey {
134 Cidr = "cidr",
135 DhcpEnabled = "enable_dhcp",
136 GatewayIp = "gateway_ip",
137 Id = "id",
138 IpVersion = "ip_version",
139 Ipv6AddressMode = "ipv6_address_mode",
140 Ipv6RouterAdvertisementMode = "ipv6_ra_mode",
141 Name = "name",
142 NetworkId = "network_id"
143 }
144}
145
146protocol_enum! {
147 #[doc = "IPv6 modes for assigning IP addresses."]
148 enum Ipv6Mode {
149 DhcpStateful = "dhcpv6-stateful",
150 DhcpStateless = "dhcpv6-stateless",
151 Slaac = "slaac"
152 }
153}
154
155#[derive(Debug, Clone, Deserialize, Serialize)]
157pub struct Network {
158 pub admin_state_up: bool,
159 #[serde(default, skip_serializing)]
160 pub availability_zones: Vec<String>,
161 #[serde(default, skip_serializing)]
162 pub created_at: Option<DateTime<FixedOffset>>,
163 #[serde(
164 deserialize_with = "empty_as_default",
165 default,
166 skip_serializing_if = "Option::is_none"
167 )]
168 pub description: Option<String>,
169 #[serde(
170 deserialize_with = "empty_as_default",
171 default,
172 skip_serializing_if = "Option::is_none"
173 )]
174 pub dns_domain: Option<String>,
175 #[serde(rename = "router:external", skip_serializing_if = "Option::is_none")]
176 pub external: Option<bool>,
177 #[serde(skip_serializing)]
178 pub id: String,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub is_default: Option<bool>,
181 #[serde(default, skip_serializing)]
182 pub l2_adjacency: Option<bool>,
183 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub mtu: Option<u32>,
185 #[serde(
186 deserialize_with = "empty_as_default",
187 skip_serializing_if = "Option::is_none"
188 )]
189 pub name: Option<String>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
191 pub port_security_enabled: Option<bool>,
192 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub project_id: Option<String>,
194 #[serde(default, skip_serializing_if = "Not::not")]
195 pub shared: bool,
196 #[serde(skip_serializing)]
197 pub status: NetworkStatus,
198 #[serde(default, skip_serializing)]
201 pub updated_at: Option<DateTime<FixedOffset>>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub vlan_transparent: Option<bool>,
204}
205
206impl Default for Network {
207 fn default() -> Network {
208 Network {
209 admin_state_up: true,
210 availability_zones: Vec::new(),
211 created_at: None,
212 description: None,
213 dns_domain: None,
214 external: None,
215 id: String::new(),
216 is_default: None,
217 l2_adjacency: None,
218 mtu: None,
219 name: None,
220 port_security_enabled: None,
221 project_id: None,
222 shared: false,
223 status: NetworkStatus::Active,
224 updated_at: None,
226 vlan_transparent: None,
227 }
228 }
229}
230
231#[derive(Debug, Clone, Default, Serialize)]
233pub struct NetworkUpdate {
234 #[serde(skip_serializing_if = "Option::is_none")]
235 pub admin_state_up: Option<bool>,
236 #[serde(rename = "router:external", skip_serializing_if = "Option::is_none")]
237 pub external: Option<bool>,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub description: Option<String>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub dns_domain: Option<String>,
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub is_default: Option<bool>,
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub mtu: Option<u32>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub name: Option<String>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub port_security_enabled: Option<bool>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub shared: Option<bool>,
252}
253
254#[derive(Debug, Clone, Deserialize, Serialize)]
256pub struct NetworkRoot {
257 pub network: Network,
258}
259
260#[derive(Debug, Clone, Serialize)]
262pub struct NetworkUpdateRoot {
263 pub network: NetworkUpdate,
264}
265
266#[derive(Debug, Clone, Deserialize)]
268pub struct NetworksRoot {
269 pub networks: Vec<Network>,
270}
271
272#[derive(Debug, Clone, Deserialize, Serialize)]
274pub struct PortExtraDhcpOption {
275 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub ip_version: Option<IpVersion>,
278 #[serde(rename = "opt_name")]
280 pub name: String,
281 #[serde(rename = "opt_value")]
283 pub value: String,
284 #[doc(hidden)]
285 #[serde(skip)]
286 pub __nonexhaustive: PhantomData<()>,
287}
288
289impl PortExtraDhcpOption {
290 pub fn new<S1, S2>(name: S1, value: S2) -> PortExtraDhcpOption
292 where
293 S1: Into<String>,
294 S2: Into<String>,
295 {
296 PortExtraDhcpOption {
297 ip_version: None,
298 name: name.into(),
299 value: value.into(),
300 __nonexhaustive: PhantomData,
301 }
302 }
303
304 pub fn new_with_ip_version<S1, S2>(
306 name: S1,
307 value: S2,
308 ip_version: IpVersion,
309 ) -> PortExtraDhcpOption
310 where
311 S1: Into<String>,
312 S2: Into<String>,
313 {
314 PortExtraDhcpOption {
315 ip_version: Some(ip_version),
316 name: name.into(),
317 value: value.into(),
318 __nonexhaustive: PhantomData,
319 }
320 }
321}
322
323#[derive(Debug, Clone, Deserialize, Serialize)]
325pub struct FixedIp {
326 #[serde(skip_serializing_if = "::std::net::IpAddr::is_unspecified")]
327 pub ip_address: net::IpAddr,
328 #[serde(skip_serializing_if = "String::is_empty")]
329 pub subnet_id: String,
330}
331
332#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Ord, PartialOrd, Hash)]
333pub struct MacAddress(macaddr::MacAddr6);
334
335impl MacAddress {
336 pub fn is_nil(&self) -> bool {
337 self.0.is_nil()
338 }
339}
340
341impl std::fmt::Display for MacAddress {
342 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
343 write!(f, "{}", self.0)
344 }
345}
346
347impl std::ops::Deref for MacAddress {
348 type Target = macaddr::MacAddr6;
349
350 fn deref(&self) -> &Self::Target {
351 &self.0
352 }
353}
354
355impl std::str::FromStr for MacAddress {
356 type Err = macaddr::ParseError;
357
358 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
359 Ok(Self(s.parse::<macaddr::MacAddr6>()?))
360 }
361}
362
363impl Serialize for MacAddress {
364 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
365 where
366 S: Serializer,
367 {
368 serializer.serialize_str(&self.to_string())
369 }
370}
371
372impl<'de> Deserialize<'de> for MacAddress {
373 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
374 where
375 D: Deserializer<'de>,
376 {
377 let s: String = Deserialize::deserialize(deserializer)?;
378 s.parse().map_err(serde::de::Error::custom)
379 }
380}
381
382#[derive(Debug, Clone, Deserialize, Serialize, Copy)]
384pub struct AllowedAddressPair {
385 pub ip_address: net::IpAddr,
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub mac_address: Option<MacAddress>,
388}
389
390#[derive(Debug, Clone, Deserialize, Serialize)]
392pub struct Port {
393 pub admin_state_up: bool,
394 #[serde(skip_serializing_if = "Vec::is_empty", default)]
395 pub allowed_address_pairs: Vec<AllowedAddressPair>,
396 #[serde(default, skip_serializing)]
397 pub created_at: Option<DateTime<FixedOffset>>,
398 #[serde(
399 deserialize_with = "empty_as_default",
400 default,
401 skip_serializing_if = "Option::is_none"
402 )]
403 pub description: Option<String>,
404 #[serde(
405 deserialize_with = "empty_as_default",
406 default,
407 skip_serializing_if = "Option::is_none"
408 )]
409 pub device_id: Option<String>,
410 #[serde(
411 deserialize_with = "empty_as_default",
412 default,
413 skip_serializing_if = "Option::is_none"
414 )]
415 pub device_owner: Option<String>,
416 #[serde(
417 deserialize_with = "empty_as_default",
418 default,
419 skip_serializing_if = "Option::is_none"
420 )]
421 pub dns_domain: Option<String>,
422 #[serde(
423 deserialize_with = "empty_as_default",
424 default,
425 skip_serializing_if = "Option::is_none"
426 )]
427 pub dns_name: Option<String>,
428 #[serde(default, skip_serializing_if = "Vec::is_empty")]
429 pub extra_dhcp_opts: Vec<PortExtraDhcpOption>,
430 #[serde(default, skip_serializing_if = "Vec::is_empty")]
431 pub fixed_ips: Vec<FixedIp>,
432 #[serde(skip_serializing)]
433 pub id: String,
434 #[serde(skip_serializing_if = "MacAddress::is_nil")]
435 pub mac_address: MacAddress,
436 #[serde(
437 deserialize_with = "empty_as_default",
438 skip_serializing_if = "Option::is_none"
439 )]
440 pub name: Option<String>,
441 pub network_id: String,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub project_id: Option<String>,
444 #[serde(default, skip_serializing_if = "Vec::is_empty")]
445 pub security_groups: Vec<SecurityGroupRef>,
446 #[serde(skip_serializing)]
447 pub status: NetworkStatus,
448 #[serde(default, skip_serializing)]
449 pub updated_at: Option<DateTime<FixedOffset>>,
450}
451
452#[derive(Debug, Clone, Serialize, Default)]
454pub struct PortUpdate {
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub admin_state_up: Option<bool>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub description: Option<String>,
459 #[serde(skip_serializing_if = "Option::is_none")]
460 pub device_id: Option<String>,
461 #[serde(skip_serializing_if = "Option::is_none")]
462 pub device_owner: Option<String>,
463 #[serde(skip_serializing_if = "Option::is_none")]
464 pub dns_domain: Option<String>,
465 #[serde(skip_serializing_if = "Option::is_none")]
466 pub dns_name: Option<String>,
467 #[serde(skip_serializing_if = "Option::is_none")]
468 pub extra_dhcp_opts: Option<Vec<PortExtraDhcpOption>>,
469 #[serde(skip_serializing_if = "Option::is_none")]
470 pub fixed_ips: Option<Vec<FixedIp>>,
471 #[serde(skip_serializing_if = "Option::is_none")]
472 pub mac_address: Option<MacAddress>,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub name: Option<String>,
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub security_groups: Option<Vec<SecurityGroupRef>>,
477}
478
479#[derive(Debug, Clone, Deserialize, Serialize)]
481pub struct PortRoot {
482 pub port: Port,
483}
484
485#[derive(Debug, Clone, Serialize)]
487pub struct PortUpdateRoot {
488 pub port: PortUpdate,
489}
490
491#[derive(Debug, Clone, Deserialize)]
493pub struct PortsRoot {
494 pub ports: Vec<Port>,
495}
496
497protocol_enum! {
498 #[doc = "Allowed conntrack helpers as defined [here](https://opendev.org/openstack/neutron/src/branch/master/neutron/conf/extensions/conntrack_helper.py)"]
499 enum Helper {
500 Amanda = "amanda",
501 FTP = "ftp",
502 H323 = "h323",
503 IRC = "irc",
504 NetbiosNS = "netbios-ns",
505 PPTP = "pptp",
506 SANE = "sane",
507 SIP = "sip",
508 SNMP = "snmp",
509 TFTP = "tftp"
510 }
511}
512
513#[derive(Debug, Clone, Copy, Deserialize, PartialEq)]
517pub struct ConntrackHelper {
518 pub helper: Helper,
520 pub protocol: NetworkProtocol,
522 pub port: u16,
524}
525
526#[non_exhaustive]
528#[derive(Debug, Clone, Deserialize, Serialize)]
529pub struct ExternalGateway {
530 pub network_id: NetworkRef,
532 #[serde(skip_serializing_if = "Option::is_none")]
534 pub enable_snat: Option<bool>,
535 #[serde(skip_serializing_if = "Vec::is_empty")]
537 pub external_fixed_ips: Vec<FixedIp>,
538}
539
540impl ExternalGateway {
541 pub fn new<N: Into<NetworkRef>>(external_network: N) -> ExternalGateway {
543 ExternalGateway {
544 network_id: external_network.into(),
545 enable_snat: None,
546 external_fixed_ips: Vec::new(),
547 }
548 }
549
550 pub(crate) async fn into_verified(self, session: &Session) -> Result<Self> {
551 Ok(ExternalGateway {
552 network_id: self.network_id.into_verified(session).await?,
553 ..self
554 })
555 }
556}
557
558#[derive(Debug, Serialize)]
560pub struct Routes {
561 pub routes: Vec<HostRoute>,
562}
563
564#[derive(Debug, Clone, Deserialize, Serialize)]
566pub struct Router {
567 pub admin_state_up: bool,
568 #[serde(default, skip_serializing)]
569 pub availability_zone_hints: Vec<String>,
570 #[serde(default, skip_serializing)]
571 pub availability_zones: Vec<String>,
572 #[serde(default, skip_serializing)]
573 pub conntrack_helpers: Vec<ConntrackHelper>,
574 #[serde(default, skip_serializing)]
575 pub created_at: Option<DateTime<FixedOffset>>,
576 #[serde(
577 deserialize_with = "empty_as_default",
578 default,
579 skip_serializing_if = "Option::is_none"
580 )]
581 pub description: Option<String>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 pub distributed: Option<bool>,
584 #[serde(
585 default,
586 skip_serializing_if = "Option::is_none",
587 rename = "external_gateway_info"
588 )]
589 pub external_gateway: Option<ExternalGateway>,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub flavor_id: Option<String>,
592 #[serde(skip_serializing_if = "Option::is_none")]
593 pub ha: Option<bool>,
594 #[serde(skip_serializing)]
595 pub id: String,
596 #[serde(
597 deserialize_with = "empty_as_default",
598 skip_serializing_if = "Option::is_none"
599 )]
600 pub name: Option<String>,
601 #[serde(default, skip_serializing_if = "Option::is_none")]
602 pub project_id: Option<String>,
603 #[serde(default, skip_serializing)]
604 pub revision_number: Option<u32>,
605 #[serde(skip_serializing_if = "Option::is_none")]
606 pub routes: Option<Vec<HostRoute>>,
607 #[serde(skip_serializing_if = "Option::is_none")]
608 pub service_type_id: Option<String>,
609 #[serde(skip_serializing)]
610 pub status: RouterStatus,
611 #[serde(skip_serializing)]
612 pub tags: Option<Vec<String>>,
613 #[serde(default, skip_serializing)]
614 pub updated_at: Option<DateTime<FixedOffset>>,
615}
616
617impl Default for Router {
618 fn default() -> Router {
619 Router {
620 admin_state_up: true,
621 availability_zones: vec![],
622 availability_zone_hints: vec![],
623 created_at: None,
624 conntrack_helpers: vec![],
625 description: None,
626 distributed: None,
627 external_gateway: None,
628 flavor_id: None,
629 ha: None,
630 id: String::new(),
631 name: None,
632 project_id: None,
633 revision_number: None,
634 routes: None,
635 service_type_id: None,
636 status: RouterStatus::Active,
637 tags: None,
638 updated_at: None,
639 }
640 }
641}
642
643impl Router {
644 pub(crate) async fn into_verified(self, session: &Session) -> Result<Self> {
645 Ok(Router {
646 external_gateway: match self.external_gateway {
647 Some(gw) => Some(gw.into_verified(session).await?),
648 None => None,
649 },
650 ..self
651 })
652 }
653}
654
655#[derive(Debug, Clone, Deserialize, Serialize)]
657pub struct RouterRoot {
658 pub router: Router,
659}
660
661#[derive(Debug, Clone, Default, Serialize)]
663pub struct RouterUpdate {
664 #[serde(skip_serializing_if = "Option::is_none")]
665 pub admin_state_up: Option<bool>,
666 #[serde(skip_serializing_if = "Option::is_none")]
667 pub description: Option<String>,
668 #[serde(skip_serializing_if = "Option::is_none")]
669 pub distributed: Option<bool>,
670 #[serde(
671 default,
672 skip_serializing_if = "Option::is_none",
673 rename = "external_gateway_info"
674 )]
675 pub external_gateway: Option<ExternalGateway>,
676 #[serde(skip_serializing_if = "Option::is_none")]
677 pub ha: Option<bool>,
678 #[serde(skip_serializing_if = "Option::is_none")]
679 pub name: Option<String>,
680 #[serde(skip_serializing_if = "Option::is_none")]
681 pub routes: Option<Vec<HostRoute>>,
682}
683
684#[derive(Debug, Clone, Serialize)]
686pub struct RouterUpdateRoot {
687 pub router: RouterUpdate,
688}
689
690#[derive(Debug, Clone, Deserialize)]
692pub struct RoutersRoot {
693 pub routers: Vec<Router>,
694}
695
696#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
698pub struct AllocationPool {
699 pub start: net::IpAddr,
701 pub end: net::IpAddr,
703}
704
705#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
707pub struct HostRoute {
708 pub destination: ipnet::IpNet,
710 #[serde(rename = "nexthop")]
712 pub next_hop: net::IpAddr,
713}
714
715#[derive(Debug, Clone, Deserialize, Serialize)]
717pub struct Subnet {
718 #[serde(default, skip_serializing_if = "Vec::is_empty")]
719 pub allocation_pools: Vec<AllocationPool>,
720 pub cidr: ipnet::IpNet,
721 #[serde(default, skip_serializing)]
722 pub created_at: Option<DateTime<FixedOffset>>,
723 #[serde(
724 deserialize_with = "empty_as_default",
725 default,
726 skip_serializing_if = "Option::is_none"
727 )]
728 pub description: Option<String>,
729 #[serde(rename = "enable_dhcp")]
730 pub dhcp_enabled: bool,
731 #[serde(default, skip_serializing_if = "Vec::is_empty")]
732 pub dns_nameservers: Vec<String>,
733 #[serde(default, skip_serializing_if = "Option::is_none")]
734 pub gateway_ip: Option<net::IpAddr>,
735 #[serde(default, skip_serializing_if = "Vec::is_empty")]
736 pub host_routes: Vec<HostRoute>,
737 #[serde(skip_serializing)]
738 pub id: String,
739 pub ip_version: IpVersion,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
741 pub ipv6_address_mode: Option<Ipv6Mode>,
742 #[serde(
743 default,
744 rename = "ipv6_ra_mode",
745 skip_serializing_if = "Option::is_none"
746 )]
747 pub ipv6_router_advertisement_mode: Option<Ipv6Mode>,
748 #[serde(
749 deserialize_with = "empty_as_default",
750 skip_serializing_if = "Option::is_none"
751 )]
752 pub name: Option<String>,
753 pub network_id: String,
754 #[serde(default, skip_serializing_if = "Option::is_none")]
755 pub project_id: Option<String>,
756 #[serde(default, skip_serializing)]
757 pub updated_at: Option<DateTime<FixedOffset>>,
758}
759
760impl Subnet {
761 pub(crate) fn empty(cidr: ipnet::IpNet) -> Subnet {
762 Subnet {
763 allocation_pools: Vec::new(),
764 cidr,
765 created_at: None,
766 description: None,
767 dhcp_enabled: true,
768 dns_nameservers: Vec::new(),
769 gateway_ip: None,
770 host_routes: Vec::new(),
771 id: String::new(),
772 ip_version: match cidr {
773 ipnet::IpNet::V4(..) => IpVersion::V4,
774 ipnet::IpNet::V6(..) => IpVersion::V6,
775 },
776 ipv6_address_mode: None,
777 ipv6_router_advertisement_mode: None,
778 name: None,
779 network_id: String::new(),
780 project_id: None,
781 updated_at: None,
782 }
783 }
784}
785
786#[derive(Debug, Clone, Serialize, Default)]
788pub struct SubnetUpdate {
789 #[serde(skip_serializing_if = "Option::is_none")]
790 pub allocation_pools: Option<Vec<AllocationPool>>,
791 #[serde(skip_serializing_if = "Option::is_none")]
792 pub description: Option<String>,
793 #[serde(rename = "enable_dhcp", skip_serializing_if = "Option::is_none")]
794 pub dhcp_enabled: Option<bool>,
795 #[serde(skip_serializing_if = "Option::is_none")]
796 pub dns_nameservers: Option<Vec<String>>,
797 #[serde(skip_serializing_if = "Option::is_none")]
798 pub gateway_ip: Option<net::IpAddr>,
799 #[serde(skip_serializing_if = "Option::is_none")]
800 pub host_routes: Option<Vec<HostRoute>>,
801 #[serde(skip_serializing_if = "Option::is_none")]
802 pub name: Option<String>,
803}
804
805#[derive(Debug, Clone, Deserialize, Serialize)]
807pub struct SubnetRoot {
808 pub subnet: Subnet,
809}
810
811#[derive(Debug, Clone, Serialize)]
813pub struct SubnetUpdateRoot {
814 pub subnet: SubnetUpdate,
815}
816
817#[derive(Debug, Clone, Deserialize)]
819pub struct SubnetsRoot {
820 pub subnets: Vec<Subnet>,
821}
822
823#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
824pub struct PortForwarding {
825 pub external_port: u16,
827 pub internal_ip_address: net::IpAddr,
829 pub internal_port: u16,
831 pub protocol: String,
833}
834
835#[derive(Debug, Clone, Deserialize, Serialize)]
837pub struct FloatingIp {
838 #[serde(default, skip_serializing)]
839 pub created_at: Option<DateTime<FixedOffset>>,
840 #[serde(
841 deserialize_with = "empty_as_default",
842 default,
843 skip_serializing_if = "Option::is_none"
844 )]
845 pub description: Option<String>,
846 #[serde(
847 deserialize_with = "empty_as_default",
848 default,
849 skip_serializing_if = "Option::is_none"
850 )]
851 pub dns_domain: Option<String>,
852 #[serde(
853 deserialize_with = "empty_as_default",
854 default,
855 skip_serializing_if = "Option::is_none"
856 )]
857 pub dns_name: Option<String>,
858 #[serde(default, skip_serializing_if = "Option::is_none")]
859 pub fixed_ip_address: Option<net::IpAddr>,
860 #[serde(skip_serializing_if = "::std::net::IpAddr::is_unspecified")]
861 pub floating_ip_address: net::IpAddr,
862 pub floating_network_id: String,
863 #[serde(skip_serializing)]
864 pub id: String,
865 #[serde(default)]
866 pub port_id: Option<String>,
867 #[serde(default, skip_serializing)]
868 pub port_forwardings: Vec<PortForwarding>,
869 #[serde(default, skip_serializing)]
870 pub router_id: Option<String>,
871 #[serde(skip_serializing)]
872 pub status: FloatingIpStatus,
873 #[serde(skip_deserializing, skip_serializing_if = "Option::is_none")]
874 pub subnet_id: Option<String>,
875 #[serde(default, skip_serializing)]
876 pub updated_at: Option<DateTime<FixedOffset>>,
877}
878
879#[derive(Debug, Clone, Serialize, Default)]
881pub struct FloatingIpUpdate {
882 #[serde(skip_serializing_if = "Option::is_none")]
883 pub description: Option<String>,
884 #[serde(skip_serializing_if = "Option::is_none")]
885 pub fixed_ip_address: Option<net::IpAddr>,
886 #[serde(skip_serializing_if = "Option::is_none")]
887 pub port_id: Option<Value>,
888}
889
890#[derive(Debug, Clone, Deserialize, Serialize)]
892pub struct FloatingIpRoot {
893 pub floatingip: FloatingIp,
894}
895
896#[derive(Debug, Clone, Serialize)]
898pub struct FloatingIpUpdateRoot {
899 pub floatingip: FloatingIpUpdate,
900}
901
902#[derive(Debug, Clone, Deserialize)]
904pub struct FloatingIpsRoot {
905 pub floatingips: Vec<FloatingIp>,
906}
907
908#[cfg(test)]
909mod test {
910 use super::*;
911
912 #[test]
913 fn test_parse_macaddr() {
914 let a: AllowedAddressPair = serde_json::from_value(
916 serde_json::json!({"ip_address":"0.0.0.0", "mac_address":"ab:aa:aa:aa:aa:aa"}),
917 )
918 .expect("Could not parse this JSON");
919 assert_eq!(
920 a.mac_address.expect("MAC address is missing").to_string(),
921 "AB:AA:AA:AA:AA:AA"
922 );
923
924 assert_eq!(
926 serde_json::to_value(&a)
927 .expect("Could not serialize")
928 .get("mac_address")
929 .expect("No mac_address")
930 .as_str()
931 .expect("No string found"),
932 "AB:AA:AA:AA:AA:AA"
933 );
934
935 let a: AllowedAddressPair =
937 serde_json::from_value(serde_json::json!({"ip_address":"0.0.0.0"}))
938 .expect("Cannot parse this JSON");
939 assert_eq!(a.mac_address, None);
940 }
941}