ansible-tools/dynhosts
2022-09-04 15:57:05 +02:00

136 lines
3.9 KiB
Perl
Executable File

#!/usr/bin/perl
use strict; use warnings;
use JSON ();
use JSON::PP ();
use Getopt::Long qw(:config no_ignore_case bundling);
use File::Slurp;
use FindBin;
my %opts = (
list => 0
);
my $usage = <<EOT;
usage: $0 [OPTIONS..]
--list print JSON inventory
EOT
GetOptions(
"list" => \$opts{list},
"help|h" => sub {print $usage; exit 0}
) or die $usage;
my @inventory = read_file("$FindBin::Bin/../inventory");
my %groups;
my %hostvars;
my %blockvars;
my @blockgroups;
for (my $i = 0; $i <= $#inventory; $i++) {
my $line = $inventory[$i];
chomp $line;
next if $line =~ /^\s*#/;
$line =~ s/\s*$//;
if ($line =~ /^\s+/) {
print STDERR "Syntax error (leading whitespace) in line ". ($i + 1) .
": $line\n";
next;
}
next if $line !~ /\S/;
# continuation lines (leading whitespace):
while ($i + 1 <= $#inventory) {
last if $inventory[$i + 1] !~ /^\s+([^# ].*)/;
$i++;
my $cont = $1;
$cont =~ s/\s*$//;
$line .= " ". $cont;
}
# group definition: [group < parent + group < parent...] default_vars...
if ($line =~ /^\[([a-z0-9_< +]+)\](\s*.*)/i) {
my ($grouptext, $vars) = ($1, $2);
@blockgroups = ();
for my $x (split /\+/, $grouptext) {
if ($x !~ /^\s*([a-z0-9_]+)(\s*<\s*([a-z0-9_]+))?\s*$/) {
print STDERR "Invalid group definition in line ". ($i + 1) .
": $line\n";
next;
}
my ($group, $parent) = ($1, $3);
push @blockgroups, $group;
$groups{$group} = {hosts => []} if !exists $groups{$group};
if ($parent) {
$groups{$parent} = {hosts => []} if !exists $groups{$parent};
$groups{$parent}->{children} = []
if !exists $groups{$parent}->{children};
push @{$groups{$parent}->{children}}, $group;
}
}
%blockvars = ();
if ($vars) {
while ($vars =~ /\s+([a-z0-9_]+)=(\S*)/gci) {
my ($a, $v) = ($1, $2);
$blockvars{$a} = $v;
}
if (length $vars != pos $vars) {
print STDERR "Invalid parameters in line ". ($i + 1) .
": $line\n";
}
}
next;
}
if ($line !~ /^(\S+)\s+(\S+)(\s+\[(\S+)\])?(\s+.*)?/) {
print STDERR "Syntax error in line ". ($i + 1) .": $line\n";
next;
}
my ($name, $addr, $gr, $rest) = ($1, $2, $4, $5);
my @gr;
@gr = split /,/, $gr if $gr;
push @gr, @blockgroups;
for my $g (@gr) {
push @{$groups{$g}->{hosts}}, $name;
}
if (exists $hostvars{$name}) {
print STDERR "Duplicate host definition in line ". ($i + 1) .": $line\n";
}
$hostvars{$name} = { %blockvars };
$hostvars{$name}->{ansible_host} = $addr;
if ($rest and $rest =~ s/\s+(\{.*\})//) {
my $jsonstring = $1;
my $jo = JSON::PP->new->relaxed->allow_singlequote->allow_barekey;
my $data;
eval {
$data = $jo->decode($jsonstring);
};
if ($@) {
print STDERR "Invalid JSON in line ". ($i + 1) .": $jsonstring\n";
}
for my $k (keys %$data) {
$hostvars{$name}->{$k} = $data->{$k};
}
}
if ($rest) {
while ($rest =~ /\s+([a-z0-9_]+)=(\S*)/gci) {
my ($a, $v) = ($1, $2);
$hostvars{$name}->{$a} = $v;
}
if (length $rest != pos $rest) {
print STDERR "Invalid parameters in line ". ($i + 1) .": $line\n";
}
}
}
$groups{_meta}->{hostvars} = \%hostvars;
if ($opts{list}) {
my $json = JSON->new->canonical->pretty;
print $json->encode(\%groups);
} else {
printf "%i hosts in %i groups.\n",
scalar keys %hostvars, (scalar keys %groups) - 1;
}
__END__
# vim: set tabstop=4 shiftwidth=4 expandtab smarttab: