Using NQE to do Configurations Compliance checks require having the proper structure for the searched text pattern.
Ex: this will not match eventhough that line exists within the configurations, but it’s not in the root level
PATTERN=```
set admintimeout 10
```;
So, to match this line we have to provide the proper structure, or recusively add an extra level until a match is found
So, modifing that example to one of the below patterns should work
PATTERN1=```
config system global
set admintimeout 10
```;
PATTERN2=```
{(string)+}
set admintimeout 10
```;
So, in this example it was only 1 level down, so it’s straight forward to fix.
But having to work with different patterns with different nesting levels the below code can be used to automatically modify the provided pattern and recursively search all the different levels
Beware that there are many limitations due to the actual version of the language.
ex: splitting a string is not straight forward, so the pattern has to be provided as a list of strings
ex:
PATTERN=T"config system global"," set admintimeout 10"];
The other limitation, is having this in a different file would force yout to use a PatternBlocks<{}> with no variables. So, it’s better to copy this code within the same nqe script you’re working on if this is needed
The below code block is a PoC for that implementation
spaces(n)=
foreach x in fromTo(0, n)
// don't prepend white spaces in the 1st line
where x > 0
select " ";
new_headers(n)=
foreach x in fromTo(0, n)
where x > 0
let pre = join("",spaces(x))
select pre + "{a" + toString(x-1) + ":(string)+}";
mod_cmd(cmd,n)=join("", spaces(n))+cmd;
mod_cmds(cmds,n)=(foreach cmd in cmds select(mod_cmd(cmd, n)));
export gen_pattern(str_list: List<String>, depth:Number)=blockPattern(join("\n", new_headers(depth) + mod_cmds(str_list, depth+1)));
export gen_patterns(str_list: List<String>, depth:Number)=
foreach x in fromTo(0, depth)
select gen_pattern(str_list, depth);
And a full implementation would look like this
//
// recursive pattern
//
spaces(n)=
foreach x in fromTo(0, n)
// don't prepend white spaces in the 1st line
where x > 0
select " ";
new_headers(n)=
foreach x in fromTo(0, n)
where x > 0
let pre = join("",spaces(x))
select pre + "{a" + toString(x-1) + ":(string)+}";
mod_cmd(cmd,n)=join("", spaces(n))+cmd;
mod_cmds(cmds,n)=(foreach cmd in cmds select(mod_cmd(cmd, n)));
export gen_pattern(str_list: List<String>, depth:Number)=blockPattern(join("\n", new_headers(depth) + mod_cmds(str_list, depth+1)));
export gen_patterns(str_list: List<String>, depth:Number)=
foreach x in fromTo(0, depth)
select gen_pattern(str_list, depth);
//
// Helper functions:
//
export parse_config_commands(device:Device, pattern:PatternBlocks<{}>, all_cmd_types:Bool, cmd_type:CommandType, cmd_text:String)=
foreach command in device.outputs.commands
where all_cmd_types || command.commandType == cmd_type
let continue = if isPresent(cmd_text) && length(cmd_text)>0
then isPresent(command.commandText) && matches(command.commandText,cmd_text)
else true
where continue
let config = parseConfigBlocks(device.platform.os, command.response)
let results= blockMatches(config, pattern)
foreach res in results
select {command_type: command.commandType, matches: res};
export gen_pattern_then_parse(device:Device, str_list: List<String>, depth:Number, all_cmd_types:Bool, cmd_type:CommandType, cmd_text:String)=
parse_config_commands(device, gen_pattern(str_list, depth), all_cmd_types, cmd_type, cmd_text);
longest_matches(l)=length(l);
export gen_patterns_then_parse(device:Device, str_list: List<String>, depth:Number, all_cmd_types:Bool, cmd_type:CommandType, cmd_text:String)=
maxBy(foreach n in fromTo(0, depth)
select gen_pattern_then_parse(device, str_list, n, all_cmd_types, cmd_type, cmd_text)
, longest_matches);
Using that code:
ADMIN_TIMEOUT=A"set admintimeout 10"];
IDLE_TIMEOUT=>"set idle_timeout 10"];
foreach device in network.devices
let admin_timeout = gen_patterns_then_parse(device, ADMIN_TIMEOUT, 2, false, CommandType.CUSTOM, "show full-configuration")
let idle_timeout = gen_pattern_then_parse(device, IDLE_TIMEOUT, 1, false, CommandType.CUSTOM, null:String)
select {
device: device.name,
admin_timeout,
idle_timeout
}
Sample output
I hope this code helps
Also, improvements to that is v. welcome as well!