Using variable as value for [/find] parameter

I have the following script:

/ip dns static
:foreach rec in=[find type=[]] do={
  :local name [get $rec name]
  :local address [get $rec address]

  :if ([:len $name] > 0) do={
    :put ("Checking A record: " . $rec . " [name=" . $name . ", address=" . $address . "]")
    :local expectedIpv6Address ("::ffff:" . $address)
    :local existingAAAARecordsWithThisName [find name=$name type=AAAA]

    :if ([:len $existingAAAARecordsWithThisName] > 0) do={
      :foreach existingAAAARecord in=$existingAAAARecordsWithThisName do={
        :put ("Removing old AAAA record: " . $existingAAAARecord . " [name=" . [get $existingAAAARecord name] . ", address=" . [get $existingAAAARecord address] . "]")
        # remove $existingAAAARecord
      }
    }

    :put ("Creating AAAA record for name=" . $name . " address=" . $expectedIpv6Address)
    # add name=$name address=$expectedIpv6Address type=AAAA
  }
}

There’s a problem with

:local existingAAAARecordsWithThisName [find name=$name type=AAAA]
  • the value becomes an array of all AAAA entries, not just the one matching “name” - it’s like the
$name

is not applied at all to the find function.

What am I missing?

Why type=[] instead of !type ?

Simply do that.

/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $name]="str")] do={
    remove [find where (name=[get $item name]) and (type=AAAA)]
    add name=[get $item name] type=AAAA address=("::ffff:$[get $item address]")
}

Why complicate script with useless frills for debug???..

I was not aware on how to use that, thanks for the hint!


To understand what's happening and not deploy the code that might break stuff. Mikrotik scripting has a lots of quirks, e.g. you might expect that [find type=A] would return a list of A records. But it doesn't. Hence the need to be verbose, especially while designing scripts. And the need to maintain them properly, e.g. in case a ROS update drops that breaks backwards compatibility.

Thank you for the suggestion, but I'm still unaware on why my solution doesn't work :confused: Any suggestions? Is it a scoping issue (i.e. the [find ...] creates a new scope, in which the $name does not exist?)

Ok, this integrate debug…

/ip dns static
/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $name]="str")] do={
    :local newadd ("::ffff:$[get $item address]")
    :local iname  [get $item name]
    :local removethis [find where (name=$iname) and (type=AAAA)]
    :foreach idx in=$removethis do={
        :put "Removing AAAA name $[get $idx name] with address $[get $idx address]"
    }
    remove $removethis
    :put "Adding AAAA name $iname with address $newadd"
    add name=$iname type=AAAA address=$newadd
}

Warning, I fix a misspell error on previous post #3, check twice.
https://forum.mikrotik.com/viewtopic.php?p=995896#p995891



Is why I write (!type or (type=A)) and not only !type

I write the description for your other question, please wait…

Thank you! Your script has helped me debug the underlying issue: the $name appears to be a reserved keyword, as soon as I changed it to $iname my script started to work correctly.

oh, i was writing to you…
bravo!

In fact that word is reserved in the context /ip dns static, if you notice I didn’t declare “name” but I used it… :wink:
and ([:typeof $name]=“str”)] do={

The problem on OP script is also the check “[:len $name] > 0”.
Since the name can be nil is wrong compare “nil” with something…
I omith the check of the name because on the first query I request records of “type=A” that have the name specified :wink:

Fixed “AAA” typo and one variable error (not used the existant variable, call again “get” instead) on script #5



You can do also the same for RegEx records… :wink:

Debug version

/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $regexp]="str")] do={
    :local newadd ("::ffff:$[get $item address]")
    :local iregex [get $item regexp]
    :local removethis [find where (regexp=$iregex) and (type=AAAA)]
    :foreach idx in=$removethis do={
        :put "Removing AAAA RegEx $[get $idx regexp] with address $[get $idx address]"
    }
    remove $removethis
    :put "Adding AAAA RegEx $iregex with address $newadd"
    add regexp=$iregex type=AAAA address=$newadd
}

Short version

/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $regexp]="str")] do={
    remove [find where (regexp=[get $item regexp]) and (type=AAAA)]
    add regexp=[get $item regexp] type=AAAA address=("::ffff:$[get $item address]")
}

Thank you for the insights!

I even went further and upgraded the solution like this:

/ip dns static
:foreach ARecord in=[find where (!type or (type=A)) and ([:typeof $name]="str")] do={
    :local correctIpv6 ("::ffff:$[get $ARecord address]")
    :local iname [get $ARecord name]
    :local wrongAAAA [find where (name=$iname) and (type=AAAA) and !(address=$correctIpv6)]
    :local correctAAAA [find where (name=$iname) and (type=AAAA) and (address=$correctIpv6)]

    :foreach idx in=$wrongAAAA do={
        :put "Removing AAA record $[get $idx name] with address $[get $idx address]"
        remove $idx
    }
    :if ([:len $correctAAAA] = 0) do={
        :put "Adding AAAA record $iname with address $correctIpv6"
        add name=$iname type=AAAA address=$correctIpv6
    }
}

That way, I can run this in scheduler every couple of minutes and nothing will happen if all the AAAA records are aligned properly to their corresponding A records.

I hope it's not too much to ask - would you mind proof-checking for some pitfalls, I might not be aware of?

It’s more efficient to remove all records with one command, rather than deleting each individual record individually,
that’s why on my versions remove is outside the :put logging.

For Name

/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $name]="str")] do={
    :local newadd ("::ffff:$[get $item address]")
    :local iname  [get $item name]
    :local removethis [find where (name=$iname) and (type=AAAA) and (address!=$newadd)]
    :foreach idx in=$removethis do={
        :put "Removing AAAA name $[get $idx name] with address $[get $idx address]"
    }
    remove $removethis
    :if ([:len [find where (name=$iname) and (type=AAAA) and (address=$newadd)]] = 0) do={
        :put "Adding AAAA name $iname with address $newadd"
        add name=$iname type=AAAA address=$newadd
    }
}

For RegEx

/ip dns static
:foreach item in=[find where (!type or (type=A)) and ([:typeof $regexp]="str")] do={
    :local newadd ("::ffff:$[get $item address]")
    :local iregex [get $item regexp]
    :local removethis [find where (regexp=$iregex) and (type=AAAA) and (address!=$newadd)]
    :foreach idx in=$removethis do={
        :put "Removing AAAA RegEx $[get $idx regexp] with address $[get $idx address]"
    }
    remove $removethis
    :if ([:len [find where (regexp=$iregex) and (type=AAAA) and (address=$newadd)]] = 0) do={
        :put "Adding AAAA RegEx $iregex with address $newadd"
        add regexp=$iregex type=AAAA address=$newadd
    }
}