Created
November 20, 2012 19:11
-
-
Save mat813/4120316 to your computer and use it in GitHub Desktop.
Bird 2 Dot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*.conf | |
*.dot | |
*.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'pp' | |
require 'treetop' | |
$-I << '.' | |
require 'bird_conf' | |
parser = BirdConfParser.new | |
parser.consume_all_input = false | |
servers = {} | |
ARGV.each do |f| | |
result = parser.parse(File.readlines(f).join()) | |
if result.empty? | |
puts parser.failure_reason | |
puts parser.failure_line | |
puts parser.failure_column | |
else | |
dot = f.gsub(/\.conf$/, '.dot') | |
png = f.gsub(/\.conf$/, '.png') | |
puts dot | |
router = nil | |
tables = [] | |
protocols = {} | |
protocol_tables = [] | |
pipes = [] | |
result.content.each do |what,n,v| | |
case what | |
when :router_id | |
router = n | |
when :table | |
tables << n | |
when :protocol_bgp, :protocol_device, :protocol_direct, :protocol_kernel, :protocol_static | |
protocols[what.to_s.sub(/^protocol_/, '')] ||= [] | |
protocols[what.to_s.sub(/^protocol_/, '')] << n | |
pipes << [n,n,v["table"], v["export"] || 'all'] | |
pipes << [n,v["table"],n, v["import"] || 'all'] | |
when :protocol_pipe | |
pipes << [n, v["table"], v["peer table"], v["export"] || 'all'] | |
pipes << [n, v["peer table"], v["table"], v["import"] || 'all'] | |
end | |
end | |
File.open(dot, 'w') do |fout| | |
fout << <<-eot | |
digraph lns { | |
// Router | |
node [shape=box, fontsize=18]; | |
"#{f.sub(/\.conf$/, '')} #{router}"; | |
node [shape=box, fontsize=14, style="filled"]; | |
table [fillcolor=yellow]; | |
bgp [fillcolor=green]; | |
direct [fillcolor=cyan]; | |
static [fillcolor=red]; | |
kernel [fillcolor=purple]; | |
other [fillcolor=brown]; | |
// Tables | |
node [shape=hexagon, fillcolor="yellow" style="rounded,filled", fontsize=14]; | |
eot | |
tables.each do |t| | |
fout << " \"#{t}\";\n" | |
end | |
fout << <<-eot | |
// Protocols | |
node [shape=box, style="rounded,filled", fontsize=14]; | |
eot | |
protocols.each do |k,ps| | |
ps.each do |p| | |
fout << " \"#{p}\"[label=\"#{p}" | |
case k | |
when 'bgp' | |
fout << "\", fillcolor=green" | |
when 'direct' | |
fout << "\", fillcolor=cyan" | |
when 'static' | |
fout << "\", fillcolor=red" | |
when 'kernel' | |
fout << "\", fillcolor=purple" | |
else | |
fout << "\\n#{k}\", fillcolor=brown" | |
end | |
fout << "];\n" | |
end | |
end | |
fout << <<-eot | |
// Pipes... | |
edge [fontsize=10] | |
eot | |
pipes.each do |n,t,p,e| | |
if e != 'none' | |
fout << " \"#{t}\" -> \"#{p}\"" | |
fout << "[label=\"#{e.gsub(/"/, '\\"')}\"]" | |
fout << ";\n" | |
end | |
end | |
fout << "}\n" | |
end | |
system("dot -Gcharset=latin1 -Tpng #{dot} > #{png}") | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'ip' | |
grammar BirdConf | |
include IP | |
rule entries | |
(SPACE / COMMENT / router_id / protocol / table / function / filter / listen)+ { | |
def content | |
# XXX bad way to get rid of comments and spaces. | |
elements.select {|e| e.respond_to?(:content)}.map {|e| e.content}+ [[:table, "master"]] | |
end | |
} | |
end | |
rule router_id | |
'router' SPACE1 'id' SPACE1 i:IPv4address SEMICOLON { | |
def content | |
[:router_id, i.text_value] | |
end | |
} | |
end | |
rule protocol | |
'protocol' SPACE1 protocol:unquoted_string name:(SPACE1 name:string from:(SPACE1 "from" from:string)?)? SPACE0 '{' SPACE0 | |
c:protocol_content | |
'}' { | |
def content | |
ret = [:"protocol_#{protocol.content}"] | |
unless name.empty? | |
ret << name.name.content | |
unless name.from.empty? | |
ret << name.from.from.content | |
end | |
end | |
detail = c.content | |
detail["table"] ||= "master" | |
ret << detail | |
return ret | |
end | |
} | |
end | |
rule protocol_content | |
(protocol_content_key / protocol_content_key_value / protocol_content_key_subvalue / protocol_content_key_value_subvalue)+ { | |
def content | |
Hash[elements.map(&:content)] | |
end | |
} | |
end | |
rule protocol_content_key | |
key:protocol_key SEMICOLON { | |
def content | |
[key.text_value, 1] | |
end | |
} | |
end | |
rule protocol_content_key_value | |
key:protocol_key SPACE1 value:value SEMICOLON { | |
def content | |
[key.text_value, value.content] | |
end | |
} | |
end | |
rule protocol_content_key_subvalue | |
key:protocol_key SPACE0 '{' SPACE0 value_ext:protocol_content '}' SEMICOLON { | |
def content | |
[key.text_value, value_ext.content] | |
end | |
} | |
end | |
rule protocol_content_key_value_subvalue | |
key:protocol_key SPACE1 value:value SPACE0 '{' SPACE0 value_ext:protocol_content '}' SEMICOLON { | |
def content | |
[key.text_value, [value.content, value_ext.content]] | |
end | |
} | |
end | |
# only multiwords keywords defined here, single words are catched by | |
# unquoted_string. | |
rule protocol_key | |
# global | |
'router' SPACE1 'id' / 'peer' SPACE1 'table' | |
/ ('import' / 'export') ( SPACE1 'limit' / '' ) | |
/ ('generate' / 'accept') SPACE1 ('from' / 'to') | |
# bgp | |
/ 'source' SPACE1 'address' / 'next' SPACE1 'hop' SPACE1 'self' / 'missing' SPACE1 'lladdr' / 'igp' SPACE1 'table' / 'ttl' SPACE1 'security' / 'rr' SPACE1 'client' / 'rr' SPACE1 'cluster' SPACE1 'id' / 'rs' SPACE1 'client' / 'enable' SPACE1 'route' SPACE1 'refresh' / 'interpret' SPACE1 'communities' / 'enable' SPACE1 'as4' / 'advertise' SPACE1 'ipv4' / 'route' SPACE1 'limit' / 'disable' SPACE1 'after' SPACE1 'error' / 'hold' SPACE1 'time' / 'startup' SPACE1 'hold' SPACE1 'time' / 'keeplive' SPACE1 'time' / 'connect' SPACE1 'retry' SPACE1 'time' / 'start' SPACE1 'delay' SPACE1 'time' / 'error' SPACE1 'wait' SPACE1 'time' / 'error' SPACE1 'forget' SPACE1 'time' / 'path' SPACE1 'metric' / 'med' SPACE1 'metric' / 'deterministic' SPACE1 'med' / 'igp' SPACE1 'metric' / 'prefer' SPACE1 'older' / 'default' SPACE1 'bgp_med' / 'default' SPACE1 'bgp_local_pref' | |
# device | |
/ 'scan' SPACE1 'time' | |
# kernel | |
/ 'scan' SPACE1 'time' / 'device' SPACE1 'routes' / 'kernel' SPACE1 'table' | |
# static | |
/ 'igp' SPACE1 'table' | |
# bordel | |
/ unquoted_string | |
end | |
rule table | |
'table' SPACE1 table:unquoted_string SEMICOLON { | |
def content | |
[:table, table.content] | |
end | |
} | |
end | |
rule function | |
# add arguments | |
'function' SPACE1 function:unquoted_string '(' ')' SPACE0 | |
# add variables | |
'{' | |
c:function_content | |
'}' { | |
def content | |
[:function, function.content, c.text_value] | |
end | |
} | |
end | |
rule function_content | |
(!'}' .)* / '{' function_content '}' | |
end | |
rule filter | |
'filter' SPACE1 filter:unquoted_string SPACE0 | |
'{' | |
c:filter_content | |
'}' { | |
def content | |
[:filter, filter.content, c.text_value] | |
end | |
} | |
end | |
rule filter_content | |
(!'}' .)* / '{' filter_content '}' | |
end | |
rule listen | |
'listen' SPACE1 'bgp' SPACE1 value:value SEMICOLON { | |
def content | |
[:listen, value.content] | |
end | |
} | |
end | |
rule CIDR | |
(IPv4address / IPv6address) ('/' [0-9]+)? { | |
require 'ipaddr' | |
def content | |
IPAddr.new(text_value) | |
end | |
} | |
end | |
rule IP | |
(IPv4address / IPv6address) { | |
require 'ipaddr' | |
def content | |
[:router_id, IPAddr.new(text_value)] | |
end | |
} | |
end | |
# special string | |
rule value | |
quoted_string / generic_value | |
end | |
rule generic_value | |
(![;{] .)+ { | |
def content | |
text_value | |
end | |
} | |
end | |
# content is taken care of by each string type. | |
rule string | |
unquoted_string / quoted_string | |
end | |
rule unquoted_string | |
[a-zA-Z0-9_]+ { | |
def content | |
text_value | |
end | |
} | |
end | |
rule quoted_string | |
'"' s:('\"' / !'"' .)* '"' { | |
def content | |
s.text_value | |
end | |
} | |
end | |
rule SEMICOLON | |
SPACE0 ';' COMMENT_OR_SPACE | |
end | |
rule COMMENT_OR_SPACE | |
SPACE0 COMMENT? SPACE0 | |
end | |
rule SPACE1 | |
SPACE+ | |
end | |
rule SPACE0 | |
SPACE* | |
end | |
rule SPACE | |
[ \t\r\n] | |
end | |
rule COMMENT | |
D_COMMENT / C_COMMENT | |
end | |
rule D_COMMENT | |
'#' c:(!EOL .)* EOL | |
end | |
rule C_COMMENT | |
'/*' | |
c:( | |
!'*/' | |
(. / "\n") | |
)* | |
'*/' | |
end | |
rule EOL | |
[\r\n] | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env tt | |
# | |
# Author:: Iñaki Baz Castillo <[email protected]> | |
# Copyright:: 2008 Iñaki Baz Castillo | |
# License:: Public Domain | |
# | |
# ABNF syntax for IPv4 and IPv6 is defined in Appendix A of RFC 3986: | |
# http://tools.ietf.org/html/rfc3986#appendix-A | |
grammar IP | |
rule IPv4address | |
DIGIT DIGIT? DIGIT? '.' DIGIT DIGIT? DIGIT? '.' DIGIT DIGIT? DIGIT? '.' DIGIT DIGIT? DIGIT? | |
end | |
rule IPv6address | |
( h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' ls32 ) | |
/ ( '::' h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' ls32 ) | |
/ ( h16? '::' h16 ':' h16 ':' h16 ':' h16 ':' ls32 ) | |
/ ( ( '::' / h16 '::' / h16 ':' h16 '::' ) h16 ':' h16 ':' h16 ':' ls32 ) | |
/ ( ( '::' / h16 '::' / h16 ':' h16 '::' / h16 ':' h16 ':' h16 '::' ) h16 ':' h16 ':' ls32 ) | |
/ ( ( '::' / h16 '::' / h16 ':' h16 '::' / h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 '::' ) h16 ':' ls32 ) | |
/ ( ( '::' / h16 '::' / h16 ':' h16 '::' / h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' ) ls32 ) | |
/ ( ( '::' / h16 '::' / h16 ':' h16 '::' / h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' ) h16 ) | |
/ ( '::' / h16 '::' / h16 ':' h16 '::' / h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' / h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' h16 ':' h16 '::' ) | |
end | |
rule h16 | |
HEXDIG HEXDIG? HEXDIG? HEXDIG? | |
end | |
rule ls32 | |
( h16 ":" h16 ) / IPv4address | |
end | |
rule HEXDIG | |
[\x30-\x39a-fA-F] | |
end | |
rule DIGIT | |
[\x30-\x39] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment