File Coverage

blib/lib/Rex/CMDB/YAMLwithRoles.pm
Criterion Covered Total %
statement 106 140 75.7
branch 20 48 41.6
condition 3 9 33.3
subroutine 12 21 57.1
pod 0 2 0.0
total 141 220 64.0


line stmt bran cond sub pod time code
1             # (c) Jan Gehring
2             # (c) Zane C. Bowers-Hadley
3              
4             package Rex::CMDB::YAMLwithRoles;
5              
6 2     2   547487 use 5.010001;
  2         9  
7 2     2   11 use strict;
  2         9  
  2         112  
8 2     2   12 use warnings;
  2         5  
  2         187  
9              
10             our $VERSION = '0.0.1'; # VERSION
11              
12 2     2   12 use base qw(Rex::CMDB::Base);
  2         45  
  2         910  
13              
14 2     2   238661 use Rex::Commands -no => [qw/get/];
  2         4  
  2         21  
15 2     2   6424 use Rex::Logger;
  2         5  
  2         67  
16 2     2   36 use YAML::XS;
  2         10301  
  2         182  
17 2     2   19 use Data::Dumper;
  2         5  
  2         124  
18 2     2   24 use Hash::Merge qw/merge/;
  2         12337  
  2         3656  
19              
20             require Rex::Commands::File;
21              
22             sub new {
23 1     1 0 335395 my $that = shift;
24 1   33     9 my $proto = ref($that) || $that;
25 1         6 my $self = {@_};
26              
27 1         9 $self->{merger} = Hash::Merge->new();
28              
29 1 50       150 if ( !defined $self->{merge_behavior} ) {
30             $self->{merger}->specify_behavior(
31             {
32             SCALAR => {
33 0     0   0 SCALAR => sub { $_[0] },
34 0     0   0 ARRAY => sub { $_[0] },
35 0     0   0 HASH => sub { $_[0] },
36             },
37             ARRAY => {
38 0     0   0 SCALAR => sub { $_[0] },
39 0     0   0 ARRAY => sub { $_[0] },
40 0     0   0 HASH => sub { $_[0] },
41             },
42             HASH => {
43 0     0   0 SCALAR => sub { $_[0] },
44 0     0   0 ARRAY => sub { $_[0] },
45 0     0   0 HASH => sub { Hash::Merge::_merge_hashes( $_[0], $_[1] ) },
46             },
47             },
48 0         0 'REX_DEFAULT',
49             ); # first found value always wins
50              
51 0         0 $self->{merger}->set_behavior('REX_DEFAULT');
52             } else {
53 1 50       5 if ( ref $self->{merge_behavior} eq 'HASH' ) {
54 0         0 $self->{merger}->specify_behavior( $self->{merge_behavior}, 'USER_DEFINED' );
55 0         0 $self->{merger}->set_behavior('USER_DEFINED');
56             } else {
57 1         7 $self->{merger}->set_behavior( $self->{merge_behavior} );
58             }
59             }
60              
61 1         73 bless( $self, $proto );
62              
63             # turn roles off by default
64 1 50       8 if ( !defined( $self->{use_roles} ) ) {
65 0         0 $self->{use_roles} = 0;
66             }
67              
68             # set the default role path ro 'cmdb/roles'
69 1 50       6 if ( !defined( $self->{roles_path} ) ) {
70 1         40 $self->{roles_path} = File::Spec->join( $self->{path}, 'roles' );
71             }
72              
73             # if parsing failure should be fatal
74             # default true
75 1 50       4 if ( !defined( $self->{parse_error_fatal} ) ) {
76 1         17 $self->{parse_error_fatal} = 1;
77             }
78              
79             # die if the role does not exist
80             # default true
81 1 50       6 if ( !defined( $self->{missing_role_fatal} ) ) {
82 1         3 $self->{missing_role_fatal} = 1;
83             }
84              
85             # default to false, config overwrites role settings
86 1 50       4 if ( !defined( $self->{roles_merge_after} ) ) {
87 1         3 $self->{roles_merge_after} = 0;
88             }
89              
90 1         141 return $self;
91             } ## end sub new
92              
93             sub get {
94 2     2 0 2286 my ( $self, $item, $server ) = @_;
95              
96 2         9 $server = $self->__get_hostname_for($server);
97              
98 2         33 my $result = {};
99              
100             # keep this out here so generated when the files are loaded
101             # keep it around later for role processing
102 2         6 my %template_vars;
103              
104 2 50       7 if ( $self->__cache->valid( $self->__cache_key() ) ) {
105 0         0 $result = $self->__cache->get( $self->__cache_key() );
106             } else {
107              
108 2         54 my @files = $self->_get_cmdb_files( $item, $server );
109              
110 2         40 Rex::Logger::debug( Dumper( \@files ) );
111              
112             # configuration variables
113 2         445 my $config_values = Rex::Config->get_all;
114 2         39 for my $key ( keys %{$config_values} ) {
  2         22  
115 2 50       16 if ( !exists $template_vars{$key} ) {
116 2         17 $template_vars{$key} = $config_values->{$key};
117             }
118             }
119 2         11 $template_vars{environment} = Rex::Commands::environment();
120              
121 2         25 for my $file (@files) {
122 8         348 Rex::Logger::debug("CMDB - Opening $file");
123 8 100       233 if ( -f $file ) {
124              
125 4         9 my $content = eval { local ( @ARGV, $/ ) = ($file); <>; };
  4         37  
  4         440  
126 4         70 my $t = Rex::Config->get_template_function();
127 4         4367 $content .= "\n"; # for safety
128 4         15 $content = $t->( $content, \%template_vars );
129              
130 4         8834 my $ref;
131             my $parse_error;
132 4         12 eval { $ref = Load($content); };
  4         466  
133 4 50       22 if ($@) {
134 0         0 $parse_error = $@;
135             }
136              
137             # only merge it if we have a actual result
138 4 50       55 if ( !defined($parse_error) ) {
139 4         7403 $result = $self->{merger}->merge( $result, $ref );
140             } else {
141 0         0 my $error = 'Failed to parse YAML config file "' . $file . '" with error... ' . $parse_error;
142 0 0       0 if ( $self->{parse_error_fatal} ) {
143 0         0 die($error);
144             } else {
145 0         0 warn($error);
146             }
147             }
148             } ## end if ( -f $file )
149             } ## end for my $file (@files)
150             } ## end else [ if ( $self->__cache->valid( $self->__cache_key...))]
151              
152             # if use_roles is true, process the roles variablesif set
153             # the item has roles and that the roles is a array
154 2 50 33     195 if ( $self->{use_roles}
      33        
155             && ( defined( $result->{roles} ) )
156             && ( ref( $result->{roles} ) eq 'ARRAY' ) )
157             {
158 2         11 Rex::Logger::debug("CMDB - Starting role processing");
159              
160             # load each role
161 2         16 foreach my $role ( @{ $result->{roles} } ) {
  2         7  
162 2         14 Rex::Logger::debug( "CMDB - Processing role '" . $role . "'" );
163 2         98 my $role_file = File::Spec->join( $self->{roles_path}, $role . '.yaml' );
164              
165             # if the file exists, load it
166 2 50       57 if ( -f $role_file ) {
167              
168 2         4 my $content = eval { local ( @ARGV, $/ ) = ($role_file); <>; };
  2         15  
  2         190  
169 2         14 my $t = Rex::Config->get_template_function();
170 2         110 $content .= "\n"; # for safety
171 2         9 $content = $t->( $content, \%template_vars );
172              
173 2         15127 my $ref;
174             my $parse_error;
175 2         6 eval { $ref = Load($content); };
  2         261  
176 2 50       11 if ($@) {
177 0         0 $parse_error = $@;
178             }
179              
180             # only merge it if we have a actual result
181             # undef causes the merge feature to wipe it all out
182             # that and it did error... so we need to handle the error
183 2 50       9 if ( !defined($parse_error) ) {
184              
185             # don't let host variables override the role if
186             # roles_merge_after is true
187 2 50       9 if ( $self->{roles_merge_after} ) {
188 0         0 $result = $self->{merger}->merge( $ref, $result );
189             } else {
190 2         12 $result = $self->{merger}->merge( $result, $ref );
191             }
192             } else {
193 0         0 my $error = 'Failed to parse YAML role file "' . $role_file . '" with error... ' . $parse_error;
194 0 0       0 if ( $self->{parse_error_fatal} ) {
195 0         0 die($error);
196             } else {
197 0         0 warn($error);
198             }
199             }
200             } else {
201 0         0 my $error = "The role '" . $role . "' is specified by the file '" . $role_file . "' does not eixst";
202 0 0       0 if ( $self->{missing_role_fatal} ) {
203 0         0 die($error);
204             } else {
205 0         0 warn($error);
206             }
207             }
208             } ## end foreach my $role ( @{ $result->{roles} } )
209             } ## end if ( $self->{use_roles} && ( defined( $result...)))
210              
211 2 50       523 if ( defined $item ) {
212 0         0 return $result->{$item};
213             }
214 2         17 return $result;
215             } ## end sub get
216              
217             sub _get_cmdb_files {
218 2     2   9 my ( $self, $item, $server ) = @_;
219              
220 2         7 $server = $self->__get_hostname_for($server);
221              
222 2         19 my @files;
223              
224 2 50       12 if ( !ref $self->{path} ) {
    0          
    0          
225 2         13 my $env = Rex::Commands::environment();
226 2         31 my $server_file = "$server.yaml";
227 2         8 my $default_file = 'default.yaml';
228             @files = (
229             File::Spec->join( $self->{path}, $env, $server_file ),
230             File::Spec->join( $self->{path}, $env, $default_file ),
231             File::Spec->join( $self->{path}, $server_file ),
232 2         81 File::Spec->join( $self->{path}, $default_file ),
233             );
234             } elsif ( ref $self->{path} eq "CODE" ) {
235 0         0 @files = $self->{path}->( $self, $item, $server );
236             } elsif ( ref $self->{path} eq "ARRAY" ) {
237 0         0 @files = @{ $self->{path} };
  0         0  
238             }
239              
240 2         43 my $os = Rex::Hardware::Host->get_operating_system();
241              
242 2         78360 @files = map { $self->_parse_path( $_, { hostname => $server, operatingsystem => $os, } ) } @files;
  8         932  
243              
244 2         198 return @files;
245             } ## end sub _get_cmdb_files
246              
247             1;
248              
249             __END__