Forum Discussion

grantae's avatar
5 years ago

Making a Backup for Cisco ISE in LogicMonitor

I am trying to setup a ConfigSource in LogicMonitor for Cisco ISE backups. We want the backup to only pull when a change is made in ISE. The out-of-the-box Cisco_IOS and Cisco_NXOS ConfigSources don't seem to work for ISE. So I tried to make a ConfigSource from the LM page https://www.logicmonitor.com/support/logicmodules/articles/creating-a-configsource but I haven't been able to get it to work properly. I made a copy of Cisco_IOS so I could have the Config Checks and removed the scripts. I have the following:

AppliesTo:

(
    ( 
      startsWith( system.sysinfo, "Cisco Identity Services Engine" )
    ) 
  ) &&
  ( 
    (ssh.user && ssh.pass ) || 
    (config.user && config.pass) 
)

 

Parameters:

import com.santaba.agent.groovyapi.expect.Expect

host = hostProps.get("system.hostname");
user = hostProps.get("config.user");
pass = hostProps.get("config.pass");

cli=Expect.open(host, user, pass);
cli.expect("#");

cli.send("terminal length 0\n");
cli.expect("#");

cli.send("show running-config all\n");
cli.expect(/Current configuration.*\n/);

cli.send("exit\n");
cli.expect("#exit");

config=cli.before();
config = config.replaceAll(/ntp clock-period \d+/,"ntp clock-period ");

cli.expectClose();
println config;

 

Test comes back with the error: 

The script failed, elapsed time: 60 seconds - End of stream reached, no match found
java.io.IOException: End of stream reached, no match found

 

Does anyone have any ideas? 

  • Anonymous's avatar
    Anonymous
    22 minutes ago, grantae said:

    Not sure what cli.expect() I should use for the line following cli.send("exit\n")

    Once you've done what you need to do to log off the device a simple "cli.expectClose()" is all that's needed. At that point, you don't need to wait for anything else before closing the SSH connection. I added some comments to your code. 

    Something to understand: cli.before() grabs everything between the most recent cli.expect() and the previous cli.expect(). Since you had "cli.expect("[screen is terminating]");" before the cli.before(), it was trying to grab the data after it had already exited the buffer. Does that make sense? Your cli.before() needs to be right after you know the config has been completely received. 

    import com.santaba.agent.groovyapi.expect.Expect
    host = hostProps.get("system.hostname");
    user = hostProps.get("config.user");
    pass = hostProps.get("config.pass");
    prompt = /.+\#\s/ //<---this may be problematic as it might match on the # and everything before it. You might need to be more specific.
    
    cli=Expect.open(host, user, pass);
    cli.expect(prompt);
    
    cli.send("terminal length 0\n");
    cli.expect(prompt);
    
    cli.send("show running-config\n");
    //cli.expect(/Current configuration.*\n/); //<--If the previous command ends at the prompt, then just expect the prompt.
    cli.expect(prompt)
    
    cli.send("exit\n");
    //cli.expect("[screen is terminating]"); //You've already exited, no need to wait for anything before closing the connection. You probably wouldn't get it anyway.
    
    config=cli.before(); //println(cli.before()) should be just fine. However, it might include "show running-config" on the first line, which is technically not part of the config.
    config = config.replaceAll(/ntp clock-period \d+/,"ntp clock-period "); //<--- Not sure ntp clock period is different every time you show the config. This removes the difference so that LM doesn't alert you that the config has been changed. There are better ways to do this. Just create an exception in the configcheck.
    
    cli.expectClose();
    println config;

     

  • OK, that helps explain the prompt thing. I was thinking it just needed to end # not that it was looking for the whole prompt. So I used https://www.regexpal.com/ to verify a correct prompt. I also typed through my cli.send while logged in as the LogicMonitor account. 

    *login*
    terminal length 0
    show running-config
    exit

    ----------------------

    When I exit the device the follow displays:

    <host/user># exit

    [screen is terminating]
    Connection to <host> closed.
    bash-4.3$

    Not sure what cli.expect() I should use for the line following cli.send("exit\n")

    ---------------------My Current Script---------------------------

    import com.santaba.agent.groovyapi.expect.Expect

    host = hostProps.get("system.hostname");
    user = hostProps.get("config.user");
    pass = hostProps.get("config.pass");

    prompt = /.+\#\s/

    cli=Expect.open(host, user, pass);   <----login
    cli.expect(prompt); <----wait for prompt

    cli.send("terminal length 0\n"); <----Set so show run shows all at one time
    cli.expect(prompt); <----wait for prompt

    cli.send("show running-config\n");       <------------ Changed from cli.send("show running-config all\n"); since show running-config all isn't a command
    cli.expect(/Current configuration.*\n/); <--Guess this one is expecting the config (Currently with the terminal length 0 show run ends on the prompt)

    cli.send("exit\n");  <---- Exit the device
    cli.expect("[screen is terminating]"); <----- Not sure what to expect here

    config=cli.before(); <------ Seems unnecessary (just change println config to println(cli.before()) )
    config = config.replaceAll(/ntp clock-period \d+/,"ntp clock-period "); <--- Not sure 

    cli.expectClose();
    println config;

    ------------------------------------------------------------------------

    I've played with variations of the above script and tried /* commenting */ parts of it out with println(cli.before()) and/or println(cli.stdout()) added in different sections. Also tried ending it with return 0 and switching the config.user and config.pass to ssh.user and ssh.pass. 

    Still getting: 

    The script failed, elapsed time: 60 seconds - End of stream reached, no match found
    java.io.IOException: End of stream reached, no match found

    Earlier in the day I also sometimes got a different error, but now I cannot get the error to come up again. It Was something about timing out after 47 seconds. 

  • Anonymous's avatar
    Anonymous

    Perhaps I should explain what these lines do:

    /*This logs into the device. Once logon happens, the script can send text to the 
    device and the device can send text back to the script. Any text sent back and
    forth will be stored in cli.stdout().*/
    cli=Expect.open(host, user, pass);
    
    /*This sleeps for 10 seconds. The purpose of this is to give your device time to send your script 
    something now that it's logged on. This can include a message of the day, logon banner, etc., but also 
    includes (hopefully eventually) a prompt: some text indicating that the device is ready to receive a 
    command from the script.*/
    sleep(10000)
      
    /*When expect runs, it is constantly listening to the device to see if anything comes in. This line prints 
    out the contents of cli.before(), which is the holding place for any text received before the most recent 
    expect match...*/
    println(cli.before())
    
    /* This is an expect match. This tells the script to look for a # sign in the text streaming from the 
    device. Usually, this is used to signal to the script that the device is ready to receive commands. It's 
    ready to receive commands because the device sent back the prompt which is usually like "my-cisco-ice#"
    When the script sees the # sign in the text coming from the device, it takes everything before that pound 
    sign and puts it into cli.before()*/
    cli.expect("#");

    In each of your attempts, you had cli.expect("#"). It would seem that the prompt for this device doesn't contain the pound sign so the script isn't not finding it. The script is waiting for a prompt with a # in it, but the device doesn't use the # sign (or it doesn't in the current mode) in its prompt.  Because the script never finds it, the script exits with a non-zero exit code. When testing a script from the UI, a non-zero exit code doesn't actually display the output. In order to get that, you'll need to go into the collector console and issue a !groovy command. This will give you a dialog box where you can pick a device and run a groovy script. Then it'll give you the whole output.

    I'm doing this against a linux box. The prompt is like this. I found this out simply by manually logging into the device and looking at what the prompt was. Notice that mine does not contain a #. If my script is expecting the device to send a #, the script will timeout waiting for something that will never get sent.

    [sweenig@staticcollector ~]$ 

    So, i've setup my expect statements to be slightly different. I had to create a regex expression to match on the prompt. I used https://www.regexpal.com/ to make sure my expression caught the whole thing and nothing else from my manual session.

    I ran this script against one of my linux devices (centos). 

    import com.santaba.agent.groovyapi.expect.Expect
    
    host = hostProps.get("system.hostname")
    user = hostProps.get("ssh.user")
    pass = hostProps.get("ssh.pass")
    
    prompt = /\[.+\]\$\s/ //this is the regex that identifies my prompt "[sweenig@staticcollector ~]$ "
    
    cli=Expect.open(host, user, pass) //connect
    cli.expect(prompt) //Waiting for prompt...
    cli.send("ls -l ~ \n") //Issuing command
    cli.expect(prompt) //Waiting for prompt...
    
    println("Full Output:\n" + "="*60)
    println(cli.stdout())
    println("="*60)
    
    println("Just the output between the prompts:\n" + "="*60)
    println(cli.before())
    println("="*60)
    
    return 0

     

    This is what the output looks like:

    returns 0
    output:
    Full Output:
    ============================================================
    Last login: Mon Aug 31 17:07:19 2020 from localhost
    [sweenig@staticcollector ~]$ ls -l ~ 
    total 278764
    -rwxrwxr-x. 1 sweenig sweenig     23284 Mar 25 04:15 LogicmonitorBootstrap64_6.bin
    -rwxr-xr-x. 1 root    root    285427235 Mar 25 04:17 LogicmonitorSetup.bin
    [sweenig@staticcollector ~]$ 
    ============================================================
    Just the output between the prompts:
    ============================================================
    ls -l ~ 
    total 278764
    -rwxrwxr-x. 1 sweenig sweenig     23284 Mar 25 04:15 LogicmonitorBootstrap64_6.bin
    -rwxr-xr-x. 1 root    root    285427235 Mar 25 04:17 LogicmonitorSetup.bin
    
    ============================================================

     

    The key is that my cli.expect statements were looking for a prompt that actually exists in the text returned by the device.

  • cli=Expect.open(host, user, pass);
    sleep(10000)
    println(cli.stdout())
    cli.expect("#");

    Results: 

    The script failed, elapsed time: 60 seconds - End of stream reached, no match found
    java.io.IOException: End of stream reached, no match found

    -----------------------------

    cli=Expect.open(host, user, pass);
    cli.expect("#");
    sleep(10000)
    println(cli.stdout())

    Results:

    The script failed, elapsed time: 60 seconds - Timeout
    com.santaba.agent.groovyapi.expect.expectj.TimeoutException: Timeout

    --------------------------

    cli=Expect.open(host, user, pass);
    println(cli.before())
    cli.expect("#");

    Results: 

    The script failed, elapsed time: 60 seconds - Timeout
    com.santaba.agent.groovyapi.expect.expectj.TimeoutException: Timeout

    ------------------------

    cli=Expect.open(host, user, pass);
    sleep(10000)
    println(cli.before())
    cli.expect("#");

    Results: 

    The script failed, elapsed time: 60 seconds - End of stream reached, no match found
    java.io.IOException: End of stream reached, no match found

    ------------------

    They all come back with the same error. Am I missing an import or something?

  • Anonymous's avatar
    Anonymous

    Groovy doesn't require semicolons. Also, you can't printlin(cli.before()) before you define what cli actually is.

    If you do:

    cli=Expect.open(host, user, pass);
    println(cli.before())

    You need to give your device some time to respond before printing out what has been received so far. I also just remembered that you can do println(cli.stdout()) as well. So, maybe something like this:

    cli=Expect.open(host, user, pass);
    sleep(10000)
    println(cli.stdout())

    Which would login, wait for 10 seconds for the device to log you in and present the command prompt. Then println(cli.stdout()) prints out everything that has been sent back and forth to the moment.

  • Thank you for helping me learn more about groovy and expect. 

    ----------------------------


    cli=Expect.open(host, user, pass);
    println(cli.before())
    cli.expect("#");

    Results in the same error.

    -----------------------------

    println(cli.before())
    cli=Expect.open(host, user, pass);
    cli.expect("#");

    Results:

    The script failed, elapsed time: 0 seconds - No such property: cli for class: Script129
    Possible solutions: class
    groovy.lang.MissingPropertyException: No such property: cli for class: Script129
    Possible solutions: class
    	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:66)
    	at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:51)
    	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:310)
    	at Script129.run(Script129.groovy:7)

    So I don't think it works there.

    --------------------------------

    Noticed I didn't put a ; after the println(cli.before()) so tried 

    cli=Expect.open(host, user, pass);
    cli.expect("#");
    println(cli.before());

    and got:

    The script failed, elapsed time: 60 seconds - Timeout
    com.santaba.agent.groovyapi.expect.expectj.TimeoutException: Timeout

    --------------------------------- (Surprised it ran without the semicolon)

    I did something similar to your PuTTY suggestion when I was checking if the password worked. I logged in with the user I made for LogicMonitor to verify the password and it goes to: "<ISE-hostname>/<username>#". So I believe cli.expect("#") would be correct. I also noted that "show logins cli" shows the LogicMonitor account logging in from the LogicMonitor IP for 00:01 a few times (Probably from running the test script).

  • Anonymous's avatar
    Anonymous

    I'd suggest using before the first cli.expect statement, in case that first expect statement is the one expecting the "#" and never getting it back from the device. You might want to put a sleep statement (sleep(n) where n is the number of milliseconds the script should wait before executing the next line) between logging in and doing the println(cli.before()) so that the device has a chance to send something back to you after logging in.

    A lower level way to work through this is to replicate the activity of the script using your own SSH client (e.g. putty on Windows or terminal in Linux/Mac). Log in using the same credentials, make a note of what get sent to you when you log in. After providing the username and password, are you sent directly to the prompt? How do you recognize what the prompt is? Think about how you use your human brain to understand that the device is waiting for your input. Then, what command do you send and what do you expect back? When you get that back, how do you (as a human) recognize that the device has sent you everything it's going to send? Usually because it returns to the prompt. You need to teach your script not only to send the command but also to recognize the output coming back from the device and know when the device is done sending the response back and is waiting for another command.

    In this case, the script (trying to emulate human behavior) is looking for a #, likely because the prompt probably uses something like "my-cisco-ise# " as the prompt. 

     

    Welcome to the deep end where you get to learn groovy and expect in one step. ?

     

  • Thank you for the suggestion. This is the first time I am trying to script with Groovy and my overall scripting experience is pretty low, so forget me if I didn't use println(cli.before()) correctly.

    ------------

    import com.santaba.agent.groovyapi.expect.Expect

    host = hostProps.get("system.hostname");
    user = hostProps.get("config.user");
    pass = hostProps.get("config.pass");

    cli=Expect.open(host, user, pass);
    cli.expect("#");
    println(cli.before())     <-------------------------

    cli.send("terminal length 0\n");
    cli.expect("#");

    cli.send("show running-config all\n");
    cli.expect(/Current configuration.*\n/);

    cli.send("exit\n");
    cli.expect("#exit");

    config=cli.before();
    config = config.replaceAll(/ntp clock-period \d+/,"ntp clock-period ");

    cli.expectClose();
    println config;

    ----------------------

    With it added after the first section, I still got the same error. No change. I also tried the line after each section with no change.

    Did I just use it wrong or is the script failing immediately? 

  • Anonymous's avatar
    Anonymous
    25 minutes ago, grantae said:

    End of stream reached, no match found

    This usually means that you've sent some command to the device and are waiting for a particular response that doesn't come. The lines in your code that are waiting are the cli.expect() ones. It's difficult to tell which one timed out. You can add some println statements before/after them to know which ones executed successfully. println(cli.before()) is especially handy in determining what has come back from the device.