in reply to Template-based router configuration audit

What exactly do you want to test? The templating engine?

If you want to test a self-written templating engine, do yourself a favor and use an already tested one from CPAN: There is Template::Toolkit or the smaller Text::Template or Template::Simple. They should already be well tested

What do you want to do? Revert the config file to the template and compare? Or change the template to a config file and compare?

If you have the actual data that was used to create the config file, just use the second version and a different template package to create the same config file and compare

The first version on the other hand does not need the actual data. But your regex makes no sense:$mask_tmp =~ s/__REGEX__/[^\\n]+/g; (I repeat it here because you forgot code tags arround it in your post). You can't substitute __REGEX__ with a match expression. How should the machine know what you want to put in there.

If you meant $mask_tmp =~ s/[^\\n]+/__REGEX__/g; instead, it would replace anything, so lets change that to $mask_tmp =~ s/interface \(\n+/interface (__REGEX__/g;

The additional problem that you might have more interfaces in the filled in config file could be corrected with the following hacks for example:

1. Count lines that begin with interface in the config file and add lines appropriately to the template file.

2. Parse the config file line by line and match it against the template. This would mean "don't ever change your template or you have to edit your script too"

OR you could do a clean solution: Use a template library. Or if you are paranoid, use two and compare their output

Replies are listed 'Best First'.
Re^2: Template-based router configuration audit
by ccie (Initiate) on Nov 03, 2010 at 10:22 UTC
    Sorry, I realize my explanation was not clear enough.

    Inventory, such as IPs, neighbors, interfaces, etc., is not known in advance. So the Template module not really useful IMO. I want to collect inventory from device configurations, and later import as csv in excel, where simple sort by desired column could outline anomalies. By scanning device configurations against "templates" I make sure that configuration sections are correct, and at the same time acquire inventory.

    I wanted this task to b simple for end user, hence no regex in the "template" (or mask) file.

    The template example above is transformed in following regex string $mask_tmp:

    (mpls ldp) (router-id) ([^\n]+) nsr graceful-restart backoff 5 10 session protection (neighbor) ([^\n]+) password encrypted [^\n]+ (neighbor) ([^\n]+) password encrypted [^\n]+ (neighbor) ([^\n]+) password encrypted [^\n]+ igp sync delay 10 label allocate for LLAF ! log neighbor nsr ! interface ([^\n]+) ! interface ([^\n]+) ! !
    And then I loop (mask can be found multiple times in the configuration) this mask through device configuration stored in $current_cfg as follows:
    while (@MASK_VARIABLES = $current_cfg =~ /$mask_tmp/s) { $MASK_VARIABLES = join(",",@MASK_VARIABLES); $MASK_SEEN{$hostname}{$mask} = "YES"; $MASK_COUNT{$hostname}{$mask}++; # ($MASK_VARIABLES[1] =~ /\w+/) && do { print "$hostname,$MASK_VARIABLES\n"; }; # # remove identified cfg section from router configuration # $current_cfg =~ s/$mask_tmp//s; }
    Result is the following:
    - inventories in .csv on stdout (for further analyzing in excel)
    - summary view (code not shown above) where I see which and how many times given template has been matched in each device configuration
    - remaining of $current_cfg stored in a "leftover" file, where I can see all commands not identifed by above process (this allows network operator to asses any anomalies - ie. config sections not compliant with the template)

      Ok, now I get it.

      Your idea is clever but smells a bit like a hack. Whenever the config file changes minimally (for example an additional space or empty line somewhere) your method breaks. And as you have already found out it can't cope with a variable number of lines.

      With a bit more complexity you might still get this to work, by doing the parsing piece by piece (aka chunk by chunk). Your end user would have to split the static lines from the dynamic and give you something like this:

      Chunk 1: (mpls ldp) Chunk 2: (router-id) (__REGEX__) ... Chunk 6: ! interface (__REGEX__)

      Your end user would have to know that Chunk 6 has to include the line with the '!' because it is part of the repeating pattern.

      Then your script should match chunk after chunk. And try to repeat matching any chunks with __REGEX__ in them until they don't match anymore. And naturally it should warn if it doesn't get at least one match