Dynamic assign/ignore where clauses?

I’m trying to convince Icinga to handle things slightly more intelligently, however this has me stumped:

object Host "some.host.name" {
    // other config lines here
    groups += ["windows"]
    vars.winsvcs += [
        "A Windows Service",
        "Another Windows Service",
    ]
}
// 7 lines of header comments

apply Service "winsvc" for (winsvc in host.vars.winsvcs) {
    import "generic-service"
    check_command ="snmp-service"
    display_name = winsvc
    vars.snmp_service_name = winsvc
    vars.snmp_retries = 2
    vars.snmp_timeout = 15

    var knownServices = get_services(host.name).map(X=>X.name)
    assign where match("*"+winsvc, this.knownServices, MatchAny) == false
    ignore where match("*"+winsvc, this.knownServices, MatchAny)
}

the result:

[2024-02-05 15:35:44 +0100] critical/config: Error: An object with type 'Service' and name 'some.host.name!winsvcA Windows Service' already exists (in /etc/icinga2/zones.d/global-templates/Services/winsvcs.conf: 9:1-9:56), new declaration: in /etc/icinga2/zones.d/global-templates/Services/winsvcs.conf: 9:1-9:56
Location: in /etc/icinga2/zones.d/global-templates/Services/winsvcs.conf: 9:1-9:56
/etc/icinga2/zones.d/global-templates/Services/winsvcs.conf(7): #=- ---------------------------------------------------------------------------
/etc/icinga2/zones.d/global-templates/Services/winsvcs.conf(8):
/etc/icinga2/zones.d/global-templates/Services/winsvcs.conf(9): apply Service "winsvc" for (winsvc in host.vars.winsvcs) {
                                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/etc/icinga2/zones.d/global-templates/Services/winsvcs.conf(10):     import "generic-service"
/etc/icinga2/zones.d/global-templates/Services/winsvcs.conf(11):     check_command ="snmp-service"

Expected result: no complaints about services already being defined as the assign/ignore clauses were expected to take care of that issue.

I pulled the get_services(…) call into a separate var as I was unsure whether or not match(…) could handle it.

Using the console I’ve verified that the get_services(…) call indeed returns an array, which I’d expect was fine:

<1> => match("*A Windows Service",get_services("some.host.name").map(X=>X.__name),MatchAny)
true
<2> =>

and

<1> => winsvc="A Windows Service"
null
<2> => match("*"+winsvc,get_services("some.host.name").map(X=>X.__name),MatchAny)
true

Have I missed something or am I simply expecting too much of DSL?

Hi bofhdk,
personally I don’t use that assign stuff, we follow another aproach. But maybe some thoughts:

  • Are you sure the service isn’t assigned via another rule already?
  • If “assign where” has the rule “== false”, shouldn’t the “ignore where” part have the rule “!= false”?

Regards,
Dirk

Hi Dirk,

I’m quite sure that this is the only place that this gets assigned. A recursive grep of the configuration seems to support this as well :slight_smile:

As I understand the (slightly cryptic) documentation, match() returns a boolean so the ‘== false’ is merely a substitute for ‘assign where !match(…)’ as I wasn’t sure that this oparticular notation would work.

/b

May I ask what you hope to achieve with this approach?
Because I can’t see any benefit.

Are you trying to circumvent Icingas “redfined” detection, in case someone defines the the same service name twice?

@log1c : Not quite. I’m trying to get Icinga to only apply the service once for any given host. It seems that it might be attempting to assign the same definition twice and that is causing it to break.

Since the error message is rather unhelpful (no surprise!) I’d like for the second attempt to merely fail (possibly with a warning) but for Icinga to continue.

That is what the apply for does anyway. It deploys one check for each entry in an array.
Why your config tries to define it twice, I don’t really get, tbh.

Example (from a Director config) that works

apply Service for (config in host.vars.m365_avepoint_booked_backupservices) {
    name = "avepoint-backup-health_" + config
    import "m365-avepoint-backup-health"

    assign where "m365-baas-dummy-hosts" in host.templates
    vars.m365_avepoint_jobmodule = config

    import DirectorOverrideTemplate
}

Hello @bofhdk!

I’m afraid you have to do exactly the opposite – to inline the get_services call.

Best,
A/K