From 2b72e13052c5316358bcee17476c4728c476e61e Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Tue, 19 Sep 2023 14:47:25 +0100 Subject: [PATCH 1/2] Port from eui48 to macaddr eui48 has a dependency on the deprecated rustc-serialize package. --- Cargo.toml | 2 +- src/network/ports.rs | 8 ++++---- src/network/protocol.rs | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92e55e2c6a..b020d30c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ rustls = ["reqwest/rustls-tls", "osauth/rustls"] async-stream = "^0.3" async-trait = "^0.1" chrono = { version = "^0.4", features = ["serde"] } -eui48 = { version = "^1.0", features = ["disp_hexstring", "serde"] } +macaddr = { version = "^1.0", features = ["serde_std"]} futures = "^0.3" ipnet = { version = "^2.0", features = ["serde"] } log = "^0.4" diff --git a/src/network/ports.rs b/src/network/ports.rs index a835714dd7..ec08b2966d 100644 --- a/src/network/ports.rs +++ b/src/network/ports.rs @@ -21,8 +21,8 @@ use std::time::Duration; use async_trait::async_trait; use chrono::{DateTime, FixedOffset}; -use eui48::MacAddress; use futures::stream::{Stream, TryStreamExt}; +use macaddr::MacAddr6; use super::super::common::{ NetworkRef, PortRef, Refresh, ResourceIterator, ResourceQuery, SecurityGroupRef, SubnetRef, @@ -211,12 +211,12 @@ impl Port { transparent_property! { #[doc = "MAC address of the port."] - mac_address: MacAddress + mac_address: MacAddr6 } update_field! { #[doc = "Update the MAC address (admin-only)."] - set_mac_address, with_mac_address -> mac_address: MacAddress + set_mac_address, with_mac_address -> mac_address: MacAddr6 } transparent_property! { @@ -573,7 +573,7 @@ impl NewPort { creation_inner_field! { #[doc = "Set MAC address for the port (generated otherwise)."] - set_mac_address, with_mac_address -> mac_address: MacAddress + set_mac_address, with_mac_address -> mac_address: MacAddr6 } creation_inner_field! { diff --git a/src/network/protocol.rs b/src/network/protocol.rs index a420a61d29..4703700e71 100644 --- a/src/network/protocol.rs +++ b/src/network/protocol.rs @@ -22,7 +22,7 @@ use std::net; use std::ops::Not; use chrono::{DateTime, FixedOffset}; -use eui48::MacAddress; +use macaddr::MacAddr6; use osauth::common::empty_as_default; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -335,7 +335,7 @@ pub struct FixedIp { 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 +382,8 @@ pub struct Port { pub fixed_ips: Vec, #[serde(skip_serializing)] pub id: String, - #[serde(skip_serializing_if = "MacAddress::is_nil")] - pub mac_address: MacAddress, + #[serde(skip_serializing_if = "MacAddr6::is_nil")] + pub mac_address: MacAddr6, #[serde( deserialize_with = "empty_as_default", skip_serializing_if = "Option::is_none" @@ -420,7 +420,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")] From 511ae0d965f592110eb501768aa880d98b835590 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Sun, 29 Oct 2023 11:11:59 +0000 Subject: [PATCH 2/2] Wrap macaddr::MacAddr6 in newtype MacAddress This allows us to reimplement the Serialize and Deserialize traits to make use of the hex-style format. --- src/network/mod.rs | 6 +-- src/network/ports.rs | 9 ++-- src/network/protocol.rs | 96 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 14 deletions(-) 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..d1d8308f17 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,62 @@ 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::fmt::Display for MacAddress { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +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 +431,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 +469,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 +904,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); + } +}