Getting a host context from an arbitrary loop construct

I recently learned a new trick for creating a series of Service objects in a configuration via a for loop. I’m trying to apply that new technique to create multiple Dependency objects but I lost the context to my host object. In this configuration attempt, it doesn’t recognize host:

for (env => instance in host.vars.db_instances) {
  apply Dependency  "dbconn-" to Service use(env) {
      parent_service_name = "$host.name$!$env$-tnsping"
      child_service_name = "$host.name$!$env$-t3traffic"
      child_host_name = "$host.name$"
      disable_checks = true
      assign where "oracle-db-svc" in service.templates
  }
}

Error was:
Error while evaluating expression: Tried to access undefined script variable 'host'

So I thought, OK, it must only get a host object if it’s in the context of an apply so I switched it to this:

apply Dependency  "dbconn-" to Service for (env => instance in host.vars.db_instances) {
    parent_service_name = "$host.name$!$env$-tnsping"
    child_service_name = "$host.name$!$env$-t3traffic"
    child_host_name = "$host.name$"
    disable_checks = true
    assign where "oracle-db-svc" in service.templates
}

But it didn’t like looping that way:

Error: syntax error, unexpected for (T_FOR), expecting '{'
/etc/icinga2/conf.d/services.conf(159): apply Dependency  "dbconn-" to Service for (env => instance in host.vars.db_instances) {
                                                                           ^^^

However, I can’t leave off “to Service” because the Dependency needs it.

The gotcha is that I’m trying to create a service dependency. These are a bunch of Oracle checks but they also depend on the tnsping service check (which confirms that the DB connection is up). For now I’m just trying to use t3traffic as a one-off just to get a Dependency created.

So I’m trying to create a bunch of dependencies amongst existing services utilizing context information from their shared host object. The host object looks like this:

object Host "mydb.host.org" {
  ...
  vars.db_instances = {
    "DEV1" = {
      "username" = "mbestaging"
      "other" = "info"
      "Etc" = "etc"
    }
    "DEV2" = {
      "username" = "mbestaging"
      "other" = "info"
      "Etc" = "etc"
    }
    "ETC" = {
      "username" = "mbestaging"
      "other" = "info"
      "Etc" = "etc"
    }
}

Could anyone suggest an alternate approach?

Thanks for your thoughts!

Andy

host also is a scoped variable, which automatically is made available inside apply rules. This can only happen since apply rules loop over all defined to types by default.

If you’re writing such on your own, you would need to loop over all host objects by yourself. Currently this isn’t possible, since you don’t know the last point during config compilation where to run a loop over all hosts.

That being said, you cannot build dependencies like this. Instead, I’d suggest to only rely on apply rules here where all safety mechanisms are in place. Standalone for loops might be handy, but should be used with care - they don’t always solve the purpose with object scopes and availability frames.

Cheers,
Michael

2 Likes

Ok, that definitely helps my understanding. I can see how using Icinga2 takes some creativity… ! I say that in a good way because I can conceive that there must be a way to do it within the confines of the apply rules but seems like I need to exercise my creative muscle. Feels like the same muscle that needs flexing when looking at a problem from an functional vs imperative approach. Will go scratch my head now. :smile: Thanks, @dnsmichi!

Andy

2 Likes