File Coverage

blib/lib/BGPmon/Translator/XFB2PerlHash.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package BGPmon::Translator::XFB2PerlHash;
2              
3 5     5   1099 use 5.14.0;
  5         20  
  5         239  
4 5     5   27 use strict;
  5         20  
  5         181  
5 5     5   60 use warnings;
  5         10  
  5         166  
6 5     5   17575 use XML::LibXML::Simple;
  0            
  0            
7             use Data::Dumper;
8              
9             BEGIN{
10             require Exporter;
11             our $AUTOLOAD;
12             our @ISA = qw(Exporter);
13             our @EXPORT_OK = qw(translate_msg toString get_content reset
14             get_error_code get_error_message get_error_msg);
15             our $VERSION = '2.0';
16             }
17              
18             #Variable to hold both the original as well as the converted XML
19             my $raw_xml = '';
20             my $xml_hashref = {};
21              
22             #Variables to hold error codes and messages
23             my %error_code;
24             my %error_msg;
25             my @function_names = ('translate_msg', 'toString', 'get_content');
26              
27             use constant NO_ERROR_CODE => 0;
28             use constant NO_ERROR_MSG => 'No Error. Life is good.';
29             use constant NO_MESSAGE_CODE => 601;
30             use constant NO_MESSAGE_MSG => 'No XML message provided';
31             use constant UNDEFINED_ARGUMENT_CODE => 602;
32             use constant UNDEFINED_ARGUMENT_MSG => 'Undefined argument';
33             use constant INVALID_FUNCTION_SPECIFIED_CODE => 603;
34             use constant INVALID_FUNCTION_SPECIFIED_MSG =>
35             'Invalid Function Name Specified';
36             use constant PARSER_ERROR_CODE => 604;
37             use constant PARSER_ERROR_MSG => 'XML Parser Error';
38             use constant NO_SUCH_INFORMATION_CODE => 605;
39             use constant NO_SUCH_INFORMATION_MSG => 'No such element/attribute exists';
40              
41             for my $function_name (@function_names) {
42             $error_code{$function_name} = NO_ERROR_CODE;
43             $error_msg{$function_name} = NO_ERROR_MSG;
44             }
45              
46             =head1 NAME
47              
48             BGPmon::Translator::XFB2PerlHash - convert an XFB message into a Perl hash
49              
50             This module converts an XML message to a nested hash data structure
51             and provides an interface to get a stringified representation of
52             the data structure as well as the ability to extract individual subtrees
53             from the nested structure.
54              
55             =head1 SYNOPSIS
56              
57             use BGPmon::Translator::XFB2PerlHash;
58              
59             my $xml_string = '...
60              
61              
62             #Convering and soring the xml message
63              
64             my %hash = translate_msg($xml_string);
65              
66              
67             #printing the data in an easier-to-read way
68              
69             print toString();
70              
71              
72             #printing the port number of the peer that passed us this message
73              
74             my $result = get_content('/BGP_MONITOR_MESSAGE/SOURCE/PORT/content');
75              
76             print $result;
77              
78              
79             #Printing all the prefixes found in the NLRI section
80              
81             $result = get_content('/BGP_MONITOR_MESSAGE/bgp:UPDATE/bgp:NLRI/');
82              
83             print $_->{'ADDRESS'}->{'content'} foreach (@$result);
84              
85              
86             #Resetting the module
87              
88             reset();
89              
90             =head1 EXPORT
91              
92             translate_msg
93             toString
94             get_content
95             reset
96             get_error_code
97             get_error_msg
98             get_error_message
99              
100             =head1 SUBROUTINES/METHODS
101              
102             =head2 translate_msg
103              
104             Converts an XML message into a Perl hash structure while maintaining the
105             structure of the message itself.
106              
107             Input: The XML string to be parsed
108              
109             Output: A perl hash structure that contains the converted string
110             or an empty hash if there is no string provided or the
111             XML parser fails
112              
113             =cut
114              
115             sub translate_msg{
116             my $xml_msg = shift;
117             my $fname = 'translate_msg';
118             if( !defined($xml_msg) ){
119             $error_code{$fname} = NO_MESSAGE_CODE;
120             $error_msg{$fname} = NO_MESSAGE_MSG;
121             return {};
122             }
123             #Reset the state variables
124             %$xml_hashref = ();
125             $raw_xml = '';
126             #Store the XML message
127             $raw_xml = $xml_msg;
128             #Instantiates a new LibXML::Simple object
129             my $xml = new XML::LibXML::Simple;
130             #XMLin converts the XML to a nested hash
131             #the ForceArray option forces the listed tags to be represented
132             #as arrays so that the user can iterate through them
133             my $data = ();
134             eval{
135             $data = $xml->XMLin("$xml_msg",ForceArray =>
136             ['PREFIX','ATTRIBUTE','AS_SEG','AS'], KeepRoot => 1 ,
137             ForceContent => 1);
138             $data->{'raw'} = $raw_xml; #Saves the raw XML in the hash structure
139             $xml_hashref = $data;
140             return $xml_hashref;
141             } or do {
142             $error_code{$fname} = PARSER_ERROR_CODE;
143             $error_msg{$fname} = PARSER_ERROR_MSG . ": $?";
144             return {};
145             };
146             }
147              
148             =head2 toString
149              
150             Returns a printable version of the most recent XML message that was parsed with
151             translate_msg. If there is no such message, returns the empty string.
152              
153             =cut
154             sub toString{
155             my $fname = 'toString';
156             if( !keys %$xml_hashref ){
157             $error_code{$fname} = NO_MESSAGE_CODE;
158             $error_msg{$fname} = NO_MESSAGE_MSG;
159             return '';
160             }
161             return Dumper($xml_hashref);
162             }
163              
164             =head2 get_content
165              
166             Returns a reference to an element or attribute of the most recent XML message
167             translated via translate_msg.
168              
169             Input: A slash-delimited string which gives the path through
170             the message tree structure, i.e.
171             "/ROOT_TAG/NEXT_TAG/attribute_name"
172             NOTE: To get the text contents of an element, specify "/content"
173             as the final "node" in the target string.
174              
175             Output: A reference to the appropriate content if found.
176             undef if no such information is found
177              
178             =cut
179             sub get_content{
180             my $target_loc = shift;
181             my $fname = 'get_content';
182              
183             if( !keys %$xml_hashref ){
184             $error_code{$fname} = NO_MESSAGE_CODE;
185             $error_msg{$fname} = NO_MESSAGE_MSG;
186             return undef;
187             }
188              
189             if( !defined($target_loc) ){
190             $error_code{$fname} = UNDEFINED_ARGUMENT_CODE;
191             $error_msg{$fname} = UNDEFINED_ARGUMENT_MSG;
192             return undef;
193             }
194             #Extract the node names from the input by splitting on forward slashes
195             my @path = split "/",$target_loc;
196             shift @path; #Removes the leading blank space from path
197             #Now initialize a new hash reference to use to iteratively step
198             #through the XML hash
199             my $new_hashref = $xml_hashref;
200             ELEMENT: foreach my $el (@path){
201             #If we encounter an array in the hash structure
202             #we need to go through it and see if any element
203             #has the next element in the chain in it.
204             if( ref $new_hashref eq 'ARRAY' ){
205             foreach my $attr (@$new_hashref){
206             #If the next element is found, we can move the hashref
207             if( exists $attr->{"$el"} ){
208             $new_hashref = $attr;
209             last;
210             }
211             }
212             }
213             #Otherwise we try to dereference the next element in the chain.
214             #If the array runs out or we give a hash element that isn't there,
215             #catch it, set the error, and return.
216             eval{
217             $new_hashref = $new_hashref->{"$el"};
218             1;
219             } or do {
220             $error_code{$fname} = NO_SUCH_INFORMATION_CODE;
221             $error_msg{$fname} = NO_SUCH_INFORMATION_MSG;
222             return undef;
223             };
224             }
225             if( defined $new_hashref ){
226             $error_code{$fname} = NO_ERROR_CODE;
227             $error_msg{$fname} = NO_ERROR_MSG;
228             return $new_hashref;
229             }
230             else{
231             $error_code{$fname} = NO_SUCH_INFORMATION_CODE;
232             $error_msg{$fname} = NO_SUCH_INFORMATION_MSG;
233             return undef;
234             }
235             }
236              
237             =head2 reset
238              
239             Resets the module's state variables
240              
241             =cut
242              
243             sub reset{
244             $raw_xml = '';
245             %$xml_hashref = ();
246             for my $function_name (@function_names) {
247             $error_code{$function_name} = NO_ERROR_CODE;
248             $error_msg{$function_name} = NO_ERROR_MSG;
249             }
250             return;
251             }
252              
253             =head2 get_error_code
254              
255             Get the error code for some function
256              
257             Input : the name of the function whose error code we should report
258              
259             Output: the function's error code
260             or UNDEFINED_ARGUMENT if the user did not supply a function
261             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid function name
262             =cut
263              
264             sub get_error_code {
265             my $function = shift;
266              
267             # check we got a function name
268             if (!defined($function)) {
269             return UNDEFINED_ARGUMENT_CODE;
270             }
271              
272             return $error_code{$function} if defined $error_code{$function};
273             return INVALID_FUNCTION_SPECIFIED_CODE;
274             }
275              
276             =head2 get_error_message
277              
278             Get the error message for some function
279              
280             Input : the name of the function whose error message we should report
281              
282             Output: the function's error message
283             or UNDEFINED_ARGUMENT if the user did not supply a function
284             or INVALID_FUNCTION_SPECIFIED if the user provided an invalid function name
285             =cut
286              
287             sub get_error_message {
288             my $function = shift;
289              
290             # check we got a function name
291             if (!defined($function)) {
292             return UNDEFINED_ARGUMENT_MSG;
293             }
294              
295             return $error_msg{$function} if defined($error_msg{$function});
296             return INVALID_FUNCTION_SPECIFIED_MSG.": $function";
297             }
298              
299             =head2 get_error_msg
300              
301             Get the error message
302              
303             This function is identical to get_error_message
304             =cut
305             sub get_error_msg {
306             my $msg = shift;
307             return get_error_message($msg);
308             }
309              
310             =head1 ERROR CODES AND MESSAGES
311              
312             The following error codes and messages are defined:
313              
314             0: No Error
315             'No Error. Life is good.'
316              
317             601: There has been no XML message passed through translate_msg
318             'No XML message provided'
319              
320             602: No argument was passed to a function expecting one
321             'Undefined argument'
322              
323             603: An invalid function name was passed to get_error_[code/message/msg]
324             'Invalid Function Name Specified'
325              
326             604: The XML parser failed
327             'XML Parser Error'
328              
329             605: There was no information found at the location passed to
330             get_content
331             'No such element/attribute exists'
332              
333              
334             =head1 BUGS
335              
336             Please report any bugs or feature requests to
337             C, or through
338             the web interface at L.
339              
340              
341             =head1 SUPPORT
342              
343             You can find documentation for this module with the perldoc command.
344              
345             perldoc BGPmon::Translator::XFB2PerlHash
346              
347             =cut
348              
349             =head1 LICENSE AND COPYRIGHT
350              
351             Copyright (c) 2012 Colorado State University
352              
353             Permission is hereby granted, free of charge, to any person
354             obtaining a copy of this software and associated documentation
355             files (the "Software"), to deal in the Software without
356             restriction, including without limitation the rights to use,
357             copy, modify, merge, publish, distribute, sublicense, and/or
358             sell copies of the Software, and to permit persons to whom
359             the Software is furnished to do so, subject to the following
360             conditions:
361              
362             The above copyright notice and this permission notice shall be
363             included in all copies or substantial portions of the Software.
364              
365             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
366             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
367             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
368             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
369             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
370             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
371             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
372             OTHER DEALINGS IN THE SOFTWARE.\
373              
374             File: XFB2PerlHash.pm
375              
376             Authors: M. Lawrence Weikum, Jason Bartlett, Kaustubh Gadkari, Dan Massey, Cathie Olschanowsky
377             Date: 13 October 2013
378             =cut
379              
380             1; # End of BGPmon::Translator::XFB2PerlHash