=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)

-- 
Posted via http://www.ruby-forum.com/.