Searching an array for a pattern derived from another array

Hi,
I’ve just started using Ruby last week and have a somewhat complex task
to
complete in a short amount of time and nobody local to ask.

Here is what I’m trying to find:
Given two arrays containing IP addresses like the ones below, how would
I
search for the first element in ‘servers’ that pattern matches and
element
in ‘nics’ where only the first 2 parts of the IP addresses need to
match,
like 192.168.. or 10.14.. or 98.139..

servers = Array[“192.168.0.251”,“10.14.0.142”,“98.139.180.149”]
nics = Array[“10.10.0.255”,“173.194.37.16”,“10.14.0.170”]

I’ve got some kind of beginning with the code below but I’m stuck. Any
hints?


log_server = “nothing”

until log_server != “nothing” servers.each |thisip| do

Consider only the first two parts of the ‘thisip’ IP address

(192.168..
or 10.14.. or 98.139..)

Compare it with each element in ‘nics’

Set ‘log_server’ equal to the first value of ‘thisip’ that pattern

matches an element in ‘nics’
log_server = thisip

end

hey there,

looks like you’re on the right track… check out regexps - they’re
easy to get started with, and i think they’ll help you a bunch…

hth-

  • j

Some helpful methods:

$ irb
irb(main):001:0> ip1 = “192.168.42.1”
=> “192.168.42.1”
irb(main):002:0> ip2 = “192.168.125.64”
=> “192.168.125.64”
irb(main):003:0> ip1.split(".")
=> [“192”, “168”, “42”, “1”]
irb(main):004:0> ip2.split(".")
=> [“192”, “168”, “125”, “64”]
irb(main):005:0> ip1.split(".")[0…1]
=> [“192”, “168”]
irb(main):006:0> ip1.split(".")[0…1] == ip2.split(".")[0…1]
=> true

martin

On Mon, Dec 12, 2011 at 3:41 PM, Chaim Keren-Tzion

On Tue, Dec 13, 2011 at 12:41 AM, Chaim Keren-Tzion
[email protected] wrote:

servers = Array[“192.168.0.251”,“10.14.0.142”,“98.139.180.149”]

Consider only the first two parts of the ‘thisip’ IP address (192.168..

or 10.14.. or 98.139..)

Compare it with each element in ‘nics’

Set ‘log_server’ equal to the first value of ‘thisip’ that pattern

matches an element in ‘nics’
log_server = thisip

end

I would approach this like this:

  1. Write a method which receives two arguments (servers and nics of
    course).
  2. Let the method start by preparing the data, i.e. #map both arrays
    into something which is quicker to match.
  3. iterate by doing servers_converted.each or maybe
    servers_converted.each_with_index and returning from the method on
    first match.
  4. return nil at the end of the method (nothing found) or raise an
    exception depending on the wanted semantics

Now, how to do the matching? I would not use regular expressions for
the matching as you really want to match numeric values. Conversion
could use ip.scan(/\d+/).map(&:to_i). You could use Array#[] to
obtain a two element Array from a four element Array.

You could as well search for a gem which deals with IP addresses.
Since the problem is so common chances are that such a beast exists.

Kind regards

robert

On Dec 13, 2011, at 5:12 AM, Robert K. wrote:

like 192.168.. or 10.14.. or 98.139..
until log_server != “nothing” servers.each |thisip| do

robert


remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Rather than search for a gem, look no further than the standard library
and you’ll come across the IPAddr library. (It’s been in there long
before 1.9.3 so you’re almost certain to have it.)

$ irb
irb(main):001:0> RUBY_VERSION
=> “1.9.3”
irb(main):002:0> require ‘ipaddr’
irb(main):003:0> servers = [“192.168.0.251”, “10.14.0.142”,
“98.139.180.149”].map {|ip| IPAddr.new(ip)}
irb(main):004:0> nics = [ “10.10.0.255”, “173.194.37.16”,
“10.14.0.170” ].map {|ip| IPAddr.new(“#{ip}/16”)}
irb(main):005:0> log_server = servers.detect {|ip| nics.any? {|nic|
nic.include? ip } }
=> #<IPAddr: IPv4:10.14.0.142/255.255.255.255>
irb(main):006:0> log_server.to_s
=> “10.14.0.142”

The /16 added to the mapping of nics is the number of bits in the
network mask.

So translating Robert’s approach:

  1. Write a methods that receives two arguments, servers and nics (as
    arrays of IP strings). (Optionally, a third argument that gives the bits
    in the netmask, perhaps defaulting to 16.)
  2. map the Strings to IPAddrs
  3. detect (find) the first server having an IPAddr that is included in
    the nic’s network.
  4. (there is no step 4 :wink:

-Rob

I must say that Ruby is very elegant! I’ve been scripting in bash for
years
but haven’t used oop languages nearly as much and this is beyond!

The code was needed for a chef cookbook that will set the servers in our
network to use the preferred connection to our syslog server. (Can’t
utilize DNS for this)
The three lines below will do all the decision making.

servers = node[:syslogserver][:ip].map {|ip| IPAddr.new(ip)}
nics = node.network.interfaces.map { |p, f| f[:addresses].keys
}.flatten.delete_if{|x| x =~ /(.):(.)/ }.map {|ip|
IPAddr.new("#{ip}/16")}
rsyslog_server = servers.detect {|ip| nics.any? {|nic| nic.include? ip
}
}.to_s

Note: “node[:syslogserver][:ip]” are attributes that I define in a
“role”
“node.network.interfaces.map { |p, f| f[:addresses].keys }.flatten” will
get me the mac addresses and ips on the client that chef is running on
then I clean it up by removing the mac addresses (and perhaps the IPV6
addresses that will show up one day?) with “delete_if{|x| x =~
/(.):(.)/
}”

Thanks guys!!

Chaim

On Tue, Dec 13, 2011 at 3:13 PM, Rob B.