File Coverage

blib/lib/Device/Network/ConfigParser.pm
Criterion Covered Total %
statement 32 66 48.4
branch 0 16 0.0
condition 0 25 0.0
subroutine 11 13 84.6
pod 1 1 100.0
total 44 121 36.3


line stmt bran cond sub pod time code
1             package Device::Network::ConfigParser;
2             # ABSTRACT: A harness for parsing network device configuration.
3             our $VERSION = '0.005'; # VERSION
4              
5              
6 1     1   50482 use 5.006;
  1         6  
7 1     1   8 use strict;
  1         3  
  1         41  
8 1     1   7 use warnings;
  1         2  
  1         44  
9 1     1   404 use Modern::Perl;
  1         7567  
  1         6  
10 1     1   703 use Getopt::Long;
  1         11337  
  1         8  
11 1     1   686 use Pod::Usage;
  1         41452  
  1         168  
12 1     1   410 use Perl6::Slurp;
  1         1233  
  1         7  
13 1     1   358 use Module::Load;
  1         880  
  1         6  
14 1     1   478 use Data::Dumper;
  1         6149  
  1         108  
15 1     1   26 use Scalar::Util qw{reftype};
  1         4  
  1         72  
16              
17              
18 1     1   11 use Exporter qw{import};
  1         3  
  1         569  
19              
20             our @EXPORT_OK = qw{app};
21              
22             =head1 NAME
23              
24             Device::Network::ConfigParser - harness for parsing network configurations.
25              
26             =head1 VERSION
27              
28             version 0.005
29              
30             =head1 SYNOPSIS
31              
32             Device::Network::ConfigParser is a harness for parsing network device configuration. It exports a single subroutine - C - which takes command line arguments and runs the harness. This module is used by the C command line utility. For information on how to use the command line utility, refer to the L, or following installation type C at the command line.
33              
34             The harness supports specific parsing modules by:
35              
36             =over 4
37              
38             =item * Dynamically loading a specific parsing module based on command line arguments.
39              
40             =item * Slurping in the device configuration from STDIN or from a number of files.
41              
42             =item * Opening the required output filehandles.
43              
44             =back
45              
46             =head1 CURRENT PARSER MODULES
47              
48             =over 4
49              
50             =item L
51              
52             =item L
53              
54             =item L
55              
56             =back
57              
58             =head1 DEVELOPING MODULES
59              
60             Parsing modules exist within the C namespace. For a I and I of device, the module is defined as C.
61              
62             The harness takes care of parsing the command line arguments, opening files (or STDIN/STDOUT) and slurping in their contents. It calls specified subroutines exported by the specified parsing module. All modules must export the following subroutines:
63              
64             =head2 get_parser
65              
66             my $module_parser = get_parser();
67              
68             This sub receives no arguments, and must return a reference to an object or subroutine that parses the configuration. This is most likely going to be a Parse::RecDescent object, but you're not limited to this.
69              
70             =head2 parse_config
71              
72             my $parsed_config = parse_config($module_parser, $device_config);
73              
74             This sub receives the reference returned by the C sub, and the full contents of a file specified on the command line. It should return a reference a data structure that represents the parsed configuration.
75              
76             =head2 post_process
77              
78             my $processed_config = post_process($parsed_config);
79              
80             This sub receives the reference to the data structure returned by C. It allows for some post-processing of the data structure. If no processing is required, it can be defined as C.
81              
82             =head2 get_output_drivers
83              
84             open($fh, ">>:encoding(UTF-8)", $output_filename);
85             my $output_drivers = get_output_drivers();
86              
87             $output_drivers->{csv}->($fh, $output_filename, $processed_config);
88              
89             This sub takes no arguments, and must return a HASHREF of subroutines used to output the parsed configurationm keyed on the command line argument. For example the sub may return:
90              
91             {
92             csv => \&csv_output_driver,
93             }
94              
95             The drivers themselves take a filehandle to write the output to (this may be STDOUT), the output filename, and the post-processed configuration.
96              
97             The driver called is based on the C<--output csv> as a command line argument
98              
99             There is a default 'raw' driver, which uses Data::Dumper to serialise the structure. A module may return its own 'raw' driver which will override this default.
100              
101             =head1 SUBROUTINES
102              
103             =head2 app
104              
105             The C subroutine in general takes C<@ARGV> (although it could be any list) and runs the harness.
106              
107             =cut
108              
109             sub app {
110 0     0 1   local @ARGV = @_;
111 0           my %args;
112              
113             GetOptions(
114             "vendor=s" => \$args{vendor},
115             "type=s" => \$args{type},
116             "format=s" => \$args{format},
117             "output=s" => \$args{output}
118 0 0         ) or pod2usage(2);
119              
120             # Set the defaults
121 0   0       $args{vendor} //= 'CheckPoint';
122 0   0       $args{type} //= 'Gaia';
123 0   0       $args{format} //= 'raw';
124 0   0       $args{output} //= '-'; # STDOUT
125              
126              
127             # Load the module specified by the command line parameters
128 0           my $parser_module_name = "Device::Network::ConfigParser::$args{vendor}::$args{type}";
129 0           load $parser_module_name, qw{get_parser get_output_drivers parse_config post_process};
130              
131             # Check the exports
132 0 0 0       if (!defined &get_parser ||
      0        
      0        
133             !defined &get_output_drivers ||
134             !defined &parse_config ||
135             !defined &post_process) {
136 0           die "$parser_module_name does not export all required subroutines\n";
137             }
138              
139             # Retrieve the parser and the output drivers from the module. If 'raw' doesn't exist,
140             # it's populated with the default sub from this package.
141             # The active driver is then selected for use.
142 0           my $parser = get_parser();
143 0           my $output_drivers = get_output_drivers();
144 0   0       $output_drivers->{raw} //= \&_default_raw_output_driver;
145 0           my $active_output_driver = $output_drivers->{ $args{format} };
146              
147 0 0 0       if (!defined $active_output_driver || reftype $active_output_driver ne 'CODE' ) {
148 0           die "'$args{format}' is not a valid output driver for $parser_module_name\n";
149             }
150              
151             # If there are no files specified, we slurp from STDIN
152 0 0         push @ARGV, \*STDIN if !@ARGV;
153              
154 0           for my $config_filename (@ARGV) {
155             # Read in the configuration
156 0           my $raw_config = slurp $config_filename;
157              
158             # Change the name to something sensible so it isn't stringified to something like 'GLOB(0x17c7350)'
159 0 0 0       $config_filename = 'STDIN' if reftype($config_filename) && reftype($config_filename) eq 'GLOB';
160              
161             # Call the parser imported from the module.
162 0           my $parsed_config = parse_config($parser, $raw_config);
163              
164             # Perform any post-processing.
165 0           my $post_processed_config = post_process($parsed_config);
166              
167             # Open the file, which could be STDOUT
168 0           my $fh;
169 0 0         if ($args{output} eq '-') {
170 0           local *STDOUT;
171 0           $fh = \*STDOUT;
172             } else {
173             # Replace the placeholder with the filename
174 0           my ($outfile) = $args{output} =~ s{%in_file%}{$config_filename}rxms;
175 0 0         open($fh, ">>:encoding(UTF-8)", $outfile) or die "Unable to open output file '$outfile': $!\n";
176             }
177              
178             # Call the output driver.
179 0           $active_output_driver->($fh, $config_filename, $post_processed_config);
180              
181 0 0         close($fh) unless $args{output} eq '-';
182             }
183              
184 0           return 0;
185             }
186              
187              
188             sub _default_raw_output_driver {
189 0     0     my ($fh, $filename, $parsed_config) = @_;
190 0           print $fh Dumper($parsed_config);
191             }
192              
193             =head1 AUTHOR
194              
195             Greg Foletta, C<< >>
196              
197             =head1 BUGS
198              
199             Please report any bugs or feature requests to C, or through
200             the web interface at L. I will be notified, and then you'll
201             automatically be notified of progress on your bug as I make changes.
202              
203              
204              
205              
206             =head1 SUPPORT
207              
208             You can find documentation for this module with the perldoc command.
209              
210             perldoc Device::Network::ConfigParser
211              
212              
213             You can also look for information at:
214              
215             =over 4
216              
217             =item * RT: CPAN's request tracker (report bugs here)
218              
219             L
220              
221             =item * AnnoCPAN: Annotated CPAN documentation
222              
223             L
224              
225             =item * CPAN Ratings
226              
227             L
228              
229             =item * Search CPAN
230              
231             L
232              
233             =back
234              
235              
236             =head1 ACKNOWLEDGEMENTS
237              
238              
239             =head1 LICENSE AND COPYRIGHT
240              
241             Copyright 2017 Greg Foletta.
242              
243             This program is free software; you can redistribute it and/or modify it
244             under the terms of either: the GNU General Public License as published
245             by the Free Software Foundation; or the Artistic License.
246              
247             See L for more information.
248              
249              
250             =cut
251              
252             1; # End of Device::Network::ConfigParser