Hi all I didn't get much consideration with my latest messages, but I will try again :-D
As said on May 24th, I am trying to manage /etc/hosts in a decent way, adding records that should be there but aren't, or removing some which are there but shouldn't. After a lot of trial and error, and some regex wrestling, I came out with the first policy in attachment which does the job. Then I said: OK, let's make it parametric, so that we can specify the hosts file path and which IP address we consider the main one (instead of relying on $(sys.ipv4) for example). So I changed a few things here and there and produced the second policy in attachment. But in this second policy, readstringarray seems to have stopped working properly: $(count) gets its value right, but $(records) is not filled out, so $(ip) doesn't get it's stuff, all the classes I use to determine what to do don't get defined, and spurious stuff is added to the file. I inspected the file and tested several times, but I came to nothing. A "diff -w" of these two files doesn't show any change that should result in this behaviour. Am I missing anything obvious here? Thanks for your help Ciao -- bronto PS: tested with 3.3.1 and 3.3.2
body common control { bundlesequence => { "hostfiles" } ; inputs => { "cfengine_stdlib.cf" } ; version => "test" ; } bundle agent hostfiles { vars: "hostfile" string => "/var/cfengine/testfiles/hosts" ; files: "$(hostfile)" edit_line => fix_host_entries ; } bundle edit_line fix_host_entries { vars: "re_ipv4" string => escape("$(sys.ipv4)") ; "re_local" string => escape("127.0.1.1") ; "re_fqhost" string => escape("$(sys.fqhost)") ; "re_uqhost" string => escape("$(sys.uqhost)") ; "re_domain" string => escape("$(sys.domain)") ; "re_hostaddr" string => "($(re_local)|$(re_ipv4))" ; "host_record" string => "$(sys.fqhost) $(sys.uqhost)" ; "ipv6standard" slist => { "::1 localhost", "fe00::0 ip6-localnet", "ff00::0 ip6-mcastprefix", "ff02::1 ip6-allnodes", "ff02::2 ip6-allrouters", } ; "count" int => readstringarray("records", # array to populate "$(hostfiles.hostfile)", # file to read "\s*#[^\n]*?", # match comments "\s+", # match fields "1000", # max entries "80000") ; # max bytes "ip" slist => getindices("records") ; "ipclass[$(ip)]" string => canonify("$(ip)") ; "myaddr_class" string => canonify("$(sys.ipv4)") ; classes: "has_ip_$(ipclass[$(ip)])" expression => "any" ; "has_ipv4_localhost" expression => "has_ip_127_0_0_1" ; "has_ipv6_localhost" expression => "has_ip___1" ; "has_host_record" or => { "has_ip_127_0_1_1", "has_ip_$(myaddr_class)" } ; delete_lines: # Thanks to oha for his help with this pattern. I was trying to solve # this with a negative lookbehind, (host name *not* preceded by...) and # I didn't realise that a positive lookbehind at the ^ (beginning of # line NOT followed by...) would work! # Anyway, the pattern below means: # Beginning of line # not followed by either the local address or our ip address, and a space # then we start matching the real thing: # an address (IPv4 or IPv6; this RE matches much more than that, KISS...) # a sequence starting with whitespace followed by an hostname, 0 or more times # then whitespace and our hostname, either unqualified or qualified # then again whitespace and hostname sequence, 0 or more times # whitespace padding the end of line, 0 or more # # This means that this RE matches all the lines which contain our hostname, # either qualified or unqualfied, but not associated with a proper IP address. # It's a dangerous line, and we wipe it. hosts_records_normalized:: "^(?!($(re_local)|$(re_ipv4))\s)[a-fA-F0-9:\.]+(\s+[a-zA-Z0-9\.\-]+)*\s$(re_uqhost)(\.$(re_domain))?(\s+[a-zA-Z0-9\.\-]+)*\s*" ; insert_lines: hosts_records_normalized:: "127.0.0.1$(const.t)localhost" ifvarclass => "!has_ipv4_localhost"; "::1$(const.t)localhost ip6-localhost ip6-loopback" ifvarclass => "!has_ipv6_localhost" ; "$(sys.ipv4)$(const.t)$(host_record)" ifvarclass => "!has_host_record" ; replace_patterns: hosts_records_normalized:: "^127\.0\.0\.1\t(?!localhost\b)(.*)" replace_with => value("127.0.0.1$(const.t)localhost $(match.1)"), ifvarclass => "has_ipv4_localhost" ; "^::1\t(?!localhost\b)(.*)" replace_with => value("::1$(const.t)localhost $(match.1)"), ifvarclass => "has_ipv6_localhost" ; "^$(re_hostaddr)\t(?!$(re_fqhost)\s+$(re_uqhost))(.*)" replace_with => value("$(match.1)$(const.t)$(host_record)$(match.2)"), ifvarclass => "has_host_record" ; !hosts_records_normalized:: "^(\s*)([a-zA-z0-9:\.]+)( +)" replace_with => value("$(match.2)$(const.t)"), classes => if_ok("hosts_records_normalized"), comment => "Normal records begin at column one and are in IP\tNAMES format" ; reports: __debug__:: "Read $(count) records from $(edit.filename)" ; "Address matched: $(ip)" ; }
body common control { bundlesequence => { "test" } ; inputs => { "cfengine_stdlib.cf" } ; version => "test" ; } bundle agent test { methods: "hosts" usebundle => hostfile("/var/cfengine/testfiles/hosts","$(sys.ipv4)") ; } bundle agent hostfile(hostfile,myip) { files: "$(hostfile)" edit_line => fix_host_entries("$(myip)") ; reports: } bundle edit_line fix_host_entries(myip) { vars: "re_myip" string => escape("$(myip)") ; "re_local" string => escape("127.0.1.1") ; "re_fqhost" string => escape("$(sys.fqhost)") ; "re_uqhost" string => escape("$(sys.uqhost)") ; "re_domain" string => escape("$(sys.domain)") ; "re_hostaddr" string => "($(re_local)|$(re_myip))" ; "host_record" string => "$(sys.fqhost) $(sys.uqhost)" ; "ipv6standard" slist => { "::1 localhost", "fe00::0 ip6-localnet", "ff00::0 ip6-mcastprefix", "ff02::1 ip6-allnodes", "ff02::2 ip6-allrouters", } ; "count" int => readstringarray("records", # array to populate "$(edit.filename)", # file to read "\s*#[^\n]*?", # match comments "\s+", # match fields "1000", # max entries "80000") ; # max bytes "ip" slist => getindices("records") ; "ipclass[$(ip)]" string => canonify("$(ip)") ; "myaddr_class" string => canonify("$(myip)") ; classes: "has_ip_$(ipclass[$(ip)])" expression => "any" ; "has_ipv4_localhost" expression => "has_ip_127_0_0_1" ; "has_ipv6_localhost" expression => "has_ip___1" ; "has_host_record" or => { "has_ip_127_0_1_1", "has_ip_$(myaddr_class)" } ; delete_lines: # Thanks to oha for his help with this pattern. I was trying to solve # this with a negative lookbehind, (host name *not* preceded by...) and # I didn't realise that a positive lookbehind at the ^ (beginning of # line NOT followed by...) would work! # Anyway, the pattern below means: # Beginning of line # not followed by either the local address or our ip address, and a space # then we start matching the real thing: # an address (IPv4 or IPv6; this RE matches much more than that, KISS...) # a sequence starting with whitespace followed by an hostname, 0 or more times # then whitespace and our hostname, either unqualified or qualified # then again whitespace and hostname sequence, 0 or more times # whitespace padding the end of line, 0 or more # # This means that this RE matches all the lines which contain our hostname, # either qualified or unqualfied, but not associated with a proper IP address. # It's a dangerous line, and we wipe it. hosts_records_normalized:: "^(?!($(re_local)|$(re_myip))\s)[a-fA-F0-9:\.]+(\s+[a-zA-Z0-9\.\-]+)*\s$(re_uqhost)(\.$(re_domain))?(\s+[a-zA-Z0-9\.\-]+)*\s*" ; insert_lines: hosts_records_normalized:: "127.0.0.1$(const.t)localhost" ifvarclass => "!has_ipv4_localhost"; "::1$(const.t)localhost ip6-localhost ip6-loopback" ifvarclass => "!has_ipv6_localhost" ; "$(myip)$(const.t)$(host_record)" ifvarclass => "!has_host_record" ; replace_patterns: hosts_records_normalized:: "^127\.0\.0\.1\t(?!localhost\b)(.*)" replace_with => value("127.0.0.1$(const.t)localhost $(match.1)"), ifvarclass => "has_ipv4_localhost" ; "^::1\t(?!localhost\b)(.*)" replace_with => value("::1$(const.t)localhost $(match.1)"), ifvarclass => "has_ipv6_localhost" ; "^$(re_hostaddr)\t(?!$(re_fqhost)\s+$(re_uqhost))(.*)" replace_with => value("$(match.1)$(const.t)$(host_record)$(match.2)"), ifvarclass => "has_host_record" ; !hosts_records_normalized:: "^(\s*)([a-zA-z0-9:\.]+)( +)" replace_with => value("$(match.2)$(const.t)"), classes => if_ok("hosts_records_normalized"), comment => "Normal records begin at column one and are in IP\tNAMES format" ; reports: __debug__:: "Read $(count) records from $(edit.filename)" ; "Address matched: $(ip)" ; }
_______________________________________________ Help-cfengine mailing list Help-cfengine@cfengine.org https://cfengine.org/mailman/listinfo/help-cfengine