=begin
A note about the structure:
- a route map has a name and a list of entries
- a route map entry has a priority (in Cisco world anyway), a list
of matches, and a list of actions
So let’s make an object which holds the data for one route map entry
=end
class RouteMapEntry
attr_accessor :priority, :conditions, :actions
def initialize
@priority = nil
@conditions = []
@actions = []
end
end
=begin
Parsing a single route map entry is pretty simple; we can use regular
expressions to match the ‘match’ and ‘set’ lines, and convert them to
the relevant Juniper form before storing them in a RouteMapEntry.
For flexibility, we will write this parser so it assumes that the first
line (the route-map header) has already been parsed, and it returns the
first unparseable line. This allows us easily to assemble different
parsers
if we want to parse an entire Cisco config.
=end
def parse_cisco_routemap_entry(src, priority)
rme = RouteMapEntry.new
rme.priority = priority
loop do
line = src.gets
line.strip! if line # remove leading/trailing whitespace
case line
when /^match as-path (\d+)$/
rme.conditions << “as-path in aspath#{$1}”
when /^match ip address prefix-list (\S+)$/
rme.conditions << “destination in #{$1}”
when /^set local-preference (\d+)$/
rme.actions << “set local-preference #{$1}”
when /^set community (.*)$/
rme.actions << “set community (#{$1})”
# add more here as required
else
# EOF or any unrecognised line terminates this routemap entry
return [rme, line]
end
end
end
=begin
So now we parse a whole series of routemaps. In general a config
may have multiple named routemaps, so we build a hash of them.
=end
def parse_cisco_routemaps(src)
routemaps = {} # {“name” => [RouteMapEntry, RouteMapEntry, …]}
line = src.gets
while line
line.strip! # remove leading/trailing whitespace
case line
when /^!/, /^\s*$/
# skip comment or empty line
line = src.gets
when /^route-map (\S+) permit (\d+)$/
name, priority = $1, $2.to_i
rme, line = parse_cisco_routemap_entry(src, priority)
routemaps[name] ||= []
routemaps[name] << rme
else
$stderr.puts “Unrecognized line: #{line.inspect}”
line = src.gets
end
end
return routemaps
end
=begin
We need to be able to assemble a list of routemaps into Juniper form
=end
def write_juniper_routemaps(routemaps, dest=$stdout)
routemaps.each do |name,routemap_entries|
dest << “route-policy #{name}\n”
dest << " apply bogons\n"
routemap_entries = routemap_entries.sort_by { |e| e.priority }
cond = “if”
routemap_entries.each do |rme|
if rme.conditions.empty?
dest << " else\n"
else
dest << " #{cond} #{rme.conditions.join(" and “)} then\n”
end
rme.actions.each do |action|
dest << " #{action}\n"
end
break if rme.conditions.empty?
cond = “elseif”
end
dest << " endif\n"
dest << “end-policy\n”
end
end
Testing
src = <<EOS
route-map 6300-test-in permit 5
match as-path 1
set local-preference 90
set community 65000:1 65000:3 65000:1000 65000:3000 65000:3500
65000:3613
!
route-map 6300-test-in permit 10
match ip address prefix-list 6300-pref-low
set local-preference 90
set community 65000:1 65000:3 65000:1000 65000:3000 65000:3500
65000:3613
!
route-map 6300-test-in permit 5000
set local-preference 200
set community 65000:1 65000:3 65000:1000 65000:3000 65000:3500
65000:3613
EOS
require ‘stringio’
rms = parse_cisco_routemaps(StringIO.new(src))
write_juniper_routemaps(rms)
Real main program might look like this:
rms = parse_cisco_routemaps($stdin)
write_juniper_routemaps(rms)