Skip to content

Instantly share code, notes, and snippets.

@jof
Created April 18, 2013 23:58
Show Gist options
  • Save jof/5417132 to your computer and use it in GitHub Desktop.
Save jof/5417132 to your computer and use it in GitHub Desktop.
NETCONF-speaking script to poll a Juniper SRX NAT pool and stuff the metrics into Graphite
#!/usr/bin/env ruby
require 'rubygems'
require 'pry'
require 'socket'
$:.unshift '/home/jof/src/net-netconf/lib'
$:.unshift '/home/jof/src/net-ssh/lib'
require 'net/netconf'
require 'net/ssh'
require 'net/ssh/proxy/command'
jump_host = ARGV.shift
target = ARGV.shift
snat_pool = ARGV.shift
unless jump_host and target and snat_pool then
STDERR.puts "This needs a jump host, target, and snat pool name as arguments"
exit 1
end
poll_interval = 5
corp_jump_host = Net::SSH::Proxy::Command.new("ssh #{jump_host} nc %h %p")
netconf = Netconf::SSH.new(:target => target, :username => ENV['USER'], :port => 22)
netconf.trans_open(:proxy => corp_jump_host)
netconf.open
graphite_socket = TCPSocket.new('localhost', 2003)
at_exit do
netconf.close
graphite_socket.close
end
loop do
sleep poll_interval
cluster_status = netconf.rpc.get_chassis_cluster_status(:redundancy_group => '0')
nodes = cluster_status.xpath('//device-stats/device-name').map { |node| node.text }
statuses = cluster_status.xpath('//device-stats/redundancy-group-status').map { |status| status.text }
states = nodes.zip(statuses)
active_node = states.select { |state| state[1] == 'primary' }.flatten.first
pools = netconf.rpc.retrieve_source_nat_pool_information(:pool_name => snat_pool)
# FIXME: Detect <error> tags in RPC response when the remote node times out.
# FIXME: Maybe there is a better way to walk the <multi-routing-engine-item> nodes. xpath'ing a Nokogiri::XML::NodeSet seems to still require the full path from the root.
total_addresses = pools.xpath("//multi-routing-engine-item[re-name=\"#{active_node}\"]//total-pool-address").text # "16"
total_addresses = total_addresses.to_i
translation_range = pools.xpath("//multi-routing-engine-item[re-name=\"#{active_node}\"]//source-pool-port-translation").text # "[1024, 63487]"
translation_range = translation_range.gsub(/[\[\]]/,'').split(/,\s*/).map { |i| i.to_i }
translation_range = Range.new(*translation_range)
single_port_translations = pools.xpath("//multi-routing-engine-item[re-name=\"#{active_node}\"]//single-port").text
single_port_translations = single_port_translations.to_i
twin_port_translations = pools.xpath("//multi-routing-engine-item[re-name=\"#{active_node}\"]//twin-port").text
twin_port_translations = twin_port_translations.to_i
available_source_port_translations = translation_range.count * total_addresses
in_use_port_translations = single_port_translations + twin_port_translations
now = Time.now
metrics = { 'available' => available_source_port_translations,
'single_port_used' => single_port_translations,
'twin_port_used' => twin_port_translations,
'ports_used' => (single_port_translations + twin_port_translations) }
metrics.each do |metric, value|
# Change this metric name for your own Graphite environment
graphite_socket.puts "jof.nat_pools.#{target}.#{snat_pool}.#{metric} #{value} #{now.to_i}"
end
graphite_socket.flush
metrics.each do |metric, value|
puts "Reporting that jof.nat_pools.#{target}.#{snat_pool}.#{metric} is #{value} at #{now}"
end
end
#netconf.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment