3 years ago
Cisco IOS-XR backups
Hello,
It does not look like there is a Cisco IOS-XR config module to add for IOS-XR device backups. Maybe I'm missing something? Is there a way to do this with just groovy script maybe? A...
We have a config source in our portal that does this. I think it's built off an older IOS config source from awhile back that we modified. Here it is in 2 posts for you.
AD part
/*******************************************************************************
* © 2007-2019 - LogicMonitor, Inc. All rights reserved.
******************************************************************************/
import com.santaba.agent.groovyapi.expect.Expect
def host = hostProps.get("system.hostname")
def teln = hostProps.get("configsource.use.telnet").toString().toBoolean()
def user = hostProps.get("ssh.user") ?: hostProps.get("config.user")
def pass = hostProps.get("ssh.pass") ?: hostProps.get("config.pass")
def epas = hostProps.get("ssh.enable.pass") ?: hostProps.get("config.enable.pass") ?: pass
def admn = hostProps.get("ssh.admin") ?: "false"
def port = teln ? (hostProps.get("configsource.telnet.port") ?: 23) : (hostProps.get("ssh.port") ?: 22)
// Basic mode is used for devices that can't handle terminal or enable commands.
def basicMode = hostProps.get("system.sysinfo").contains("Cisco Firepower Threat Defense")
// These are the commands we check are supported.
def desiredCommands = ["running-config", "startup-config", "version", "inventory"]
// The command list can be overwritten via this property.
if(hostProps["configsource.desired.commands"]) {
desiredCommands = hostProps["configsource.desired.commands"].split(",").collect { it.trim() }
}
def cli
def timeout = 60
def success = false
def error = ""
// We remove common characters used to decorate a terminal.
def termClean = /(.?\[\?7h)/
def rawPrompt = /(?-m)[^\n\s]*[>#$]\s*$/
// CLI based approaches fail often. We make up to 10 attempts to connect and retrieve a single byte of data.
def retries = 0
while(retries < 10 && !success) {
try {
cli = teln ? Expect.open(host, port.toInteger(), timeout) : Expect.open(host, port.toInteger(), user, pass, timeout)
cli.expect(".")
success = true
}
catch (ex) {
sleep(1000)
error += "[Connection Attempt ${retries}] ${ex.message}\n"
}
retries++
}
if(success) {
try {
if(teln) {
// Telnet sometimes doesn't require a user to be sent. Only do so if requested.
cli.expect(["[Uu]?sername:", "[Pp]?assword:"] as String[])
if(cli.matched().toLowerCase().contains("name")) {
cli.send("${user}\n")
cli.expect("[Pp]?assword:")
}
cli.send("${pass}\n")
}
// At this point we are ready to check for a prompt, but don't know if the device has finished sending data.
// We wait until the data has been stable for 100 ms. Staggered/Delayed data is reasonably common.
def outputStability = 0
def lastLength = -1
while(outputStability < 100) {
outputStability += (cli.stdout().length() == lastLength) ? 1 : -outputStability
lastLength = cli.stdout().length()
sleep(10)
}
// Now the data is stable, the standard output should have everything we need to gather a prompt.
def stdPrompt = cli.stdout() =~ rawPrompt.bitwiseNegate()
def prompt = ""
if(stdPrompt.size()) {
// We've got our best guess at a prompt at this point. Clean it and get it ready for line matching.
prompt = "^${termClean}*.?${stdPrompt[0].trim().replaceAll(termClean,'').drop(1).trim().replaceAll(/[.*+?^()|\[\]\\{}$]/, '\\\\$0')}"
// Consume the buffer expecting the prompt so we know we have a clean buffer to work with for future matches.
cli.expect(prompt)
}
else {
// We didn't find a prompt in the standard output. Use expect to wait for one.
cli.expect(rawPrompt)
prompt = "^.?${cli.matched().replaceAll(termClean,'').trim().replaceAll(/[.*+?^()|\[\]\\{}$]/, '\\\\$0')}"
}
if(!basicMode) {
// The enable command will elevate the prompt if possible.
cli.send("enable\n")
cli.expect(["(?i)password:", "${prompt[0..-2]}[>#\$]", prompt] as String[])
if(cli.matched().toLowerCase().contains("password")) {
cli.send("${epas}\n")
cli.expect(rawPrompt)
}
// The enable command may have changed the prompt. We've got to get it again.
prompt = "^${cli.matched().replaceAll(termClean,'').trim().replaceAll(/[.*+?^()|\[\]\\{}$]/, '\\\\$0')}"
// We aren't in basic mode so try and configure the terminal to avoid pagination with both variations of the width and length commands.
["width", "length"].each { dimension ->
cli.send("terminal ${dimension} 0\n")
cli.expect(prompt)
if(cli.before().trim().readLines().size() > 1) {
cli.send("screen-${dimension} 0\n")
cli.expect(prompt)
}
}
if(admn.toLowerCase().contains("true")) {
// Attempt to enter admin mode if requested.
cli.send("admin\n")
cli.expect(rawPrompt)
prompt = "^.?${cli.matched().replaceAll(termClean,'').trim().replaceAll(/[.*+?^()|\[\]\\{}$]/, '\\\\$0')}"
}
}
// Execute the show command for this instance, adding a space for each page we may need to postpone a prompt being returned.
def maxPages = 50
cli.send("show ?${' ' * maxPages}\n")
// As we sometimes need to paginate the output, we will concatenate the output in a buffer.
def buffer = ""
// We loop throught all potential pages up to the limit set by "maxPages" (a default of 50 was double the ammount needed for anything in testing).
for(int i = 0; i < maxPages; i++) {
cli.expect([prompt, "\\s?--More--\\s?"] as String[])
// Attempt to clean the output of unwanted information, specifically backspace characters and the --More-- decoration.
buffer += cli.before().replaceAll("(\b+\\s*\b+)|(\\s?--More--\\s?)","")
if(cli.matched().contains("--More--")) {
// We hit a page break. Send many new line characters to greatly reduce the chance of an early prompt being returned (one is not reliable).
cli.send("${' ' * maxPages}\n")
} else {
// We matched the prompt and have to assume we've gotten everything. We are unable to continue pagination even with more new line characters.
break
}
}
def output = buffer.trim()
output.eachLine{ line->
desiredCommands.each { comamnd->
def tokens = line.trim().tokenize()
if(tokens[0] == comamnd) {
println "${tokens[0]}##${tokens[0]}##${tokens[1..-1].join(" ")}"
}
}
}
} catch (ex) {
error += "[Collecting] ${ex.message}\n"
success = false
}
try {
// Add an extra exit if we might be in privilege mode.
cli.send("exit\n" * (basicMode ? 2 : 1))
cli.close()
}
catch(ex) {
error += "[Closing] ${ex.message}\n"
success = false
}
}
if(!success) {
error += cli ? "--- CLI Output ---\n${cli.stdout()}\n" : ""
error += cli ? "--- CLI Base64 ---\n${cli.stdout().bytes.encodeBase64().toString()}\n" : ""
println "--- Error Logs ---\n${error}"
//new File("../logs/Cisco_IOS_Mini_AD ${host.replaceAll('[^a-zA-Z0-9-_\\.]', '_')}.txt").write(error)
return 1
}
return 0