Building Templates
A rule performs checking based upon patterns. Thus, to form a rule, you should define both pattern(s) and rule(s).
Cisco IOS Example
The following is an example Cisco IOS template made up of two patterns “hasip” and “shutdown” followed by a rule “Shutdown_or_noip” which checks the interface block based on the presence or absence of these two patterns. The interface blocks (represented by keyword “interface”) are looped through with a “foreach” statement. If either pattern “hasip” is not matched or pattern “shutdown” is matched, a severity level of “warning” is raised. Otherwise, a severity level of “information” is raised via the print statement (equivalent of “raise info”).
#conform name ciscotemplate #conform type cisco ios @define hasip ip address $(myip) * @define shutdown shutdown @define rule Shutdown_or_noip foreach interface do if (!hasip || shutdown) then raise warning "$(interface.name) has no ip address or shutdown" else print "$(interface.name) has an ip $(myip)" end end
In the pattern hasip, note that the word following “ip address” is being saved into a variable with name “myip”, so that the IP address can be printed out subsequently in the Shutdown_or_noip rule.
Blank lines and white spaces in templates are ignored (except when used in regular expressions). So using blank lines to separate blocks of text in the template are not necessary
Juniper JUNOS Example
The following is a simple Juniper example to check a global variable, the OS version, and raise different severity levels depending upon the OS version. In this case, referencing a pattern is not necessary, since $(version) is a global variable.
#conform name junipertemplate #conform type juniper junos #conform use regular-expression @define rule junosversion if $(version) ~= "7.*" then raise critical "version $(version)" elseif $(version) ~= "8.*" then raise major "version $(version)" elseif $(version) ~= "9.[1-3].*" then raise minor "version $(version)" else print "version $(version)" end
Because Junos contains a well-defined hierarchical structure defined by braces, it is possible to design configuration compliance assessments at specific levels of the hierarchy. For example, the following rule check_rsvp checks for the existence of traceoptions under the protocols rsvp clause of each device:
#conform type junos @define rsvptraceoptions traceoptions { file rsvp.log size 10m; flag error; flag resv; flag route; flag resvtear; flag all; } @define rule check_rsvp foreach protocols.rsvp do if rsvptraceoptions then raise info "matched rsvp trace options" else raise major "no match for rsvp trace options in $(hostname)" end end
For Junos pattern definitions, key structural characters like ‘{‘ and ‘;’ should not be substituted by a regular-expression, since they have special meanings to the program.
For example, if there is a section for chassis as follows, the user can use the syntax chassis.fpc.pic to loop through the pic’s as in “foreach chassis.fpc.pic do”:
chassis ( fpc 0 { pic 0 { } } }
If the next item in the hierarchy is an unknown name, such as for the interfaces {} block, under which are the interface names such as ge-0/0/1, ge-0/0/2, etc. the keyword “child” can be used as follows, and its contents can be printed using $(instance).
@define hasdescription description $(intfdesc) @define rule maindescription foreach interfaces.child do if (hasdescription) then print "$(instance) has description $(intfdesc)" end end
For more Paragon Planner keywords, see Paragon Planner Keywords For Use Within a Rule.
Match Ordered, Unordered, or Exact
In addition to performing compliance assessments on specific blocks of code, there is a rule to check for lines within the entire configlet, using the keyword “match”, or its equivalent keyword “conform.”
Suppose the config file contains five lines:
a b c d e
Then within the template file, we can define patterns, and rules to check for an exact match of the pattern, an ordered match, or an unordered match:
@define block a b d @define block2 a c b @define rule exactmatch match exact block # not matched due to additional lines c and e @define rule orderedmatch match ordered block2 # not matched due to out of order lines (lines c and b) @define rule exactmach2 match exact block2 # not matched by the same reason above (an exact match must also be ordered) @define rule match match block2 # matched
Match and Severity
The match function will categorize the matched results based on different matched conditions. The severity of these categories be changed from the Settings option and saved per project. The following categories are available:
Matched: matched
Missing line: missing line from the defined template
Missing block: the first line is missing from the defined template
Extra line: there is an extra line from the defined template
Unordered line: the line is not in the same order as the defined template.
Match Block with Variables
In some configurations, the block to match may be slightly different based on different routers or vendors. This match block with variable feature allows users to define statements to account for these cases.
In the following example, we will try to match the policy statement. On each router, the term is different based on the router’s country code and location which can be extracted from the router’s hostname.
@define policy_statement_a policy-statement a { term term_a { from { protocol bgp; community [ to-$(country) to-$(location) ]; } then reject; } then accept; } @define rule rule_a set $(location) right(hostname,2) set $(country) left(hostname,2) foreach policy-options do match policy_statement_a end
@define <Pattern Name> |
Define a pattern of a block of text. It could contain one word, one line or multiple lines. - Wild card, *, can be used to match any text. Alternatively, regular expression can be used if appropriate #conform use regular-expression statement is included in the header. - Note: The wild card should not be used to hide key syntax operators on the first line such as braces ‘{‘ and semi-colons ‘;’ - $(<Variable Name>) can be used to capture and turn any text into a variable, which can then be printed out in the subsequent rule. Example: @define pattern1 ip vrf $(vrf) rd $(rd) route-target export * route-target import * |
@define rule <Rule Name> |
Define a compliance assessment rule used for the syntax checking. - Multiple rules can be defined within one template. - Rules can be assigned to different categories by adding category=<Category Name> in the end. - Various flow control, loop, logic boolean, logic operator, print functions can used in the rule. - Additional flow controller keyword: Exit : Once flow reaches exit statement, program will immediately stop checking for the current rule and move on to the next rule if any. Example : @define rule BFD-Check category=Protocol |
@define external <Rule Name> output=[<path>|append] |
Define a rule to execute an external program: - A external program can be written in any language which uses stdout as result output, e.g., a perl script could be used. Make sure this program is executable from the command line. - The result can be either output to a file or it can be appended to wandl’s compliance assessment report if the result is in the same CSV format or can be output to another separate file. Example : @define external rule1 output=/tmp/ls.csv /usr/bin/ls -l @define external rule2 output=append /export/home/wandl/myscript.sh |
@define description <Rule Name> |
Provide a description/explanation for the compliance assessment rule. Example : @define description This rule checks whether the interface is shutdown or not |
foreach <block> do … end |
Define a loop function to go through each pattern block matched in configuration, or to loop through each array element of an array. Flow controller keywords to use within the loop function include the following: - Next : Once flow reaches next statement, program will immediately stop the current loop and move on to the next loop. - Break : program will immediately leave the current foreach loop. Note that nested loops can be used in configuration files with well-defined hierarchical structures, such as Junos. Example for array, using reserved keyword $(element): foreach $(your_array) do print $(element) done You can get an array element by using the subscript operation. It’s syntax as follows: $(array_name.array_index) or $(array_name.array_index_variable) If $(array) is an array and $(index) is a number variable, then $(array.index), $(array.0), $(array.1), are valid syntax. $(array.length) will return the size of the array. The keyword in can be used to check if a variable exists in an array if $(string1) in $(array1) then... Example for pattern block: @define hasbandwidth bandwidth $(bandwidth); @define rule junosrule1 category=Interface foreach interfaces.child.unit do if hasbandwidth then print “$(interfaces.child) has bw $(bandwidth)” end end Note:
Nested loops are allowed for pattern blocks only if the nested loop loops through a descendent of the parent loop. For example, the above could be written as follows: foreach interfaces.child do for each unit do if hasbandwidth then print “$(interfaces.child) has bw $(bandwidth)” end end end |
if (<boolean logic condition>) then … elseif (<boolean logic condition>) then … else … end |
Define a boolean logic condition to separate flow into different scenarios based on true or false boolean result. - Both elseif and else statements are optional. - Multiple elseif statements are allowed, if necessary. - Additional Boolean logic operator keywords include the following: &: AND ==: EQUAL ||: OR !=: NOT EQUAL !: FALSE ~=: WILD CARD EQUAL Example: if (pattern1 && !pattern2) then print “pattern1 matched and pattern2 unmatched” elseif (pattern1 && pattern2) then print "both pattern1 and pattern2 matched " elseif (pattern3 ~= “Loopback*”) then print "loopback found in pattern3" else print "none of above" end |
$(<Variable Name>) |
To define a variable. Example: $(x) |
“…” |
To define a string. Example: “This is a string” |
set |
To define a value to a variable Example: set $(x) 1 |
+ |
Arithmetic addition between number value or number variable or concatenate between string and string variable. Example 1 : set $(count) $(count) + 1 Example 2 : set $(string1) $(hostname) + "," + $(interface.name) |
read |
To read in an external plain-text file containing multiple lines into a single degree string array variable. One line per array member which can be used together with “In: function. Example : read $(array1) "/tmp/interface-list.txt" Note:
Note: /tmp/interface-list.txt contains following lines Router1,interface1 Router2,interface2 … RouterN,interfaceN |
add |
To add an element to an array. add $(your_array) $(your_element) Example :foreach interfaces.child do if $(instance) ~= "xe*" then add $(full_interface_list) $(instance) end end To copy one array to another array. add $(array1) "a" add $(array1) "b" add $(array2) $(array1) print “test case 1: $(array2)” # prints “[a, b]” add $(array2) "c" print "test case 1: $(array2)" # prints "[a, b, c]" |
remove |
To remove an element from an array. remove $(your_array) $(your_element) Example :foreach protocols.isis.interface do if $(interface.name) ~= "xe*" then if isis_disable then remove $(full_interface_list) $(interface.name) end end end |
in |
To check if a string variable exists in a string array and yield true or false boolean value. Example :fif $(string1) in $(array1) then raise info "$(string) is in the file" end |
writeIn |
To write strings into a file. This can be used to create custom reports or output file. The first input parameter is the file to write in. The second input parameter is the string to write in the file. If the file already exists, it will be overwritten. Example :@define rule test_write set $(file) "/home/wandl/CAT/test/write_file.txt" foreach interfaces.child do print "$(hostname),$(instance),$(description)" writeln $(file) "$(hostname),$(instance),$(description)" end This will generate a file called write_file.txt in the directory /home/wandl/CAT/test/ with content similar to this sample: J1,ge-0/0/0,management interface for J1 J1,ge-0/0/1,to 3550S2 FastEthernet0/23 J1,ge-0/0/2,to_EX1_ge-0/0/12 J1,ge-0/0/3,to_BRS_2600 J1,lo0,loop - provision by WANDL J1,ae39,des J1,ae40,des |
raise |
To print a message entry to the compliance assessment result report with severity assigned (pass, info, minor, major and critical) To print a message entry to the compliance assessment result report with severity assigned (pass, info, minor, major and critical) Example : major “This is a major event" As a shortcut, a number can be used. The mapping between severities and numbers are as follows: - critical: 5 - major: 4 - minor: 3 - warning: 2 - info: 1 - pass: 0 Example :raise 4 “This is a major event" |
Print is equivalent to raising an info message: Example :print “This is a info event" |
|
child |
The “child” property can be used within a foreach loop to access the child item. Example :In the following configlet segment, ge-* and xe-* can be accessed using “foreach class-of-service.interfaces.child do” class-of-service {interfaces { ge-* { } xe-* { } |
line |
To get a list of single words from config block use the keyword "line" prefix-list list1 { 10.0.0.0/8; 192.168.0.0/16; 10.1.1.0/24; } e.g. foreach policy-options.prefix-list do if $(prefix-list.name) == "list1" then foreach line do print "$(instance)" end end end |
element |
For arrays, a reserved variable to refer to the value of the current array object: Example: foreach $(your_array) do print $(element) done |
conform <Pattern Name> match <Pattern Name> |
Looks for a match for the provided pattern and automatically raises a message entry into the resulting report. The Detailed Results tab will show related line numbers and line content under Template Line and Template Line #. Matches if all lines and subblocks exists in config file. These lines do not have to be in the same order for a match. Example :conform myconfiglet |
conform ordered <Pattern_name> match ordered <Pattern_name> |
All template lines and block should be in configuration file. In addition, all the lines must be ordered correctly. Note that config files may have additional lines or subblocks. Example :conform ordered myconfiglet |
conform exact <Pattern_name> match exact <Pattern_name> |
To match, the config file must contain the exact same section as the template. In addition to having the lines ordered in the same way, no additional lines are allowed in that section for a match. Example :conform exact myconfiglet |