File Coverage

blib/lib/BigIP/GTM/ParseConfig.pm
Criterion Covered Total %
statement 11 244 4.5
branch 0 114 0.0
condition 0 54 0.0
subroutine 4 44 9.0
pod 14 36 38.8
total 29 492 5.8


line stmt bran cond sub pod time code
1             package BigIP::GTM::ParseConfig;
2              
3             # CURRENTLY UNDER DEVELOMENT BY WENWU YAN < careline@126.com >
4             #----------------------------------------------------------------------------
5             # The contents of this file are subject to the iControl Public License
6             # Version 4.5 (the "License"); you may not use this file except in
7             # compliance with the License. You may obtain a copy of the License at
8             # http://www.f5.com/.
9             #
10             # Software distributed under the License is distributed on an "AS IS"
11             # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
12             # the License for the specific language governing rights and limitations
13             # under the License.
14             #
15             # The Original Code is iControl Code and related documentation
16             # distributed by F5.
17             #
18             # The Initial Developer of the Original Code is F5 Networks,
19             # Inc. Seattle, WA, USA. Portions created by F5 are Copyright (C) 1996-2019 F5 Networks,
20             # Inc. All Rights Reserved. iControl (TM) is a registered trademark of F5 Networks, Inc.
21             #
22             # Alternatively, the contents of this file may be used under the terms
23             # of the GNU General Public License (the "GPL"), in which case the
24             # provisions of GPL are applicable instead of those above. If you wish
25             # to allow use of your version of this file only under the terms of the
26             # GPL and not to allow others to use your version of this file under the
27             # License, indicate your decision by deleting the provisions above and
28             # replace them with the notice and other provisions required by the GPL.
29             # If you do not delete the provisions above, a recipient may use your
30             # version of this file under either the License or the GPL.
31             #----------------------------------------------------------------------------
32              
33             our $VERSION = '0.8.2';
34             my $AUTOLOAD;
35              
36 1     1   65640 use 5.012;
  1         3  
37 1     1   5 use Carp;
  1         2  
  1         67  
38 1     1   7 use warnings;
  1         2  
  1         40  
39 1     1   627 use Data::Dumper;
  1         6703  
  1         3329  
40              
41             # Initialize the module
42             sub new {
43 0     0 1   my $class = shift;
44              
45 0           my $self = {};
46 0           bless $self, $class;
47              
48 0           $self->{'ConfigFile'} = shift;
49              
50 0           return $self;
51             }
52              
53             # Return a list of objects
54 0     0 0   sub regions { return shift->_objectlist('gtm region'); }
55 0     0 0   sub wideips { return shift->_objectlist('gtm wideip'); }
56 0     0 1   sub pools { return shift->_objectlist('gtm pool'); }
57 0     0 0   sub servers { return shift->_objectlist('gtm server'); }
58 0     0 1   sub monitors { return shift->_objectlist('gtm monitor'); }
59 0     0 1   sub partitions { return shift->_objectlist('partition'); }
60 0     0 1   sub routes { return shift->_objectlist('net route'); }
61 0     0 0   sub selfs { return shift->_objectlist('net self'); }
62 0     0 0   sub vlans { return shift->_objectlist('net vlan'); }
63 0     0 0   sub trunks { return shift->_objectlist('net trunk'); }
64 0     0 0   sub interfaces { return shift->_objectlist('net interface'); }
65 0     0 0   sub mgmt_routes { return shift->_objectlist('sys management-route'); }
66 0     0 1   sub users { return shift->_objectlist('auth user'); }
67              
68             # Return an object hash
69 0     0 0   sub region { return shift->_object( 'gtm region', shift ); }
70 0     0 0   sub wideip { return shift->_object( 'gtm wideip', shift ); }
71 0     0 1   sub pool { return shift->_object( 'gtm pool', shift ); }
72 0     0 0   sub server { return shift->_object( 'gtm server', shift ); }
73 0     0 1   sub monitor { return shift->_object( 'gtm monitor', shift ); }
74 0     0 1   sub partition { return shift->_object( 'partition', shift ); }
75 0     0 0   sub self { return shift->_object( 'net self', shift ); }
76 0     0 1   sub route { return shift->_object( 'net route', shift ); }
77 0     0 0   sub vlan { return shift->_object( 'net vlan', shift ); }
78 0     0 0   sub trunk { return shift->_object( 'net trunk', shift ); }
79 0     0 0   sub interface { return shift->_object( 'net interface', shift ); }
80 0     0 0   sub mgmt_route { return shift->_object( 'sys management-route', shift ); }
81 0     0 0   sub snmp { return shift->_object( 'sys', 'snmp' ); }
82 0     0 0   sub sshd { return shift->_object( 'sys', 'sshd' ); }
83 0     0 0   sub ntp { return shift->_object( 'sys', 'ntp' ); }
84 0     0 0   sub syslog { return shift->_object( 'sys', 'syslog' ); }
85 0     0 1   sub user { return shift->_object( 'auth user', shift ); }
86              
87             #return a list of funcs
88              
89             sub funcs {
90 0     0 0   my $self = shift;
91              
92 0   0       $self->{'Parsed'} ||= $self->_parse();
93              
94 0   0       return keys %{ $self->{'Parsed'} } || 0;
95             }
96              
97             # Return a list of pool members
98             sub members {
99 0     0 1   my $self = shift;
100 0           my $pool = shift;
101              
102 0           my $members;
103 0   0       $self->{'Parsed'} ||= $self->_parse();
104              
105 0 0         return 0 unless $self->{'Parsed'}->{'gtm pool'}->{$pool}->{'members'};
106              
107 0 0         if ( ref $self->{'Parsed'}->{'gtm pool'}->{$pool}->{'members'} eq 'HASH' )
108             {
109             return
110 0           map {s/\:/\//r}
111             (
112 0           keys %{ $self->{'Parsed'}->{'gtm pool'}->{$pool}->{'members'} } );
  0            
113             }
114             else {
115 0           return $self->{'Parsed'}->{'gtm pool'}->{$pool}->{'members'};
116             }
117             }
118              
119             #Return a list of VS
120             sub wideips_all {
121 0     0 0   my $self = shift;
122 0   0       $self->{'Parsed'} ||= $self->_parse();
123              
124 0 0         return 0 unless $self->{'Parsed'}->{'gtm wideip'};
125              
126             # Loop for wideips()
127 0           foreach ( $self->wideips() ) {
128 0           my $pools = $self->wideip($_)->{pools};
129              
130 0           foreach my $pool ( keys %{$pools} ) {
  0            
131             my $members = $self->pool($pool)->{members}
132 0 0         if defined $self->pool($pool);
133              
134 0           $self->wideip($_)->{"pools_members"} = $members;
135              
136 0           foreach my $serverAndVs ( keys %{$members} ) {
  0            
137 0           my ( $server, $vs ) = split( /\:/, $serverAndVs );
138 0           my $details = $self->server($server);
139              
140 0 0         $self->wideip($_)->{"servers_details"}{$serverAndVs} = $server
141             if $details;
142             $self->wideip($_)->{"vs_details"}{$serverAndVs}
143 0 0         = $details->{"virtual-servers"}{$vs}
144             if $vs;
145             }
146             }
147             }
148              
149 0           return $self->{Parsed}{"gtm wideip"};
150             }
151              
152             # Modify an object
153             sub modify {
154 0     0 1   my $self = shift;
155              
156 0           my ($arg);
157 0           %{$arg} = @_;
  0            
158              
159 0 0 0       return 0 unless $arg->{'type'} && $arg->{'key'};
160              
161 0           my $obj = $arg->{'type'};
162 0           my $key = $arg->{'key'};
163 0           delete $arg->{'type'};
164 0           delete $arg->{'key'};
165              
166 0   0       $self->{'Parsed'} ||= $self->_parse();
167              
168 0 0         return 0 unless $self->{'Parsed'}->{$obj}->{$key};
169              
170 0           foreach my $attr ( keys %{$arg} ) {
  0            
171 0 0         next unless $self->{'Parsed'}->{$obj}->{$key}->{$attr};
172 0           $self->{'Modify'}->{$obj}->{$key}->{$attr} = $arg->{$attr};
173             }
174              
175 0           return 1;
176             }
177              
178             # Write out a new configuration file
179             sub write {
180 0     0 1   my $self = shift;
181 0   0       my $file = shift || $self->{'ConfigFile'};
182              
183 0 0         die "No changes found; no write necessary" unless $self->{'Modify'};
184              
185 0           foreach my $obj (
186             qw( self partition route user monitor auth profile node pool rule virtual )
187             )
188             {
189 0           foreach my $key ( sort keys %{ $self->{'Parsed'}->{$obj} } ) {
  0            
190 0 0         if ( $self->{'Modify'}->{$obj}->{$key} ) {
191 0           $self->{'Output'} .= "$obj $key {\n";
192 0           foreach my $attr ( $self->_order($obj) ) {
193 0 0         next unless $self->{'Parsed'}->{$obj}->{$key}->{$attr};
194             $self->{'Modify'}->{$obj}->{$key}->{$attr}
195 0   0       ||= $self->{'Parsed'}->{$obj}->{$key}->{$attr};
196 0 0         if (ref $self->{'Modify'}->{$obj}->{$key}->{$attr} eq
197             'ARRAY' )
198             {
199 0 0         if ( @{ $self->{'Modify'}->{$obj}->{$key}->{$attr} }
  0            
200             > 1 )
201             {
202 0           $self->{'Output'} .= " $attr\n";
203 0           foreach my $val (
204 0           @{ $self->{'Modify'}->{$obj}->{$key}->{$attr}
205             }
206             )
207             {
208 0           $self->{'Output'} .= " $val\n";
209 0 0         if ( $self->{'Parsed'}->{$obj}->{$key}
210             ->{'_xtra'}->{$val} )
211             {
212             $self->{'Output'}
213             .= ' '
214             . $self->{'Parsed'}->{$obj}->{$key}
215 0           ->{'_xtra'}->{$val} . "\n";
216             }
217             }
218             }
219             else {
220             $self->{'Output'}
221             .= " $attr "
222             . $self->{'Modify'}->{$obj}->{$key}
223 0           ->{$attr}[0] . "\n";
224             }
225             }
226             else {
227             $self->{'Output'}
228             .= " $attr "
229 0           . $self->{'Modify'}->{$obj}->{$key}->{$attr}
230             . "\n";
231             }
232             }
233 0           $self->{'Output'} .= "}\n";
234             }
235             else {
236 0           $self->{'Output'} .= $self->{'Raw'}->{$obj}->{$key};
237             }
238             }
239             }
240              
241 0   0       open FILE, ">$file" || return 0;
242 0           print FILE $self->{'Output'};
243 0           close FILE;
244              
245 0           return 1;
246             }
247              
248             # Return an object hash
249             sub _object {
250 0     0     my $self = shift;
251 0           my $obj = shift;
252 0           my $var = shift;
253              
254 0   0       $self->{'Parsed'} ||= $self->_parse();
255 0 0         return undef unless defined $var;
256 0   0       return $self->{'Parsed'}->{$obj}->{$var} || undef;
257             }
258              
259             # Return a list of objects
260             sub _objectlist {
261 0     0     my $self = shift;
262 0           my $obj = shift;
263              
264 0   0       $self->{'Parsed'} ||= $self->_parse();
265              
266 0 0         if ( $self->{'Parsed'}->{$obj} ) {
267 0           return keys %{ $self->{'Parsed'}->{$obj} };
  0            
268             }
269             else {
270 0           return 0;
271             }
272             }
273              
274             # Define object attribute ordering
275             sub _order {
276 0     0     my $self = shift;
277              
278 0           for (shift) {
279 0 0         /auth/ && return qw( bind login search servers service ssl user );
280 0 0         /gtm monitor/
281             && return
282             qw( default base debug filter mandatoryattrs password security username interval timeout manual dest recv send );
283 0 0         /gtm node/ && return qw( monitor screen );
284 0 0         /gtm pool/ && return qw( lb nat monitor members );
285 0 0         /partition/ && return qw( description );
286 0 0         /net self/ && return qw( netmask unit floating vlan allow );
287 0 0         /auth user/
288             && return qw( password description id group home shell role );
289 0 0         /gtm server/
290             && return
291             qw( translate snat pool destination ip rules profiles persist );
292              
293 0           return 0;
294             }
295             }
296              
297             # Parse the configuration file
298             sub _parse {
299 0     0     my $self = shift;
300 0   0       my $file = shift || $self->{'ConfigFile'};
301              
302             die "File not found: $self->{'ConfigFile'}\n"
303 0 0         unless -e $self->{'ConfigFile'};
304              
305 0   0       open FILE, $file || return 0;
306 0           my @file = ;
307 0           close FILE;
308              
309 0           my ( $parsed, $obj, $key, $attr, $attr1, $attr2 );
310              
311 0           until ( !$file[0] ) {
312 0           my $ln = shift @file;
313              
314             #policy hit situation with gtm attribute
315 0 0         if ( $ln =~ /^(auth user|patition|cli)\s(.*)\s\{(\s\})?$/ ) {
    0          
    0          
    0          
    0          
    0          
316 0           $obj = $1;
317 0           $key = $2;
318 0 0         $attr = undef if $3;
319             }
320              
321             #gtm attribute
322             elsif ( $ln
323             =~ /^(gtm wideip|gtm pool|gtm server|gtm monitor|gtm region)\s(.*)\s\{(\s\})?$/
324             )
325             {
326 0           $obj = $1;
327 0           $key = $2;
328 0 0         $attr = undef if $3;
329             }
330              
331             #net attribute
332             elsif ( $ln
333             =~ /^(net self|net route|net interface|net vlan|net trunk)\s(.*)\s\{(\s\})?$/
334             )
335             {
336 0           $obj = $1;
337 0           $key = $2;
338 0 0         $attr = undef if $3;
339             }
340              
341             #sys attribute
342             elsif ( $ln =~ /^(sys management-route)\s(.*)\s\{(\s\})?$/ ) {
343 0           $obj = $1;
344 0           $key = $2;
345 0 0         $attr = undef if $3;
346             }
347              
348             #sys management attribute
349             elsif ( $ln
350             =~ /^(sys)\s(snmp|sshd|ntp|syslog|state-mirroring)\s\{(\s\})?$/ )
351             {
352 0           $obj = $1;
353 0           $key = $2;
354 0 0         $attr = undef if $3;
355             }
356             elsif ( $ln =~ /^\}$/ ) {
357 0           $obj = undef;
358 0           $key = undef;
359             }
360              
361             # mungle data structure
362 0 0 0       if ( $obj && $key ) {
363 0           $self->{'Raw'}->{$obj}{$key} .= $ln;
364              
365             #Indent=4 { not empty }
366 0 0         if ( $ln =~ /^\s{4}(\S+)\s\{$/ ) {
367 0           $attr1 = $1;
368 0           next;
369             }
370              
371             #$intdent=8 { not empty }
372 0 0         if ( $ln =~ /^\s{8}(\S+)\s\{$/ ) {
373 0           $attr2 = $1;
374 0           next;
375             }
376              
377             #Indent=4 with }$
378 0 0         if ( $ln =~ /^\s{4}\}$/ ) {
379 0           $attr1 = undef;
380 0           next;
381             }
382              
383             #Indent=8 with }$
384 0 0         if ( $ln =~ /^\s{8}\}$/ ) {
385 0           $attr2 = undef;
386 0           next;
387             }
388              
389             #Indent=4 {}
390 0 0         if ( $ln =~ /^\s{4}(\S+)\s\{\s\}$/ ) {
391 0           $parsed->{$obj}{$key}{$1} = undef;
392 0           next;
393             }
394              
395             #Indent=4 { scalar }
396 0 0         if ( $ln =~ /^\s{4}(\S+)\s\{(.*)\}$/ ) {
397 0   0       $parsed->{$obj}{$key}{$1} ||= [];
398 0           push @{ $parsed->{$obj}{$key}{$1} },
399 0           grep { not /^\s*$/ } split( /\s+/, $2 );
  0            
400 0           next;
401             }
402              
403 0 0         if ($attr1) {
404              
405             #Indent=8 {}
406 0 0         if ( $ln =~ /^\s{8}(\S+)\s\{\s\}$/ ) {
407 0           $parsed->{$obj}{$key}{$attr1}{$1} = undef;
408 0           next;
409             }
410              
411             #Indent=8 { scalar }
412 0 0         if ( $ln =~ /^\s{8}(\S+)\s\{(.*)\}$/ ) {
413 0   0       $parsed->{$obj}{$key}{$1} ||= [];
414 0           push @{ $parsed->{$obj}{$key}{$1} },
415 0           grep { not /^\s*$/ } split( /\s+/, $2 );
  0            
416 0           next;
417             }
418              
419             #Indent=8 match { key => value }
420 0 0         if ( $ln =~ /^\s{8}(\S+)\s(.*)$/ ) {
421 0           $parsed->{$obj}{$key}{$attr1}{$1} = $2;
422 0           next;
423             }
424              
425             #Indent=8 match scalar
426 0 0         if ( $ln =~ /^\s{8}(\S.+)$/ ) {
427 0 0         if ( ref( $parsed->{$obj}{$key}{$attr1} ) eq 'HASH' ) {
428 0           $parsed->{$obj}{$key}{$attr1}{$1} = undef;
429             }
430             else {
431 0   0       $parsed->{$obj}{$key}{$attr1} ||= [];
432 0           push @{ $parsed->{$obj}{$key}{$attr1} }, $1;
  0            
433             }
434 0           next;
435             }
436             }
437              
438 0 0         if ($attr2) {
439              
440             #Indent=12 match { not empty }
441 0 0         if ( $ln =~ /^\s{12}(\S+)\s\{\s\}$/ ) {
442 0           $parsed->{$obj}{$key}{$attr1}{$attr2}{$1} = undef;
443 0           next;
444             }
445              
446             #Indent=12 { scalar }
447 0 0         if ( $ln =~ /^\s{12}(\S+)\s\{(.*)\}$/ ) {
448 0   0       $parsed->{$obj}{$key}{$attr1} ||= [];
449 0           push @{ $parsed->{$obj}{$key}{$attr1}{$attr2}{$1} },
450 0           grep { not /^\s*$/ } split( /\s+/, $2 );
  0            
451 0           next;
452             }
453              
454             #Indent=12 match { key => value }
455 0 0         if ( $ln =~ /^\s{12}(\S+)\s(.*)\s?$/ ) {
456 0           say( $ln, $attr1, $attr2 );
457 0           $parsed->{$obj}->{$key}{$attr1}{$attr2}{$1} = $2;
458 0           next;
459             }
460              
461             #Indent=12 match scalar
462 0 0         if ( $ln =~ /^\s{12}(.*)\s?$/ ) {
463 0 0         if ( ref( $parsed->{$obj}{$key}{$attr1} ) eq 'HASH' ) {
464 0           $parsed->{$obj}{$key}{$attr1}{$1} = undef;
465             }
466             else {
467 0   0       $parsed->{$obj}{$key}{$attr1} ||= [];
468 0           push @{ $parsed->{$obj}{$key}{$attr1} }, $1;
  0            
469             }
470 0           next;
471             }
472             }
473              
474             #Indent=4 match { key => value }
475 0 0         if ( $ln =~ /^\s{4}(\S+)\s(.*)$/ ) {
476 0           $parsed->{$obj}{$key}{$1} = $2;
477 0           next;
478             }
479             else {
480 0           say $ln;
481             }
482             }
483             }
484              
485             # Fill in ill-formatted objects
486 0           foreach my $obj ( keys %{ $self->{'Raw'} } ) {
  0            
487 0           foreach my $key ( keys %{ $self->{'Raw'}->{$obj} } ) {
  0            
488 0   0       $parsed->{$obj}{$key} ||= $self->{'Raw'}->{$obj}{$key};
489             }
490             }
491              
492 0           return $parsed;
493             }
494              
495             1;
496              
497             __END__