#!/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 = < \$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: