Here’s a sample that I found from another customer. I tested it and it runs in parallel so should go pretty quick. the logic has several nested if-then-else statements which we could look at optimizing.
The first few lines should give you a reference for what Andy is saying, the rest should give you some good ideas on how to handle the different conditions for getting the date, etc. By the way we’ve done more with date functions lately so perhaps could streamline the logic here too
/**
* @intent Saved config older than Current
* @description Compares the last configuration timestamp to last NVRAM timestamp - if newer then violation
*/
foreach device in network.devices
where device.platform.os in [OS.IOS, OS.IOS_XE, OS.IOS_XR, OS.NXOS]
foreach command in device.outputs.commands
where command.commandType == CommandType.CONFIG
let configTextWithComments = replace(command.response, "!! ", "")
let configTextWithComments2 = replace(configTextWithComments, "! ", "")
let configBlocksWithComments = parseConfigBlocks(device.platform.os, configTextWithComments2)
/*
This part is going to look like a duplicate.
need to go through one time without the "person" variable
Then a second time with the "person" variable
The problem is that a "by person" is not always present
*
* !Running configuration last done at: Tue Feb 7 06:33:51 2023 - discuss this pattern - device nxos**
*/
let lastIOSXE = max(foreach lastMatch
//! Last configuration change at 10:02:42 UTC Thu Jul 28 2022 by f-smartsncm << used to create pattern
//! Last configuration change at 20:16:59 UTC Sat Feb 4 2023 << breaks pattern
// Line for discussion 8-11 - pattern matches
in patternMatches(configBlocksWithComments, `Last configuration change at {time:string} {timezone:string} {dayofweek:string} {month:string} {dayofmonth:number} {year:number}`)
select lastMatch)
let lastIOSXR = max(foreach lastMatch
//!! Last configuration change at Tue Feb 7 16:54:27 2023 by ZILEA004 - IOSXR
// Line for discussion 8-11 - pattern matches
in patternMatches(configBlocksWithComments, `Last configuration change at {dayofweek:string} {month:string} {dayofmonth:number} {time:string} {year:number} {timezone:string}`)
select lastMatch)
let lastNull = {line: null:ConfigLine, data: {month:null:String, year: null:Number, dayofweek: null:String, time: null:String, timezone: null:String, dayofmonth: null:Number}}
let last = if isPresent(lastIOSXE)
then lastIOSXE
else if isPresent(lastIOSXR)
then lastIOSXR
else lastNull
/*
This part is going to look like a duplicate.
this is the second time through to get the "person" if present
*/
let lastIOSXEperson = max(foreach lastMatch
//! Last configuration change at 10:02:42 UTC Thu Jul 28 2022 by f-smartsncm << used to create pattern
//! Last configuration change at 20:16:59 UTC Sat Feb 4 2023 << breaks pattern
// Line for discussion 8-11 - pattern matches
in patternMatches(configBlocksWithComments, `Last configuration change at {time:string} {timezone:string} {dayofweek:string} {month:string} {dayofmonth:number} {year:number} by {person:string}`)
select lastMatch)
let lastIOSXRperson = max(foreach lastMatch
//!! Last configuration change at Tue Feb 7 16:54:27 2023 by ZILEA004 - IOSXR
// Line for discussion 8-11 - pattern matches
in patternMatches(configBlocksWithComments, `Last configuration change at {dayofweek:string} {month:string} {dayofmonth:number} {time:string} {year:number} by {person:string}`)
select lastMatch)
let lastNullperson = {line: null:ConfigLine, data: {month:null:String, year: null:Number, person: null:String, dayofweek: null:String, time: null:String, timezone: null:String, dayofmonth: null:Number}}
let lastperson = if isPresent(lastIOSXEperson)
then lastIOSXEperson
else if isPresent(lastIOSXRperson)
then lastIOSXRperson
else lastNullperson
/*
* need same duplication for nvram and nvramperson
*/
let nvram = max(foreach nvramMatch
in patternMatches(configBlocksWithComments, `NVRAM config last updated at {time:string} {timezone:string} {dayofweek:string} {month:string} {dayofmonth:number} {year:number}`)
select nvramMatch)
let nvram_nxos = max(foreach nvramMatch
in patternMatches(configBlocksWithComments, `Running configuration last done at: Tue Feb 7 06:33:51 2023`)
select nvramMatch)
/*
* need same duplication for nvram and nvramperson
*/
let nvramperson = max(foreach nvramMatch
in patternMatches(configBlocksWithComments, `NVRAM config last updated at {time:string} {timezone:string} {dayofweek:string} {month:string} {dayofmonth:number} {year:number} by {person:string}`)
select nvramMatch)
let lasthour = prefix(last.data.time, 2)
let lastminute = substring(last.data.time, 3, 5)
let lastsecond = suffix(last.data.time, 2)
let nvramhour = prefix(nvram.data.time, 2)
let nvramminute = substring(nvram.data.time, 3, 5)
let nvramsecond = suffix(nvram.data.time, 2)
let lastmonth = last.data.month
let lastmonnum = if matches(lastmonth, "Jan")
then 1
else if matches(lastmonth, "Feb")
then 2
else if matches(lastmonth, "Mar")
then 3
else if matches(lastmonth, "Apr")
then 4
else if matches(lastmonth, "May")
then 5
else if matches(lastmonth, "Jun")
then 6
else if matches(lastmonth, "Jul")
then 7
else if matches(lastmonth, "Aug")
then 8
else if matches(lastmonth, "Sep")
then 9
else if matches(lastmonth,
"Oct")
then 10
else if matches(lastmonth,
"Nov")
then 11
else if matches(lastmonth,
"Dec")
then 12
else 99
let nvrammonth = nvram.data.month
let nvrammonnum = if matches(nvrammonth, "Jan")
then 1
else if matches(nvrammonth, "Feb")
then 2
else if matches(nvrammonth, "Mar")
then 3
else if matches(nvrammonth, "Apr")
then 4
else if matches(nvrammonth, "May")
then 5
else if matches(nvrammonth, "Jun")
then 6
else if matches(nvrammonth, "Jul")
then 7
else if matches(nvrammonth, "Aug")
then 8
else if matches(nvrammonth,
"Sep")
then 9
else if matches(nvrammonth,
"Oct")
then 10
else if matches(nvrammonth,
"Nov")
then 11
else if matches(nvrammonth,
"Dec")
then 12
else 0
let violation = if !isPresent(last?.data?.year)
then true
else if !isPresent(nvram?.data?.year)
then true
else if last.data.year > nvram.data.year
then true
else if last.data.year < nvram.data.year
then false
else if lastmonnum > nvrammonnum
then true
else if lastmonnum < nvrammonnum
then false
else if last.data.dayofmonth > nvram.data.dayofmonth
then true
else if last.data.dayofmonth < nvram.data.dayofmonth
then false
else if lasthour > nvramhour
then true
else false
let stringLast = if isPresent(last?.data?.timezone) && last.data.timezone == "by" //IOS_XR does not have timezone currently
then
toString(last.data.time) + " " /*+ toString(last?.data?.timezone)+ " "*/+toString(last.data.dayofweek) + " "+toString(last.data.month)+ " " + " "+toString(last.data.dayofmonth) +" " +toString(last.data.year)
else if isPresent(last?.data?.time)
then
toString(last.data.time) + " " + toString(last?.data?.timezone)+ " "+toString(last.data.dayofweek) + " "+toString(last.data.month)+ " " + " "+toString(last.data.dayofmonth) +" " +toString(last.data.year)
else null:String
let stringNvram = if isPresent(nvram?.data?.time)
then
toString(nvram.data.time) + " " + toString(nvram.data.timezone)+ " "+toString(nvram.data.dayofweek) + " "+toString(nvram.data.month)+ " " + " "+toString(nvram.data.dayofmonth) +" " +toString(nvram.data.year)
else null:String
//where violation == true
select {
violation: violation,
device: device.name,
LastConfiguredBy: lastperson?.data?.person,
Last: stringLast, //if want whole config line - last.line.text
LastSavedBy: nvramperson?.data?.person,
NVRAM: stringNvram,
OS: device.platform.os
//timeStamp: toString(last.data.month)+ "-"+toString(last.data.dayofmonth) +"-"+toString(last.data.year)//if want whole config line - nvram.line.text
}