Skip to content

Instantly share code, notes, and snippets.

@giuliano108
Created June 12, 2013 23:02
Show Gist options
  • Save giuliano108/5769889 to your computer and use it in GitHub Desktop.
Save giuliano108/5769889 to your computer and use it in GitHub Desktop.
Given a Cisco switch, enable/disable power on every PoE port that has an IP Phone connected to it.
#!/usr/bin/env ruby
# - Connects to a Cisco switch
# - Asks CDP which ports have an "IP Phone" connected to them
# - On every such port, turn PoE on or off
#
# -- giuliano@108.bz
require 'greenletters'
require 'trollop'
require 'yaml'
YAML::ENGINE.yamler='syck'
# Captures all output in a buffer, optionally sending it to stdout as well
class StdoutBufferTranscript
def initialize
@buffer = String.new
@to_stdout = false
end
def clear
@buffer = String.new
end
def buffer
StringIO.new @buffer
end
def stdout(onoff); @to_stdout = onoff; end
def on; @to_stdout = true; end
def off; @to_stdout = false; end
def <<(output)
$stdout << output if @to_stdout
@buffer << output
self
end
end
module CiscoPrompt
def set_prompt_name(name)
@cisco_prompt_name = name
end
def wait_for_prompt(suffix = '#')
wait_for :output, /^#{Regexp.escape(@cisco_prompt_name+suffix)}$/
end
end
opts = Trollop::options do
opt :ip, "Switch/Router telnet IP address", :type => :string
opt :pw, "Password", :type => :string
opt :ena, "Enable password", :type => :string
opt :doit, "Do stuff, instead of just showing which interfaces would be affected", :default => false
opt :poe, "Enable disable PoE on the phones ports", :type => :string, :default => 'on'
opt :file, "YAML file used for dumping/reading the affected ports list", :type => :string
end
[:ip, :pw, :ena].each do |o|
Trollop::die o, "must be supplied and it cannot be empty" unless !opts[o].nil? && !opts[o].empty?
end
Trollop::die :poe, "can either be `on' or `off'" unless ['on','off'].include? opts[:poe]
if opts[:doit]
if opts[:poe] == 'off'
Trollop::die :file, "must be a valid file name" unless !opts[:file].nil? && !opts[:file].empty?
Trollop::die :file, "refusing to overwrite existing file" if File.exist? opts[:file]
else
Trollop::die :file, "must be the name of an existing file" unless File.exist? opts[:file]
end
end
out = StdoutBufferTranscript.new
t = Greenletters::Process.new "telnet #{opts[:ip]}",
:timeout => 10,
:transcript => out
t.extend(CiscoPrompt)
t.start!
t.wait_for :output, /^Password:/
out.clear
t << opts[:pw] << "\n"
t.wait_for :output, />$/
name = out.buffer.readlines[-1].sub(/>$/,'')
puts "Device name is #{name}"
t.set_prompt_name name
t << "enable\n"
t.wait_for :output, /^Password:/
t << opts[:ena] << "\n"
t.wait_for_prompt
out.clear
out.on
if opts[:poe] == 'off'
t << "show cdp neighbors\n"
t.wait_for_prompt
phones = out.buffer.select {|l| l.match ' IP Phone '}.map {|l| f = l.split(/ +/); {:id => f[0], :ifc => f[1]+f[2]} }
else
if opts[:file] && File.exist?(opts[:file])
phones = YAML.load(IO.read(opts[:file]))
else
phones = []
end
end
out.clear
if phones.length == 0
puts "\n\n\n### No phones found/loaded from file"
exit
end
if !opts[:doit]
puts "\n\n\n### Found these phones/interfaces:"
p phones
exit
end
if opts[:poe] == 'off'
File.open(opts[:file], "w") do |f|
f.write(phones.to_yaml)
end
end
t << "configure terminal\n"
t.wait_for_prompt '(config)#'
mode = (opts[:poe] == 'on') ? 'auto' : 'never'
phones.each do |p|
puts "\n! #{p[:id]}\n"
t << "interface #{p[:ifc]}\n"
t.wait_for_prompt '(config-if)#'
t << "power inline #{mode}\n"
t.wait_for_prompt '(config-if)#'
t << "exit\n"
t.wait_for_prompt '(config)#'
sleep 1
end
t << "exit\n"
puts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment