0

Actually, this is for a redis cluster, I have 3 master ips and 3 slave ips, need to produce 3 pairs of IP addresses where the addresses in the pair are in different /24 subnets.

Or in other words, given a list of n IP addresses (n being an even number, and not more than half of the IP addresses being in the same /24 subnet), how to partition them into n/2 pairs where the two addresses in each pair are in different /24 subnets?

The pairs should be stored as key and values of an ip_map associative array. For example, given a list of IP addresses stored in a $ips array:

ips=( "172.211.91.63" "172.211.90.61" "172.211.91.30" "172.211.90.173" "172.211.89.233" "172.211.89.166" ) 

The result could be:

declare -A ip_map=( [172.211.91.63]=172.211.90.61 [172.211.91.30]=172.211.89.233 [172.211.90.173]=172.211.89.166 ) 
0

1 Answer 1

1

Intuitively, I'd think that if you sort your IP addresses into buckets, one for each subnet, and keep picking 2 from the 2 fullest buckets, that should allow you to pair them all. Could be with something like:

perl -e ' push @{$s{s/\.\d+$//r}}, $_ for @ARGV; @l = values %s; for ($n = @ARGV; $n > 0; $n -= 2) { @l = sort {@$b <=> @$a} @l; printf "ip_map[%s]=%s\n", pop(@{$l[0]}), pop(@{$l[1]}); }' -- "${ips[@]}" 

Which on your sample gives:

ip_map[172.211.91.30]=172.211.89.166 ip_map[172.211.90.173]=172.211.91.63 ip_map[172.211.90.61]=172.211.89.233 
  • code for @ARGV loops over the parameters given to the inline script (the -expression) using the default $_ variable as the loop variable. A shorter form of the for (@ARGV) {code}.
  • s/\.\d+$//r (which acts on $_ by default) removes the .<digits> part at the end, but with the r flag, the result is returned instead of being stored back into $_. So that expands to the /24 subnet part of the IP address.
  • In the first line, we build the %s associative array (aka hash in perl), where $s{subnet} is a reference to the list of IP addresses in that subnet. @{that} dereferences it so we can push the IP address ($_) onto the list.
  • @l = values %s: gets the values of the hash, that is references to our bucket lists into a @l list.
  • then, n/2 times, we pluck two IP addresses from the 2 largest subnets, sorting them by size first (when a list is used in scalar context, like in @$b <=> @$a, it expands to the number of elements in it, so we compare the length of the lists to sort them) and then popping one from the first and second list (pop(@{$l[...]})).

You would evaluate the output in bash (with source <(that-code) or eval "$(that-code)"), but you might as well use perl for the whole thing, shells (especially bash) are not very good at programming.

If I had to use a shell, I would use zsh instead of bash, where something equivalent could look like:

typeset -A s ip_map for ip ($ips) s[${ip%.*}]="x$s[${ip%.*}] $ip" l=( $s ) repeat $#ips/2 { l=( ${(O)l} ) ip_map[${l[1][(w)-1]}]=${l[2][(w)-1]} l[1]=${l[1]#x} l[2]=${l[2]#x} l[1]=${l[1]% *} l[2]=${l[2]% *} } 

zsh associative arrays, like bash's can't contain lists, here we're storing the list of IP addresses space separated (so we can use (w)ord-based indexing later on) in the values of the associative array and prefixed with a string of xs (used a bit like 𝍩 𝍪 𝍫 𝍬 𝍸 tally marks), one per IP address so it looks like:

typeset -A s=( [172.211.89]='xx 172.211.89.233 172.211.89.166' [172.211.90]='xx 172.211.90.61 172.211.90.173' [172.211.91]='xx 172.211.91.63 172.211.91.30' ) 

So the ${(O)l} which we use to Order the list in reverse lexically sorts by number of elements. The popping is done by extracting and removing the last IP addresses in those buckets and remove one x from the start.

5
  • 1
    This did work!I'm not familiar with perl,could you please explain further? push @{$s{s/\.\d+$//r}} this means remove the last . and the following digits and then push ips into hash?And I don't understand this part for ($n = @ARGV; $n; $n -= 2) { @l = sort {@$b <=> @$a} @l; thanks Commented Oct 9, 2024 at 5:34
  • @pengxiao, see edit. Commented Oct 9, 2024 at 5:39
  • 1
    Thanks Stéphane for your patience and professionalism,this really solved my problem. Commented Oct 9, 2024 at 5:45
  • I will use the perl code in a bash script,as I just need those paired ips to create redis cluster Commented Oct 9, 2024 at 7:15
  • Hi Stephane,could you pleaset take further look regargding this,I have posted another question.unix.stackexchange.com/questions/785014/… Commented Oct 13, 2024 at 14:40

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.