diff --git a/src/network/mod.rs b/src/network/mod.rs index de72463e1c..bbee0d9a09 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -27,9 +27,9 @@ pub use self::networks::{Network, NetworkQuery, NewNetwork}; pub use self::ports::{NewPort, Port, PortIpAddress, PortIpRequest, PortQuery}; pub use self::protocol::{ AllocationPool, AllowedAddressPair, ConntrackHelper, ExternalGateway, FloatingIpSortKey, - FloatingIpStatus, Helper, HostRoute, IpVersion, Ipv6Mode, NetworkProtocol, NetworkSortKey, - NetworkStatus, PortExtraDhcpOption, PortForwarding, PortSortKey, RouterSortKey, RouterStatus, - SubnetSortKey, + FloatingIpStatus, Helper, HostRoute, IpVersion, Ipv6Mode, MacAddress, NetworkProtocol, + NetworkSortKey, NetworkStatus, PortExtraDhcpOption, PortForwarding, PortSortKey, RouterSortKey, + RouterStatus, SubnetSortKey, }; pub use self::routers::{NewRouter, Router, RouterQuery}; pub use self::subnets::{NewSubnet, Subnet, SubnetQuery}; diff --git a/src/network/ports.rs b/src/network/ports.rs index ec08b2966d..fd3f950b91 100644 --- a/src/network/ports.rs +++ b/src/network/ports.rs @@ -22,7 +22,6 @@ use std::time::Duration; use async_trait::async_trait; use chrono::{DateTime, FixedOffset}; use futures::stream::{Stream, TryStreamExt}; -use macaddr::MacAddr6; use super::super::common::{ NetworkRef, PortRef, Refresh, ResourceIterator, ResourceQuery, SecurityGroupRef, SubnetRef, @@ -31,7 +30,7 @@ use super::super::session::Session; use super::super::utils::Query; use super::super::waiter::DeletionWaiter; use super::super::{Result, Sort}; -use super::{api, protocol, Network, Subnet}; +use super::{api, protocol, MacAddress, Network, Subnet}; /// A query to port list. #[derive(Clone, Debug)] @@ -211,12 +210,12 @@ impl Port { transparent_property! { #[doc = "MAC address of the port."] - mac_address: MacAddr6 + mac_address: MacAddress } update_field! { #[doc = "Update the MAC address (admin-only)."] - set_mac_address, with_mac_address -> mac_address: MacAddr6 + set_mac_address, with_mac_address -> mac_address: MacAddress } transparent_property! { @@ -573,7 +572,7 @@ impl NewPort { creation_inner_field! { #[doc = "Set MAC address for the port (generated otherwise)."] - set_mac_address, with_mac_address -> mac_address: MacAddr6 + set_mac_address, with_mac_address -> mac_address: MacAddress } creation_inner_field! { diff --git a/src/network/protocol.rs b/src/network/protocol.rs index 4703700e71..6bfac0ed9e 100644 --- a/src/network/protocol.rs +++ b/src/network/protocol.rs @@ -22,9 +22,8 @@ use std::net; use std::ops::Not; use chrono::{DateTime, FixedOffset}; -use macaddr::MacAddr6; use osauth::common::empty_as_default; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use super::super::common::{NetworkRef, SecurityGroupRef}; @@ -330,12 +329,56 @@ pub struct FixedIp { pub subnet_id: String, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Ord, PartialOrd, Hash)] +pub struct MacAddress(macaddr::MacAddr6); + +impl MacAddress { + pub fn is_nil(&self) -> bool { + self.0.is_nil() + } +} + +impl std::ops::Deref for MacAddress { + type Target = macaddr::MacAddr6; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::str::FromStr for MacAddress { + type Err = macaddr::ParseError; + + fn from_str(s: &str) -> std::result::Result { + Ok(Self(s.parse::()?)) + } +} + +impl Serialize for MacAddress { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for MacAddress { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + s.parse().map_err(serde::de::Error::custom) + } +} + /// A port's IP address. #[derive(Debug, Clone, Deserialize, Serialize, Copy)] pub struct AllowedAddressPair { pub ip_address: net::IpAddr, #[serde(skip_serializing_if = "Option::is_none")] - pub mac_address: Option, + pub mac_address: Option, } /// A port. @@ -382,8 +425,8 @@ pub struct Port { pub fixed_ips: Vec, #[serde(skip_serializing)] pub id: String, - #[serde(skip_serializing_if = "MacAddr6::is_nil")] - pub mac_address: MacAddr6, + #[serde(skip_serializing_if = "MacAddress::is_nil")] + pub mac_address: MacAddress, #[serde( deserialize_with = "empty_as_default", skip_serializing_if = "Option::is_none" @@ -420,7 +463,7 @@ pub struct PortUpdate { #[serde(skip_serializing_if = "Option::is_none")] pub fixed_ips: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub mac_address: Option, + pub mac_address: Option, #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -855,3 +898,38 @@ pub struct FloatingIpUpdateRoot { pub struct FloatingIpsRoot { pub floatingips: Vec, } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse_macaddr() { + // Test that a JSON deserialisation of MAC addresses work + let a: AllowedAddressPair = serde_json::from_value( + serde_json::json!({"ip_address":"0.0.0.0", "mac_address":"ab:aa:aa:aa:aa:aa"}), + ) + .expect("Could not parse this JSON"); + assert_eq!( + a.mac_address.expect("MAC address is missing").to_string(), + "AB:AA:AA:AA:AA:AA" + ); + + // Test that a JSON serialisation of MAC addresses work + assert_eq!( + serde_json::to_value(&a) + .expect("Could not serialize") + .get("mac_address") + .expect("No mac_address") + .as_str() + .expect("No string found"), + "AB:AA:AA:AA:AA:AA" + ); + + // Test that missing MAC addresses are parsed as None + let a: AllowedAddressPair = + serde_json::from_value(serde_json::json!({"ip_address":"0.0.0.0"})) + .expect("Cannot parse this JSON"); + assert_eq!(a.mac_address, None); + } +}