Skip to main content
Solved

How can i get output in single coloum 


Forum|alt.badge.img+2

How can i get output in single coloum . 

 

team ,

 

i am using below query to get the NTP Server data , however is tried all option to get the output in single coloum , but i am not sucess.

 

NQE Query 

ciscontppattern=

```



ntp server {server:string}

```;

ciscontppattern2=

```



ntp server {vrf:string} {dir:string} {ser:ipv4Address}

```;

patternf5 = ```

sys ntp

    servers { server1:string} {server2:string}

```;




cisco1(device) =

foreach command in device.outputs.commands

  let response = parseConfigBlocks(device.platform.os, command.response)

  foreach match in blockMatches(response, ciscontppattern)

  select match.data.server;

cisco2(device) =

foreach command in device.outputs.commands

  let response = parseConfigBlocks(device.platform.os, command.response)

  foreach match in blockMatches(response, ciscontppattern2)

  select match.data.ser;

getServers(device) =

foreach command in device.outputs.commands

 where command.commandText == "list sys ntp"

let filtered_response = replace(command.response, "{", "")

let filtered_response = replace(filtered_response, "}", "")

let blocks = parseConfigBlocks(OS.F5, filtered_response)

foreach match in blockMatches(blocks, patternf5)

 select {server1: match.data.server1, server2: match.data.server2,

 };



foreach device in network.devices

select {

  vendor: device.platform.vendor,

  "Device Name": device.name,

  "IP Address": device.snapshotInfo.collectionIp,

  Tags: device.tagNames,

  Location: device.locationName,

  "Server-Cisco1": cisco1(device),

  "Server-Cisco/Arista": cisco2(device),

   "F5_server": max(getServers(device))?.server1,

    "F5_server2": max(getServers(device))?.server2,}

 

Best answer by AricaFN

@Rohit_809 Kumar if you need to add a new pattern, you can use the same pattern and function format you have for the other cisco patterns. You just need the final data types to be strings. So you can either define the pattern as a string and add it to the cisco1 function, or you can define it as an ip address and add it to the cisco2 function where it uses toString() on the ip address.

 

Examples of how that would look - 

ciscontppattern3 = ```
replace with new ntp server pattern {server:string}
```;

cisco1(device) =
  foreach match in blockMatches(device.files.config, ciscontppattern) + 
  blockMatches(device.files.config, ciscontppattern3)
  select match.data.server;

or

ciscontppattern3 = ```
replace with new ntp server pattern {server:ipv4Address}
```;

cisco2(device) =
  foreach match in blockMatches(device.files.config, ciscontppattern2)  + 
  blockMatches(device.files.config, ciscontppattern3)
  select toString(match.data.ser);

 

View original
Did this topic help you find an answer to your question?

Forum|alt.badge.img+1
  • Employee
  • April 2, 2025

@Rohit_809 Kumar this appears to be nesting your data. You can project the data as a nested structure for use in a downstream API or as a table as rows and columns. 

The task you are performing is very much like I describe in Approach 1 and Approach 2

If you keep your field selectors common you can keep the structure common to your needs.

 


Forum|alt.badge.img+2

Hi ​@GaryB  this article is very helpful. but however i am using below pattern for Syslog config .

 

pattern1 = `logging {tech:string}{"server" vrf:string server:string | server:string }`;

 

as you know some device use “ logging host “ or some “logging server “ how can i use these 2 in 1 pattern. 

 

Also F5 and Forintet the pattern is different not sure how can i add 

 

F5 

pattern = ```

sys ntp

    servers { server1:string} {server2:string}

 

Fortinet 

 

pattern_fmg = ```

config system ntp

    config ntpserver

        edit 1

            set server {ntp1:string}

```;

 

 

i am not able to create single pattern logic to above condition , Please help

 


AricaFN
Employee
Forum|alt.badge.img+1
  • Employee
  • April 3, 2025

@Rohit_809 Kumar if i’m understanding your request correctly, you’re trying to combine the results for these items -

"Server-Cisco1": cisco1(device),

"Server-Cisco/Arista": cisco2(device),

"F5_server": max(getServers(device))?.server1,

"F5_server2": max(getServers(device))?.server2,}

- into one singular output.

The main issue you’re having with trying to combine them, is data type mismatch. The variables you set when you defined the patterns all have different types.

The result of your functions now have different types as well

cisco1(device) =
  foreach command in device.outputs.commands
  let response = parseConfigBlocks(device.platform.os, command.response)
  foreach match in blockMatches(response, ciscontppattern)
  select match.data.server;

the above returns a List<String>

 

cisco2(device) =
  foreach command in device.outputs.commands
  let response = parseConfigBlocks(device.platform.os, command.response)
  foreach match in blockMatches(response, ciscontppattern2)
  select match.data.ser;

while this returns a List<IpAddress>

 

getServers(device) =
  foreach command in device.outputs.commands
  where command.commandText == "list sys ntp"
  let filtered_response = replace(command.response, "{", "")
  let filtered_response = replace(filtered_response, "}", "")
  let blocks = parseConfigBlocks(OS.F5, filtered_response)
  foreach match in blockMatches(blocks, patternf5)
  select { server1: match.data.server1, server2: match.data.server2 };

and here we’re getting List<{server1: String, server2: String}

 

This is where the trouble of combining them comes in. Here is what I would suggest to get you the results that you’re looking for - 

 



// since this pattern searches for any string, it is also going to pick up vrf lines. This excludes vrf lines from the search and leaves it to patern2
ciscontppattern = ```
ntp server {server:(!"vrf" string)}
```;

ciscontppattern2 =
  ```
ntp server {vrf:string} {dir:string} {ser:ipv4Address}
```;

// instead of defining variables for two servers, you can use this to collect any amount of server addresses in a list
patternf5 = ```
sys ntp
  servers {ser:(string*)}
```;

cisco1(device) =
//   foreach command in device.outputs.commands
//   let response = parseConfigBlocks(device.platform.os, command.response)
  foreach match in blockMatches(device.files.config, ciscontppattern) // changed to search only the config file
  select match.data.server;

cisco2(device) =
//   foreach command in device.outputs.commands
//   let response = parseConfigBlocks(device.platform.os, command.response)
  foreach match in blockMatches(device.files.config, ciscontppattern2) // changed to search only the config file
  // convert the server ip to string so data types match
  select toString(match.data.ser);

getServers(device) =
  foreach command in device.outputs.commands
  where command.commandText == "list sys ntp"
  let filtered_response = replace(command.response, "{", "")
  let filtered_response = replace(filtered_response, "}", "")
  let blocks = parseConfigBlocks(OS.F5, filtered_response)
  foreach match in blockMatches(blocks, patternf5)
  // (string*) returns the match as a list, this removes the extra list layer in the final results
  foreach server in match.data.ser
  select server;

foreach device in network.devices
// now that the functions all have matching data types, you can easily combine them into one variable
let servers = cisco1(device) + cisco2(device) + getServers(device)
select {
  vendor: device.platform.vendor,
  "Device Name": device.name,
  "IP Address": device.snapshotInfo.collectionIp,
  Tags: device.tagNames,
  Location: device.locationName,
  "NTP Servers": servers,
}

 

And if you need to add any additional patterns/functions just make sure the final output is a list of strings. You can verify this by hovering over the initial definition like so

 


Forum|alt.badge.img+2

@AricaFN thanks , its working what i need , but what is the scope to add new pattern in same query ?

example - if i need to add some other pattern for cisco.

 

Thanks


AricaFN
Employee
Forum|alt.badge.img+1
  • Employee
  • April 4, 2025

@Rohit_809 Kumar if you need to add a new pattern, you can use the same pattern and function format you have for the other cisco patterns. You just need the final data types to be strings. So you can either define the pattern as a string and add it to the cisco1 function, or you can define it as an ip address and add it to the cisco2 function where it uses toString() on the ip address.

 

Examples of how that would look - 

ciscontppattern3 = ```
replace with new ntp server pattern {server:string}
```;

cisco1(device) =
  foreach match in blockMatches(device.files.config, ciscontppattern) + 
  blockMatches(device.files.config, ciscontppattern3)
  select match.data.server;

or

ciscontppattern3 = ```
replace with new ntp server pattern {server:ipv4Address}
```;

cisco2(device) =
  foreach match in blockMatches(device.files.config, ciscontppattern2)  + 
  blockMatches(device.files.config, ciscontppattern3)
  select toString(match.data.ser);

 


Forum|alt.badge.img+2

Thanks ​@GaryB  and ​@AricaFN  , i got my answer, and this is very helpful.


Reply


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings