File Coverage

blib/lib/BigIP/ParseConfig.pm
Criterion Covered Total %
statement 12 146 8.2
branch 0 56 0.0
condition 0 44 0.0
subroutine 4 30 13.3
pod 22 22 100.0
total 38 298 12.7


line stmt bran cond sub pod time code
1             package BigIP::ParseConfig;
2              
3             # BigIP::ParseConfig, F5/BigIP configuration parser
4             #
5             # This program is free software; you can redistribute it and/or modify it under
6             # the terms of the GNU General Public License as published by the Free Software
7             # Foundation; either version 2 of the License, or (at your option) any later
8             # version.
9             #
10             # This program is distributed in the hope that it will be useful, but WITHOUT
11             # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12             # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13             # details.
14              
15             our $VERSION = '1.1.9';
16             my $AUTOLOAD;
17              
18              
19              
20 1     1   31468 use warnings;
  1         3  
  1         38  
21 1     1   6 use strict;
  1         3  
  1         2093  
22              
23              
24              
25             # Initialize the module
26             sub new {
27 0     0 1   my $class = shift;
28              
29 0           my $self = {};
30 0           bless $self, $class;
31              
32 0           $self->{'ConfigFile'} = shift;
33              
34 0           return $self;
35             }
36              
37             # Return a list of objects
38 0     0 1   sub monitors { return shift->_objectlist( 'monitor' ); }
39 0     0 1   sub nodes { return shift->_objectlist( 'node' ); }
40 0     0 1   sub partitions { return shift->_objectlist( 'partition' ); }
41 0     0 1   sub pools { return shift->_objectlist( 'pool' ); }
42 0     0 1   sub profiles { return shift->_objectlist( 'profile' ); }
43 0     0 1   sub routes { return shift->_objectlist( 'route' ); }
44 0     0 1   sub rules { return shift->_objectlist( 'rule' ); }
45 0     0 1   sub users { return shift->_objectlist( 'user' ); }
46 0     0 1   sub virtuals { return shift->_objectlist( 'virtual' ); }
47              
48             # Return an object hash
49 0     0 1   sub monitor { return shift->_object( 'monitor', shift ); }
50 0     0 1   sub node { return shift->_object( 'node', shift ); }
51 0     0 1   sub partition { return shift->_object( 'partition', shift ); }
52 0     0 1   sub pool { return shift->_object( 'pool', shift ); }
53 0     0 1   sub profile { return shift->_object( 'profile', shift ); }
54 0     0 1   sub route { return shift->_object( 'route', shift ); }
55 0     0 1   sub rule { return shift->_object( 'rule', shift ); }
56 0     0 1   sub user { return shift->_object( 'user', shift ); }
57 0     0 1   sub virtual { return shift->_object( 'virtual', shift ); }
58              
59             # Return a list of pool members
60             sub members {
61 0     0 1   my $self = shift;
62 0           my $pool = shift;
63              
64 0   0       $self->{'Parsed'} ||= $self->_parse();
65              
66 0 0         return 0 unless $self->{'Parsed'}->{'pool'}->{$pool}->{'members'};
67              
68 0 0         if ( ref $self->{'Parsed'}->{'pool'}->{$pool}->{'members'} eq 'ARRAY' ) {
69 0           return @{$self->{'Parsed'}->{'pool'}->{$pool}->{'members'}};
  0            
70             }
71             else {
72 0           return $self->{'Parsed'}->{'pool'}->{$pool}->{'members'};
73             }
74             }
75              
76             # Modify an object
77             sub modify {
78 0     0 1   my $self = shift;
79              
80 0           my ( $arg );
81 0           %{$arg} = @_;
  0            
82              
83 0 0 0       return 0 unless $arg->{'type'} && $arg->{'key'};
84              
85 0           my $obj = $arg->{'type'};
86 0           my $key = $arg->{'key'};
87 0           delete $arg->{'type'};
88 0           delete $arg->{'key'};
89              
90 0   0       $self->{'Parsed'} ||= $self->_parse();
91              
92 0 0         return 0 unless $self->{'Parsed'}->{$obj}->{$key};
93              
94 0           foreach my $attr ( keys %{$arg} ) {
  0            
95 0 0         next unless $self->{'Parsed'}->{$obj}->{$key}->{$attr};
96 0           $self->{'Modify'}->{$obj}->{$key}->{$attr} = $arg->{$attr};
97             }
98              
99 0           return 1;
100             }
101              
102             # Write out a new configuration file
103             sub write {
104 0     0 1   my $self = shift;
105 0   0       my $file = shift || $self->{'ConfigFile'};
106              
107 0 0         die "No changes found; no write necessary" unless $self->{'Modify'};
108              
109 0           foreach my $obj ( qw( self partition route user monitor auth profile node pool rule virtual ) ) {
110 0           foreach my $key ( sort keys %{$self->{'Parsed'}->{$obj}} ) {
  0            
111 0 0         if ( $self->{'Modify'}->{$obj}->{$key} ) {
112 0           $self->{'Output'} .= "$obj $key {\n";
113 0           foreach my $attr ( $self->_order( $obj ) ) {
114 0 0         next unless $self->{'Parsed'}->{$obj}->{$key}->{$attr};
115 0   0       $self->{'Modify'}->{$obj}->{$key}->{$attr} ||= $self->{'Parsed'}->{$obj}->{$key}->{$attr};
116 0 0         if ( ref $self->{'Modify'}->{$obj}->{$key}->{$attr} eq 'ARRAY' ) {
117 0 0         if ( @{$self->{'Modify'}->{$obj}->{$key}->{$attr}} > 1 ) {
  0            
118 0           $self->{'Output'} .= " $attr\n";
119 0           foreach my $val ( @{$self->{'Modify'}->{$obj}->{$key}->{$attr}} ) {
  0            
120 0           $self->{'Output'} .= " $val\n";
121 0 0         if ( $self->{'Parsed'}->{$obj}->{$key}->{'_xtra'}->{$val} ) {
122 0           $self->{'Output'} .= ' ' . $self->{'Parsed'}->{$obj}->{$key}->{'_xtra'}->{$val} . "\n";
123             }
124             }
125             }
126             else {
127 0           $self->{'Output'} .= " $attr " . $self->{'Modify'}->{$obj}->{$key}->{$attr}[0] . "\n";
128             }
129             }
130             else {
131 0           $self->{'Output'} .= " $attr " . $self->{'Modify'}->{$obj}->{$key}->{$attr} . "\n";
132             }
133             }
134 0           $self->{'Output'} .= "}\n";
135             }
136             else {
137 0           $self->{'Output'} .= $self->{'Raw'}->{$obj}->{$key};
138             }
139             }
140             }
141              
142 0   0       open FILE, ">$file" || return 0;
143 0           print FILE $self->{'Output'};
144 0           close FILE;
145              
146 0           return 1;
147             }
148              
149              
150              
151             # Return an object hash
152             sub _object {
153 0     0     my $self = shift;
154 0           my $obj = shift;
155 0           my $var = shift;
156              
157 0   0       $self->{'Parsed'} ||= $self->_parse();
158              
159 0   0       return $self->{'Parsed'}->{$obj}->{$var} || 0;
160             }
161              
162             # Return a list of objects
163             sub _objectlist {
164 0     0     my $self = shift;
165 0           my $obj = shift;
166              
167 0   0       $self->{'Parsed'} ||= $self->_parse();
168              
169 0 0         if ( $self->{'Parsed'}->{$obj} ) {
170 0           return keys %{$self->{'Parsed'}->{$obj}};
  0            
171             }
172             else {
173 0           return 0;
174             }
175             }
176              
177             # Define object attribute ordering
178             sub _order {
179 0     0     my $self = shift;
180              
181 0           for ( shift ) {
182 0 0         /auth/ && return qw( bind login search servers service ssl user );
183 0 0         /monitor/ && return qw( default base debug filter mandatoryattrs password security username interval timeout manual dest recv send );
184 0 0         /node/ && return qw( monitor screen );
185 0 0         /partition/ && return qw( description );
186 0 0         /pool/ && return qw( lb nat monitor members );
187 0 0         /self/ && return qw( netmask unit floating vlan allow );
188 0 0         /user/ && return qw( password description id group home shell role );
189 0 0         /virtual/ && return qw( translate snat pool destination ip rules profiles persist );
190              
191 0           return 0;
192             };
193             }
194              
195             # Parse the configuration file
196             sub _parse {
197 0     0     my $self = shift;
198 0   0       my $file = shift || $self->{'ConfigFile'};
199              
200 0 0         die "File not found: $self->{'ConfigFile'}\n" unless -e $self->{'ConfigFile'};
201              
202 0   0       open FILE, $file || return 0;
203 0           my @file = ;
204 0           close FILE;
205              
206 0           my ( $data, $parsed );
207              
208 0           until ( !$file[0] ) {
209 0           my $ln = shift @file;
210              
211 0           my ( $P );
212              
213 0 0         if ( $ln =~ /^(auth|monitor|node|partition|pool|profile|route|rule|self|user|virtual)\s+(.*)\s+{$/ ) {
214 0           $data->{'obj'} = $1;
215 0           $data->{'key'} = $2;
216             }
217              
218 0 0 0       if ( $data->{'obj'} && $data->{'key'} ) {
219 0           $self->{'Raw'}->{$data->{'obj'}}->{$data->{'key'}} .= $ln;
220              
221 0 0         if ( $ln =~ /^\s{3}(\w+)\s+(.+?)$/ ) {
222             # Patch for older-styled pool syntax
223 0 0         if ( $1 eq 'member' ) {
224 0           push @{$parsed->{$data->{'obj'}}->{$data->{'key'}}->{'members'}}, $2;
  0            
225 0   0       $self->{'ConfigVer'} ||= '9.2';
226 0           next;
227             };
228 0           $parsed->{$data->{'obj'}}->{$data->{'key'}}->{$1} = $2;
229             }
230              
231 0 0         if ( $ln =~ /^\s{3}(\w+)$/ ) { $data->{'list'} = $1; }
  0            
232              
233 0 0 0       if ( $ln =~ /^\s{6}((\w+|\d+).+?)$/ && $data->{'list'} ) {
234 1     1   7 no strict 'refs';
  1         6  
  1         51  
235 0           push @{$parsed->{$data->{'obj'}}->{$data->{'key'}}->{$data->{'list'}}}, $1;
  0            
236 1     1   6 use strict 'refs';
  1         2  
  1         211  
237              
238 0           $data->{'last'} = $1;
239             }
240              
241 0 0 0       if ( $ln =~ /^\s{9}((\w+|\d+).+?)$/ && $data->{'list'} ) {
242 0           $parsed->{$data->{'obj'}}->{$data->{'key'}}->{'_xtra'}->{$data->{'last'}} = $1;
243             }
244             }
245             }
246              
247             # Fill in ill-formatted objects
248 0           foreach my $obj ( keys %{$self->{'Raw'}} ) {
  0            
249 0           foreach my $key ( keys %{$self->{'Raw'}->{$obj}} ) {
  0            
250 0   0       $parsed->{$obj}->{$key} ||= $self->{'Raw'}->{$obj}->{$key};
251             }
252             }
253              
254 0           return $parsed;
255             }
256              
257              
258              
259             1;
260