File Coverage

lib/Rex/Commands/Host.pm
Criterion Covered Total %
statement 51 111 45.9
branch 10 36 27.7
condition 1 15 6.6
subroutine 11 14 78.5
pod 4 4 100.0
total 77 180 42.7


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             =head1 NAME
6              
7             Rex::Commands::Host - Edit /etc/hosts
8              
9             =head1 DESCRIPTION
10              
11             With this module you can manage the host entries in /etc/hosts.
12              
13             =head1 SYNOPSIS
14              
15             task "create-host", "remoteserver", sub {
16             create_host "rexify.org" => {
17             ip => "88.198.93.110",
18             aliases => ["www.rexify.org"],
19             };
20             };
21              
22             =head1 EXPORTED FUNCTIONS
23              
24             =cut
25              
26             package Rex::Commands::Host;
27              
28 33     33   69050 use v5.12.5;
  33         165  
29 33     33   184 use warnings;
  33         70  
  33         1584  
30              
31             our $VERSION = '1.14.3'; # VERSION
32              
33             require Rex::Exporter;
34 33     33   711 use Rex::Commands::Fs;
  33         77  
  33         260  
35 33     33   246 use Rex::Commands::File;
  33         102  
  33         376  
36 33     33   243 use Rex::Commands::MD5;
  33         86  
  33         293  
37 33     33   223 use Rex::Logger;
  33         104  
  33         224  
38 33     33   1010 use Data::Dumper;
  33         132  
  33         1706  
39              
40 33     33   195 use vars qw(@EXPORT);
  33         74  
  33         1646  
41 33     33   261 use base qw(Rex::Exporter);
  33         79  
  33         39706  
42              
43             @EXPORT = qw(create_host get_host delete_host host_entry);
44              
45             =head2 host_entry($name, %option)
46              
47             Manages the entries in /etc/hosts.
48              
49             host_entry "rexify.org",
50             ensure => "present",
51             ip => "88.198.93.110",
52             aliases => ["www.rexify.org"],
53             on_change => sub { say "added host entry"; };
54              
55             host_entry "rexify.org",
56             ensure => "absent",
57             on_change => sub { say "removed host entry"; };
58              
59             =cut
60              
61             sub host_entry {
62 0     0 1 0 my ( $res_name, %option ) = @_;
63              
64 0   0     0 $option{ensure} ||= "present";
65              
66 0         0 my $name = $res_name;
67 0 0       0 if ( exists $option{host} ) {
68 0         0 $name = $option{host};
69             }
70              
71 0         0 my $file = "/etc/hosts";
72              
73 0 0       0 if ( exists $option{file} ) {
74 0         0 $file = $option{file};
75             }
76              
77             Rex::get_current_connection()->{reporter}
78 0         0 ->report_resource_start( type => "host_entry", name => $res_name );
79              
80 0         0 my $old_md5 = md5($file);
81 0 0       0 if ( $option{ensure} eq "present" ) {
82 0         0 &create_host( $name, \%option );
83             }
84             else {
85 0         0 &delete_host($name);
86             }
87 0         0 my $new_md5 = md5($file);
88              
89 0 0       0 if ( $new_md5 ne $old_md5 ) {
90 0 0 0     0 if ( exists $option{on_change} && ref $option{on_change} eq "CODE" ) {
91 0         0 $option{on_change}->( $name, %option );
92             }
93              
94             Rex::get_current_connection()->{reporter}->report(
95 0         0 changed => 1,
96             message => "Resource host_entry changed to $option{ensure}"
97             );
98             }
99              
100             Rex::get_current_connection()->{reporter}
101 0         0 ->report_resource_end( type => "host_entry", name => $res_name );
102             }
103              
104             =head2 create_host($)
105              
106             Update or create a /etc/hosts entry.
107              
108             create_host "rexify.org", {
109             ip => "88.198.93.110",
110             aliases => ["www.rexify.org", ...]
111             };
112              
113             =cut
114              
115             sub create_host {
116 0     0 1 0 my ( $host, $data ) = @_;
117              
118 0 0       0 if ( !defined $data->{"ip"} ) {
119 0         0 Rex::Logger::info("You need to set an ip for $host");
120 0         0 die("You need to set an ip for $host");
121             }
122              
123 0   0     0 $data->{file} ||= "/etc/hosts";
124              
125 0         0 Rex::Logger::debug("Creating host $host");
126              
127 0         0 my @cur_host = get_host( $host, { file => $data->{file} } );
128 0 0       0 if ( !@cur_host ) {
129 0         0 my $fh = file_append $data->{file};
130 0         0 $fh->write( $data->{"ip"} . "\t" . $host );
131 0 0       0 if ( exists $data->{"aliases"} ) {
132 0         0 $fh->write( " " . join( " ", @{ $data->{"aliases"} } ) );
  0         0  
133             }
134 0         0 $fh->write("\n");
135 0         0 $fh->close;
136             }
137             else {
138 0 0 0     0 if ( $data->{"ip"} eq $cur_host[0]->{"ip"}
139 0 0       0 && join( " ", @{ $data->{"aliases"} || [] } ) eq
140 0         0 join( " ", @{ $cur_host[0]->{"aliases"} } ) )
141             {
142              
143 0         0 Rex::Logger::debug("Nothing to update for host $host");
144 0         0 return;
145              
146             }
147 0         0 Rex::Logger::debug("Host already exists. Updating...");
148              
149 0         0 delete_host( $host, $data->{file} );
150 0         0 return create_host(@_);
151             }
152             }
153              
154             =head2 delete_host($host)
155              
156             Delete a host from /etc/hosts.
157              
158             delete_host "www.rexify.org";
159              
160             =cut
161              
162             sub delete_host {
163 0     0 1 0 my ( $host, $file ) = @_;
164              
165 0         0 Rex::Logger::debug("Deleting host $host");
166 0   0     0 $file ||= "/etc/hosts";
167              
168 0 0       0 if ( get_host( $host, { file => $file } ) ) {
169 0         0 my $fh = file_read $file;
170 0         0 my @content = $fh->read_all;
171 0         0 $fh->close;
172              
173 0         0 my @new_content = grep { !/\s\Q$host\E\b/ } @content;
  0         0  
174              
175 0         0 $fh = file_write $file;
176 0         0 $fh->write(@new_content);
177 0         0 $fh->close;
178             }
179             else {
180 0         0 Rex::Logger::debug("Host does not exists.");
181             }
182             }
183              
184             =head2 get_host($host)
185              
186             Returns the information of $host in /etc/hosts.
187              
188             my @host_info = get_host "localhost";
189             say "Host-IP: " . $host_info[0]->{"ip"};
190              
191             =cut
192              
193             sub get_host {
194 2     2 1 4397 my ( $hostname, @lines ) = @_;
195              
196 2         14 Rex::Logger::debug("Getting host ($hostname) information");
197              
198 2         4 my $file = "/etc/hosts";
199              
200 2         3 my @content;
201 2 50 33     12 if ( @lines && !ref $lines[0] ) {
202 2         6 @content = @lines;
203             }
204             else {
205 0 0       0 if ( ref $lines[0] eq "HASH" ) {
206 0         0 $file = $lines[0]->{file};
207             }
208 0         0 my $fh = file_read $file;
209 0         0 @content = $fh->read_all;
210 0         0 $fh->close;
211             }
212              
213 2         5 my @hosts = _parse_hosts(@content);
214 2         4 my @ret;
215 2         8 for my $item (@hosts) {
216 8 100       18 if ( $item->{host} eq $hostname ) {
217 1         4 push @ret, $item;
218             }
219             else {
220 7 100       10 push @ret, $item if ( grep { $_ eq $hostname } @{ $item->{aliases} } );
  8         22  
  7         15  
221             }
222             }
223              
224 2         18 return @ret;
225             }
226              
227             sub _parse_hosts {
228 4     4   1593 my (@lines) = @_;
229              
230 4         8 my @ret;
231              
232 4         8 for my $line (@lines) {
233 24         41 chomp $line;
234 24 100       53 next if ( $line =~ m/^#/ );
235 20 100       38 next if ( !$line );
236 16 50       43 next if ( $line =~ m/^\s*$/ );
237              
238 16         64 my ( $ip, $_host, @aliases ) = split( /\s+/, $line );
239              
240 16         55 push @ret,
241             {
242             ip => $ip,
243             host => $_host,
244             aliases => \@aliases,
245             };
246              
247             }
248              
249 4         17 return @ret;
250             }
251              
252             1;