multi-step "for each"?

I'm trying to create a multi-step script (all AWK) where, in the first step, I get a list of strings, and then I want to issue the _same_ command for each of those strings. E.g.,


show vpn ipsec phase1-interface


outputs a list of defined ipsec tunnels, including the names of any cert used for the tunnel. I want to parse out the cert name for each tunnel, and then, for each cert name do:


get vpn certificate local details ${cert}


to get the cert details. The multi-step docs (https://indeni.atlassian.net/wiki/spaces/IKP/pages/75890692/Recursion+multi-step) seem to suggest that this is possible, but I can't get that example to work with --parse-only; I can't even really understand how that example _could_ work...


Anyone know how to do this? Is there a working example somewhere?

Let me take a deeper look on the infrastracture code, I'll update soon

This would work with a live device, but it won't work with --parse-only nor with the test option; well, it can work, but you'd have to manually create a multi-step input JSON (there's still no option to automatically create it).


Take a look at the "input file" section in: https://indeni.atlassian.net/wiki/spaces/IKP/pages/183762981/Indeni+knowledge+reformed+directory+structure

hawkeye_multistep.tar.gz.txt


Ok, after a long email thread, I think I understand how this is supposed to work, but I can't get it to work -- either I'm still doing something wrong, or there's a bug. Just to be clear, I think I should be able to do something like:


#! REMOTE::SSH
show interfaces

#! PARSER::AWK
/ethernet/ {
niclist = niclist $0 "," # Assuming "$0" is the name of the NIC
}

END{
sub(/,$/, "", niclist) # remove trailing comma
# nics should be something like: "ethernet1/1,ethernet1/2,ethernet1/3"
writeDynamicVariable("nics", niclist)
}

#! REMOTE::SSH
# the following command expands to:
# "show interface ethernet1/1,ethernet1/2,ethernet1/3"
# The comma-delimited string tells the indeni "ssh command parser" to
# run the "show interface" command three times, once for each of the
# sub-strings in the comma-delimited command argument "nics"
show interface ${nics}



I've attached the actual .ind script I'm trying to run, along with the command-runner full-command output log and a sample input file for the first step. Running command-runner-6.0.42_64. To reproduce:


  • run the .ind script using the command at the top of the log (command-runner.sh full-command --verbose --ssh admin,P@ssword1! ~/tmp/ind/hawkeye_multistep.ind 10.0.0.20)
    • let me know if you need an actual fortinet device to test against


result: first command appears to succeed. The comma-delimited string appears to get properly passed on to the second command:

2017-12-05 16:00:14,132 30859 DEBUG - 10.0.0.20(10.0.0.20): Running SSH commmand: get vpn certificate local details Fortinet_Factory,Fortinet_CA_SSL,Fortinet_CA_Untrusted,Fortinet_SSL,Fortinet_SSL_RSA1024,Fortinet_SSL_RSA2048,Fortinet_SSL_DSA1024,Fortinet_SSL_DSA2048,Fortinet_SSL_ECDSA256,Fortinet_SSL_ECDSA384,Fortinet_Wifi,hp-test-cert

But, I get no data back from the second SSH command (at least, I don't get anything for AWK to parse). There's no error in the log, but there is a stack trace from a debug message:

2017-12-05 16:00:14,158 30885 DEBUG - Failed to format step / remote operation CollectorCommandStepWithVariables(CollectorMonitoringCommandStep(SshOperation("get vpn certificate local details ${certs}"),indeni.collector.commands.parsing.awk.AwkMonitoringParser@54c9cdec),Map())
java.lang.IllegalArgumentException: No such variable "certs" in "get vpn certificate local details ${certs}"
at com.indeni.StringHelper$$anonfun$format$1.applyOrElse(StringHelper.scala:46)
at com.indeni.StringHelper$$anonfun$format$1.applyOrElse(StringHelper.scala:45)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:216)
at scala.util.Try$.apply(Try.scala:192)
at scala.util.Failure.recover(Try.scala:216)
at com.indeni.StringHelper$.format(StringHelper.scala:45)
at indeni.collector.recording.ActiveCommandRecordContext$$anonfun$appendStepResult$1.apply(CommandRecordingSupport.scala:72)
at indeni.collector.recording.ActiveCommandRecordContext$$anonfun$appendStepResult$1.apply(CommandRecordingSupport.scala:70)
at scala.Option.foreach(Option.scala:257)
at indeni.collector.recording.ActiveCommandRecordContext.appendStepResult(CommandRecordingSupport.scala:70)
at indeni.collector.actors.ResultsHandler$$anonfun$waitingForResults$1.applyOrElse(ResultsHandler.scala:87)
at akka.actor.Actor$class.aroundReceive(Actor.scala:497)
at indeni.collector.actors.ResultsHandler.aroundReceive(ResultsHandler.scala:24)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:526)
at akka.actor.ActorCell.invoke(ActorCell.scala:495)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
at akka.dispatch.Mailbox.run(Mailbox.scala:224)
at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)




I am also having issues after making my multi-step a little more complicated. While hawkeye is trying to get this working against a live device, I am trying to get a test case working but failing.

The code is located here:

https://bitbucket.org/indeni/indeni-knowledge/branch/IKP-1194-internal-ldap-fingerprint-check

and path: parser/test/checkpoint/firewall/ldap-fingerprint-check


Here I have a test case 0. It consists of three input files. The first input file is the result of the first command part of the multi-step. This part works fine.

The script work like this. The first commands lists which Active Directory servers the firewall is connected to, and retrieves the SSL fingerprint that is configured for them.

The second command runs a command for each Active Directory server found, to fetch the current fingerprint they have and compare with the configured one.

In my test case it will detect two Active Directory servers, thus execute two more commands afterwards, so I have two more input files reflecting the output of the two commands.


However I get an error message:

Message = Header = Execution Error,
Description = Failed to find matching input,
Message = key not found: Map(multistepList -> indeni.local__AD:asdasd:1.1.1.1:d4ba0d2c93c39e869275452da03ddc2c,indeni.local__AD:Host_192.168.197.14:192.168.197.14:899711ebae4fb70d5c6ac95b66c238b3),


What looks strange here is that it looks like it is trying to match the entire string to the second step, when I expect it to split the string on "," and send one part of it each time.

Is this an bug or expected behaviour?


In my input.json I have configured it like this. (seems crowd removes spaces when pasting)


{
 "steps": [
    [
      {
        "variables": {
    },
    "path": "input_0_0"
  }
],
[
  {
    "variables": {
      "multistepList": "indeni.local__AD:asdasd:1.1.1.1:d4ba0d2c93c39e869275452da03ddc2c"
    },
    "path": "input_1_0"
  }
],
[
  {
    "variables": {
      "multistepList": "indeni.local__AD:Host_192.168.197.14:192.168.197.14:899711ebae4fb70d5c6ac95b66c238b3"
    },
    "path": "input_2_0"
  }
]

]
}


Maybe mine and hawkeyes issues are related.