-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhosts2inventory.pl
executable file
·141 lines (121 loc) · 4.5 KB
/
hosts2inventory.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/perl
# hosts2inventory.pl--Parse a special hosts file to create an Ansible inventory
# JP, 2020-04-08
# Example input file, e.g.: host2.txt
# # Comments
# node1
# group1
# group2
# node2
# group1
# group3
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
((my $PROGRAM = $0) =~ s/^.*(\/|\\)//); # remove up to last "\" or "/"
# Declare everything to keep -w and use strict happy
my ( $debug, @warnings, %groups, %all, $node, %nodes_in, %variables_for, );
our ( $opt_o, $opt_h, $opt_D, );
use strict;
use warnings;
use POSIX qw(strftime); # Easy date/time printing
use Getopt::Std;
getopts('o:hD');
# Options
if ( $opt_h ) {
print "\nParse a special hosts file to create an Ansible inventory\n";
print "Usage:\n";
print "$PROGRAM hosts2.txt # Writes hosts\n";
print "\tcat /tmp/hosts.txt | $PROGRAM (-o <hosts>)\n";
print "\tgc | $PROGRAM (-o <hosts>)\n";
print "\t$PROGRAM (-h -D) # for help or debugging\n";
print "\n\tUse '-o -' for STDOUT, default if omitted is './hosts'\n";
exit 0;
}
# NOTES:
# Does not have good handling for unexpected CWD or missing dirs. It
# expects to run in the parent dir of ./group_vars/ where the inventory
# file should be written.
if ( $opt_D ) {
$debug = 1;
} else {
$debug = 0;
}
##########################################################################
# Main
INPUT_LOOP:
while ( my $aline = <> ) {
chomp($aline);
next INPUT_LOOP if $aline =~ m/^\s*$/; # Skip blank lines
next INPUT_LOOP if $aline =~ m/(^#)|(^\s*$)/; # Skip comments and white space
# NO!!! $aline =~ s/^\s+|\s+$//g; # Remove leading/trailing white space
$aline =~ s/\s+$//g; # Remove trailing white space
if ( $aline =~ m/^(\w+.*$)/ ) { # NODE
$node = "$1";
# Detect and omit duplicate nodes
$all{$node}++;
if ( $all{$node} > 1 ) {
push(@warnings, "Duplicate node '$node' detected--omitting from output!");
}
if ($debug >= 1) { warn "Found '$node'\n"; }
} elsif ( $aline =~ m/^\s+(\w+.*?=\w+.*$)/ ) { # VARIABLE
my $variable = "$1";
push( @{ $variables_for{$node} }, $variable);
if ($debug >= 1) { warn "'$node' has variable '$variable'\n"; }
} elsif ( $aline =~ m/^\s+(\w+.*$)/ ) { # GROUP
my $group = "$1";
$groups{$group}++;
push( @{ $nodes_in{$group} }, $node);
if ($debug >= 1) { warn "'$node' is in group '$group'\n"; }
} else {
warn "WARNING: unparsed line ~$aline~\n";
}
} # end of while input
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Write the inventory output file
if ( $opt_o and $opt_o eq '-' ) {
open(OF, '>-') or die "Error opening STDOUT for output: $!";
} elsif ( ! $opt_o ) {
$opt_o = 'hosts'; # Ugly hack to default to ./hosts...
open(OF, '>', "$opt_o") or die "Error opening '$opt_o' for output: $!";
} else {
open(OF, '>', "$opt_o") or die "Error opening '$opt_o' for output: $!";
}
print OF "# DO NOT MANUALLY EDIT THIS FILE!\n";
print OF "# Ansible inventory generated by '$PROGRAM' on ", strftime("%Y-%m-%d %H:%M:%S", localtime), "\n";
# Always have the [all] group first, and list any Ansible variables here
print OF "\n# [all] ", scalar(keys(%all)), " nodes\n";
foreach my $node ( sort keys %all ) {
print OF "$node"; # Always have this
if ( $variables_for{$node} ) { # MIGHT have these
foreach my $variable ( sort(@{ $variables_for{$node} }) ) {
print OF "\t$variable";
}
}
print OF "\n";
}
foreach my $group ( sort keys %groups ) {
if ($debug >= 1) { warn "GROUP: '$group'\n"; }
if ( $nodes_in{$group} ) {
# This group has some nodes in it. Good. Write 'em.
print OF "\n[$group] # ", @{ $nodes_in{$group} }+0, " nodes\n";
foreach my $node ( sort(@{ $nodes_in{$group} }) ) {
print OF "$node\n";
}
} else {
push(@warnings, "'$group' is defined but has no nodes in it! Better fix that!");
}
}
# Display warnings, if we had any
if ( @warnings ) {
warn "\nWARNINGS!\n";
foreach my $warning ( @warnings ) {
warn "$warning\n";
}
}
# Data Structures
# Hashes:
# %all Simple counter hash
# %groups Simple counter hash
# %variables_for Hash of key "node" has a list of variables (if any)
# %nodes_in Hash of key "group" has a list of nodes
# Lists:
# @warnings If any...