Quagga: add some ospfv3 diagnostics calls to quagga.rb (#149)

* quagga: add some ospfv3 diagnostics

* Quagga (OSPFv3): Add database support

* Quagga: add more backend calls for OSPFv3

* Quagga: integrate into controller and configd
This commit is contained in:
Fabian Franz
2017-05-07 12:12:05 +02:00
committed by GitHub
parent 4bbfcf8a2f
commit 2b79bfd00f
3 changed files with 314 additions and 8 deletions
@@ -64,6 +64,12 @@ class DiagnosticsController extends ApiControllerBase
$backend = new Backend();
return array("response" => json_decode(trim($backend->configdRun("quagga ospf-$name"))));
}
private function get_ospf3_information($name)
{
$backend = new Backend();
return array("response" => json_decode(trim($backend->configdRun("quagga ospfv3-$name"))));
}
// OSPFv2
public function ospfoverviewAction()
{
return $this->get_ospf_information('overview');
@@ -84,6 +90,28 @@ class DiagnosticsController extends ApiControllerBase
{
return $this->get_ospf_information('interface');
}
// OSPFv3
public function ospfv3overviewAction()
{
return $this->get_ospf3_information('overview');
}
public function ospfv3neighborAction()
{
return $this->get_ospf3_information('neighbor');
}
public function ospfv3routeAction()
{
return $this->get_ospf3_information('route');
}
public function ospfv3databaseAction()
{
return $this->get_ospf3_information('database');
}
public function ospfv3interfaceAction()
{
return $this->get_ospf3_information('interface');
}
// General
private function get_general_information($name)
{
$backend = new Backend();
@@ -21,9 +21,13 @@ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=end
require 'json'
require 'shellwords'
require 'pp'
$QUAGGA_DEBUG = false
class VTYSH
def initialize(path = '/usr/local/bin/vtysh')
@path = path
@@ -189,6 +193,7 @@ class OSPF
# make sure there is an array to write in
current_if[:unparsed] ||= []
current_if[:unparsed] << line
puts line if $QUAGGA_DEBUG
end
end
end
@@ -229,7 +234,7 @@ class OSPF
db[router]['external_states'] ||= []
qta = nil
else
$stderr.puts "unknown heading"
puts "unknown heading" if $QUAGGA_DEBUG
end
else
if qta == nil
@@ -286,7 +291,7 @@ class OSPF
last_line = {ip: $1, cost: $2.to_i, area: $3, asbr: (", ASBR" == $4), type: 'R'}
route[heading] << last_line
else
#puts line
puts line if $QUAGGA_DEBUG
end
end
end
@@ -328,8 +333,7 @@ class OSPF
when ""
break
else
# debug
#puts line
puts line if $QUAGGA_DEBUG
end
end
# general overview has ended - now the area overviews come
@@ -358,7 +362,7 @@ class OSPF
when "Area has no authentication"
current_area[:auth] = "none"
else
#puts line
puts line if $QUAGGA_DEBUG
end
end
overview
@@ -420,12 +424,230 @@ class BGP
end
end
class OSPFv3
def initialize(sh)
@vtysh = sh
end
def overview
lines = @vtysh.execute("show ipv6 ospf6").lines
overview = {}
while line = lines.shift&.strip
case line
when /OSPFv3 Routing Process \((\d+)\) with Router-ID ([\d\.]+)/
overview[:router_id] = $2
overview[:routing_process] = $1.to_i
when /Initial SPF scheduling delay (\d+) millisec\(s\)/
overview[:initial_spf_scheduling_delay] = $1.to_i
# this line contains a typo in the output - I made it to work with and without
# this typo
when /(Min|Max)imum hold time between consecutive SPFs (\d+) milli?second\(s\)/
overview[:hold_time] ||= {}
overview[:hold_time][$1.downcase] = $2.to_i
when "This router is an ASBR (injecting external routing information)"
overview[:asbr] = true
when /SPF timer is (.*)/
overview[:spf_timer] = $1
when /Running (.*)/
overview[:running_time] = $1
when /Number of AS scoped LSAs is (\d+)/
overview[:number_as_scoped] = $1.to_i
when /Hold time multiplier is currently (\d+)/
overview[:current_hold_time_multipier] = $1.to_i
when /Number of areas in this router is (\d+)/
overview[:number_of_areas] = $1.to_i
when ""
break
else
# debug
puts line if $QUAGGA_DEBUG
end
end
# general overview has ended - now the area overviews come
overview[:areas] = {}
current_area = {}
while line = lines.shift&.strip
case line
when /^Area ([\d\.]*)/
current_area = {}
overview[:areas][$1] = current_area
when /Interface attached to this area: (.*)/
current_area[:interfaces] = $1.split(" ")
when /Number of Area scoped LSAs is (.*)/
current_area[:number_lsas] = $1.to_i
else
puts line if $QUAGGA_DEBUG
end
end
overview
end
def linkstate
lines = @vtysh.execute("show ipv6 ospf6 linkstate").lines
linkstate = {}
qta = nil
current_area = []
while line = lines.shift&.strip
case line
when /SPF Result in Area (.*)/
linkstate[$1] = current_area = []
qta = QuaggaTableReader.new(["Type","Router-ID", "Net-ID", "Rtr-Bits", "Options", "Cost"])
lines.shift
qta.read_headline(lines.shift)
else
if line.length > 10
current_area << qta.read_entry(line)
end
end
end
linkstate
end
def route
route = []
lines = @vtysh.execute("show ipv6 ospf6 route").lines
lines.each do |line|
f1, f2, network, gateway, interface, time = line.strip.split(/\s+/)
route << { f1: f1,
f2: f2,
network: network,
gateway: gateway,
interface: interface,
time: time }
end
route
end
def neighbor
qta = QuaggaTableReader.new(["Neighbor ID","Pri", "DeadTime", "State/IfState", "Duration I/F[State]"])
neighbor = []
nb = @vtysh.execute("show ipv6 ospf6 neighbor").lines
qta.read_headline(nb.shift.strip)
while line = nb.shift&.strip
puts line
if line.length > 10
tmp = qta.read_entry(line)
tmp['Pri'] = tmp['Pri'].to_i
neighbor << tmp
end
end
neighbor
end
def database
lines = @vtysh.execute("show ipv6 ospf6 database").lines
database = {}
mode = :none
area = ''
qta = :none
while line = lines.shift&.strip
case line
when /Area Scoped Link State Database \(Area (.*)\)/
mode = :scoped_link_db
database[:scoped_link_db] ||= {}
database[:scoped_link_db][$1] = area = []
qta = database_qta(lines)
when /I\/F Scoped Link State Database \(I\/F (\S+) in Area (.*)\)/
mode = :if_scoped_link_state
database[:if_scoped_link_state] ||= {}
database[:if_scoped_link_state][$1] ||= {}
area = database[:if_scoped_link_state][$1][$2] ||= []
qta= database_qta(lines)
when "AS Scoped Link State Database"
mode = :as_scoped
area = database[:as_scoped] ||= []
qta=database_qta(lines)
# note: i have no data for this but i think it looks like the others
else
if line.length > 10
area << qta.read_entry(line)
end
end
end
database
end
def interface
lines = @vtysh.execute("show ipv6 ospf6 interface").lines
int = {}
current_if = {}
while line = lines.shift
if line.length > 5
case line.strip
when /(\S+) is (down|up), type ([A-Z]+)/
current_if = int[$1] = {up: ($2 == "up" ? true : false),
type: $3,
enabled: true}
when /Interface ID: (\d+)/
current_if[:id] = $1.to_i
when /OSPF not enabled on this interface/
current_if[:enabled] = false
when /Instance ID (\d+), Interface MTU (\d+) \(autodetect: (\d+)\)/
current_if[:instance_id] = $1.to_i
current_if[:interface_mtu] = $2.to_i
current_if[:interface_mtu_autodetect] = $3.to_i
when "Internet Address:"
# ignore
when /(inet |inet6): (\S+)/
current_if[:IPv6] ||= []
current_if[:IPv4] ||= []
family = $1 == 'inet6' ? :IPv6 : :IPv4
address = $2
current_if[family] << address
when /MTU mismatch detection: (en|dis)abled/
current_if[:mtu_mismatch_detection] = $1 == 'en'
when /DR: (\S+) BDR: (\S+)/
current_if[:designated_router] = $1
current_if[:backup_designated_router] = $2
when /State (\S+), Transmit Delay (\d+) sec, Priority (\d+)/
current_if[:state] = $1
current_if[:transmit_delay] = $2.to_i
current_if[:priority] = $3.to_i
when /Number of I\/F scoped LSAs is (\d+)/
current_if[:number_if_scoped_lsas] = $1.to_i
when /(\d+) Pending LSAs for (\S+) in Time ([\d:]+)(?: (.*))/
current_if[:pending_lsas] ||= {}
current_if[:pending_lsas][$2] = {time: $3,
count: $1,
flags: $4}
when "Timer intervals configured:"
# ignore
when /Hello (\d+), Dead (\d+), Retransmit (\d+)/
current_if[:timers] = {hello: $1.to_i,
dead: $2.to_i,
retransmit: $3.to_i }
when /Area ID (\S+), Cost (\d+)/
current_if[:area_cost] ||= []
current_if[:area_cost] << {area: $1, cost: $2.to_i }
else
puts line if $QUAGGA_DEBUG
end
end
end
int
end
private
def database_qta(lines)
# DON'T REMOVE THE SPACES!!!
# For some reasons the fields are right aligned with the fields which makes it hard
# to parse. I don't know a better way to get the correct offset except automatically.
# (Detection of semantic of the fields)
qta = QuaggaTableReader.new(["Type", "LSId", "AdvRouter", " Age", " SeqNum"," Payload"])
lines.shift
qta.read_headline(lines.shift)
qta
end
end
require 'optparse'
options = {}
supported_sections = %w{general ospf}
OptionParser.new do |opts|
opts.banner = "Usage: #{__FILE__} -s section [section specific params]"
opts.on("-d", "--ospf-database") do |od|
#### OSPFv2
opts.on("-d", "--ospf-database", "Prints the OSPF Database") do |od|
options[:ospf_database] = od
end
opts.on("-r", "--ospf-route", 'print OSPF routing table') do |od|
@@ -440,15 +662,37 @@ OptionParser.new do |opts|
opts.on("-o", "--ospf-overview", "Print OSPF summary") do |od|
options[:ospf_overview] = od
end
#### OSPFv3
opts.on("-D", "--ospfv3-database", "Prints the OSPFv3 Database") do |od|
options[:ospfv3_database] = od
end
opts.on("-t", "--ospfv3-route", 'print OSPFv3 routing table') do |od|
options[:ospfv3_route] = od
end
opts.on("-I", "--ospfv3-interface", 'print OSPFv3 interface information') do |od|
options[:ospfv3_interface] = od
end
opts.on("-N", "--ospfv3-neighbor", 'Print OSPFv3 neighbors') do |od|
options[:ospfv3_neighbors] = od
end
opts.on("-O", "--ospfv3-overview", "Print OSPFv3 summary") do |od|
options[:ospfv3_overview] = od
end
#### general things about routing
opts.on("-R", "--general-routes", "Print Routing Table") do |od|
options[:general_routes] = od
end
### BGP
opts.on("-B", "--bgp-overview", "Print an overview of BGP") do |od|
options[:bgp_overview] = od
end
### program opts
opts.on("-H", "--human-readable", "Print the output human readable (not json)") do |od|
options[:human_readable] = od
end
opts.on("-X", "--debug", "Prints debug output") do |od|
$QUAGGA_DEBUG = true
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
@@ -457,6 +701,7 @@ end.parse!
# use the lib
sh = VTYSH.new
ospf = OSPF.new sh
ospfv3 = OSPFv3.new sh
bgp = BGP.new sh
general = General.new sh
@@ -465,9 +710,12 @@ options.keys.each do |k|
# if it is true
if options[k]
begin
if k.to_s.include? 'ospf'
if k.to_s.include? 'ospf_'
cmd = k.to_s.split('_').last
result[k] = ospf.send(cmd)
elsif k.to_s.include? 'ospfv3'
cmd = k.to_s.split('_').last
result[k] = ospfv3.send(cmd)
elsif k.to_s.include? 'general'
cmd = k.to_s.split('_').last
result[k] = general.send(cmd)
@@ -477,7 +725,7 @@ options.keys.each do |k|
end
rescue # do nothing on an error
result[k] = "error"
#puts $!
puts $! if $QUAGGA_DEBUG
end
end
end
@@ -70,6 +70,36 @@ parameters:
type:script_output
message: Print OSPF summary
[ospfv3-database]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospfv3-database
parameters:
type:script_output
message: Shows the OSPF database
[ospfv3-route]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospfv3-route
parameters:
type:script_output
message: print OSPF routing table
[ospfv3-interface]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospfv3-interface
parameters:
type:script_output
message: print OSPF interface information
[ospfv3-neighbor]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospfv3-neighbor
parameters:
type:script_output
message: Print OSPF neighbors
[ospfv3-overview]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --ospfv3-overview
parameters:
type:script_output
message: Print OSPF summary
[general-routes]
command:/usr/local/opnsense/scripts/quagga/quagga.rb --general-routes
parameters: