|
1 | | -use crate::ccm::ROOT_CCM_DIR; |
| 1 | +use crate::ccm::{IP_ALLOCATOR, ROOT_CCM_DIR}; |
2 | 2 |
|
| 3 | +use super::ip_allocator::NetPrefix; |
3 | 4 | use super::logged_cmd::{LoggedCmd, RunOptions}; |
4 | 5 | use super::node_config::NodeConfig; |
5 | 6 | use anyhow::{Context, Error}; |
6 | 7 | use scylla::client::session_builder::SessionBuilder; |
7 | | -use std::collections::{HashMap, HashSet}; |
8 | | -use std::fmt::Display; |
9 | | -use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr}; |
| 8 | +use std::collections::HashMap; |
| 9 | +use std::net::IpAddr; |
10 | 10 | use std::path::{Path, PathBuf}; |
11 | 11 | use std::process::Command; |
12 | 12 | use std::str::FromStr; |
13 | 13 | use std::sync::Arc; |
14 | 14 | use tempfile::TempDir; |
15 | | -use tokio::fs::{metadata, File}; |
16 | | -use tokio::io::{AsyncBufReadExt, BufReader}; |
| 15 | +use tokio::fs::metadata; |
17 | 16 | use tokio::sync::RwLock; |
18 | 17 | use tracing::{debug, info}; |
19 | 18 |
|
@@ -450,6 +449,13 @@ impl Drop for Cluster { |
450 | 449 | fn drop(&mut self) { |
451 | 450 | if !self.opts.do_not_remove_on_drop { |
452 | 451 | self.destroy_sync().ok(); |
| 452 | + |
| 453 | + // Return the IP prefix to the pool. |
| 454 | + IP_ALLOCATOR |
| 455 | + .lock() |
| 456 | + .expect("Failed to acquire IP_ALLOCATOR lock") |
| 457 | + .free_ip_prefix(&self.opts.ip_prefix) |
| 458 | + .expect("Failed to return ip prefix"); |
453 | 459 | } |
454 | 460 | } |
455 | 461 | } |
@@ -530,7 +536,10 @@ impl Cluster { |
530 | 536 | pub(crate) async fn new(opts: ClusterOptions) -> Result<Self, Error> { |
531 | 537 | let mut opts = opts.clone(); |
532 | 538 | if opts.ip_prefix.is_empty() { |
533 | | - opts.ip_prefix = NetPrefix::sniff_ipv4_prefix().await? |
| 539 | + opts.ip_prefix = IP_ALLOCATOR |
| 540 | + .lock() |
| 541 | + .expect("Failed to acquire IP_ALLOCATOR lock") |
| 542 | + .alloc_ip_prefix()? |
534 | 543 | }; |
535 | 544 |
|
536 | 545 | let config_dir = TempDir::with_prefix_in(&opts.name, &*ROOT_CCM_DIR) |
@@ -758,152 +767,3 @@ impl Cluster { |
758 | 767 | &self.nodes |
759 | 768 | } |
760 | 769 | } |
761 | | - |
762 | | -#[derive(Debug, Clone)] |
763 | | -pub(crate) struct NetPrefix(IpAddr); |
764 | | - |
765 | | -impl NetPrefix { |
766 | | - /// This method looks at all busy /24 networks in 127.0.0.0/8 and return first available |
767 | | - /// network goes as busy if anything is listening on any port and any address in this network |
768 | | - /// 127.0.0.0/24 is skipped and never returned |
769 | | - async fn sniff_ipv4_prefix() -> Result<NetPrefix, Error> { |
770 | | - let mut used_ips: HashSet<IpAddr> = HashSet::new(); |
771 | | - let file = File::open("/proc/net/tcp").await?; |
772 | | - let mut lines = BufReader::new(file).lines(); |
773 | | - while let Some(line) = lines.next_line().await? { |
774 | | - let parts: Vec<&str> = line.split_whitespace().collect(); |
775 | | - if let Some(ip_hex) = parts.get(1) { |
776 | | - let ip_port: Vec<&str> = ip_hex.split(':').collect(); |
777 | | - if let Some(ip_hex) = ip_port.first() { |
778 | | - if let Ok(ip) = u32::from_str_radix(ip_hex, 16) { |
779 | | - used_ips.insert( |
780 | | - Ipv4Addr::new(ip as u8, (ip >> 8) as u8, (ip >> 16) as u8, 0).into(), |
781 | | - ); |
782 | | - } |
783 | | - } |
784 | | - } |
785 | | - } |
786 | | - |
787 | | - for a in 0..=255 { |
788 | | - for b in 0..=255 { |
789 | | - if a == 0 && b == 0 { |
790 | | - continue; |
791 | | - } |
792 | | - let ip_prefix: IpAddr = Ipv4Addr::new(127, a, b, 0).into(); |
793 | | - if !used_ips.contains(&ip_prefix) { |
794 | | - return Ok(ip_prefix.into()); |
795 | | - } |
796 | | - } |
797 | | - } |
798 | | - Err(Error::msg("All ip ranges are busy")) |
799 | | - } |
800 | | - |
801 | | - fn empty() -> Self { |
802 | | - NetPrefix(IpAddr::V6(Ipv6Addr::UNSPECIFIED)) |
803 | | - } |
804 | | - |
805 | | - fn is_empty(&self) -> bool { |
806 | | - self.0.is_unspecified() |
807 | | - } |
808 | | - |
809 | | - fn from_string(value: String) -> Result<Self, AddrParseError> { |
810 | | - Ok(IpAddr::from_str(&value)?.into()) |
811 | | - } |
812 | | - |
813 | | - fn to_str(&self) -> String { |
814 | | - match self.0 { |
815 | | - IpAddr::V4(v4) => { |
816 | | - let octets = v4.octets(); |
817 | | - format!("{}.{}.{}.", octets[0], octets[1], octets[2]) |
818 | | - } |
819 | | - IpAddr::V6(v6) => { |
820 | | - let mut segments = v6.segments(); |
821 | | - segments[7] = 0; // Set last segment to 0 |
822 | | - let new_ip = Ipv6Addr::from(segments); |
823 | | - let formatted = new_ip.to_string(); |
824 | | - formatted.rsplit_once(':').map(|x| x.0).unwrap().to_string() + ":" |
825 | | - } |
826 | | - } |
827 | | - } |
828 | | - |
829 | | - fn to_ipaddress(&self, id: u16) -> IpAddr { |
830 | | - match self.0 { |
831 | | - IpAddr::V4(v4) => { |
832 | | - let mut octets = v4.octets(); |
833 | | - octets[3] = id as u8; |
834 | | - IpAddr::V4(Ipv4Addr::from(octets)) |
835 | | - } |
836 | | - IpAddr::V6(v6) => { |
837 | | - let mut segments = v6.segments(); |
838 | | - segments[7] = id; |
839 | | - IpAddr::V6(Ipv6Addr::from(segments)) |
840 | | - } |
841 | | - } |
842 | | - } |
843 | | -} |
844 | | - |
845 | | -impl Display for NetPrefix { |
846 | | - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
847 | | - write!(f, "{}", self.0) |
848 | | - } |
849 | | -} |
850 | | - |
851 | | -impl From<IpAddr> for NetPrefix { |
852 | | - fn from(ip: IpAddr) -> Self { |
853 | | - NetPrefix(ip) |
854 | | - } |
855 | | -} |
856 | | - |
857 | | -impl From<NetPrefix> for String { |
858 | | - fn from(value: NetPrefix) -> Self { |
859 | | - value.0.to_string() |
860 | | - } |
861 | | -} |
862 | | - |
863 | | -impl Default for NetPrefix { |
864 | | - fn default() -> Self { |
865 | | - NetPrefix::empty() |
866 | | - } |
867 | | -} |
868 | | - |
869 | | -#[cfg(test)] |
870 | | -mod tests { |
871 | | - use super::*; |
872 | | - |
873 | | - #[test] |
874 | | - fn test_ipv4_prefix() { |
875 | | - let ip = NetPrefix::from_string("192.168.1.100".to_string()).unwrap(); |
876 | | - assert_eq!(ip.to_str(), "192.168.1."); |
877 | | - } |
878 | | - |
879 | | - #[test] |
880 | | - fn test_ipv4_loopback() { |
881 | | - let ip = NetPrefix::from_string("127.0.0.1".to_string()).unwrap(); |
882 | | - assert_eq!(ip.to_str(), "127.0.0."); |
883 | | - } |
884 | | - |
885 | | - #[test] |
886 | | - fn test_ipv4_edge_case() { |
887 | | - let ip = NetPrefix::from_string("0.0.0.0".to_string()).unwrap(); |
888 | | - assert_eq!(ip.to_str(), "0.0.0."); |
889 | | - } |
890 | | - |
891 | | - #[test] |
892 | | - fn test_ipv6_prefix() { |
893 | | - let ip = |
894 | | - NetPrefix::from_string("2001:0db8:85a3:0000:0000:8a2e:0370:7334".to_string()).unwrap(); |
895 | | - assert_eq!(ip.to_str(), "2001:db8:85a3::8a2e:370:"); |
896 | | - } |
897 | | - |
898 | | - #[test] |
899 | | - fn test_ipv6_loopback() { |
900 | | - let ip = NetPrefix::from_string("::1".to_string()).unwrap(); |
901 | | - assert_eq!(ip.to_str(), "::"); |
902 | | - } |
903 | | - |
904 | | - #[test] |
905 | | - fn test_ipv6_shortened() { |
906 | | - let ip = NetPrefix::from_string("2001:db8::ff00:42:8329".to_string()).unwrap(); |
907 | | - assert_eq!(ip.to_str(), "2001:db8::ff00:42:"); |
908 | | - } |
909 | | -} |
0 commit comments