File Coverage

blib/lib/Siebel/Srvrmgr/ListParser.pm
Criterion Covered Total %
statement 134 142 94.3
branch 31 44 70.4
condition 2 6 33.3
subroutine 20 20 100.0
pod 9 9 100.0
total 196 221 88.6


line stmt bran cond sub pod time code
1             package Siebel::Srvrmgr::ListParser;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Siebel::Srvrmgr::ListParser - state model parser to idenfity which output type was read
8              
9             =head1 SYNOPSIS
10              
11             use Siebel::Srvrmgr::ListParser;
12              
13             my $parser = Siebel::Srvrmgr::ListParser->new({ prompt_regex => $some_prompt });
14              
15             =cut
16              
17 21     21   1836272 use Moose;
  21         6938202  
  21         149  
18 21     21   164804 use Siebel::Srvrmgr::ListParser::OutputFactory;
  21         85  
  21         980  
19 21     21   12822 use Siebel::Srvrmgr::ListParser::Buffer;
  21         279578  
  21         964  
20 21     21   12674 use Siebel::Srvrmgr;
  21         147  
  21         871  
21 21     21   15656 use Siebel::Srvrmgr::ListParser::FSA;
  21         69  
  21         670  
22 21     21   176 use namespace::autoclean;
  21         36  
  21         226  
23 21     21   1982 use Carp;
  21         38  
  21         1414  
24 21     21   12229 use Siebel::Srvrmgr::Types;
  21         58  
  21         838  
25 21     21   27807 use String::BOM qw(string_has_bom strip_bom_from_string);
  21         387  
  21         125  
26              
27             =pod
28              
29             =head1 DESCRIPTION
30              
31             Siebel::Srvrmgr::ListParser is a state machine parser created to parse output of "list" commands executed through C<srvrmgr> program.
32              
33             The parser can identify different types of commands and their outputs from a buffer given as parameter to the module. For each
34             type of output identified an L<Siebel::Srvrmgr::ListParser::Buffer> object will be created, identifying which type of command
35             was executed and the raw information from it.
36              
37             At the end of information read from the buffer, this class will call L<Siebel::Srvrmgr::ListParser::OutputFactory> to create
38             specific L<Siebel::Srvrmgr::ListParser::Output> objects based on the identified type of Buffer object. Each of this objects will
39             parse the raw output and populate attributes based on this information. After this is easier to obtain the information from
40             those subclasses of L<Siebel::Srvrmgr::ListParser::Output>.
41              
42             Siebel::Srvrmgr::ListParser expects to receive output from C<srvrmgr> program in an specific format and is able to idenfity a
43             limited number of commands and their outputs, raising an exception when those types cannot be identified. See subclasses
44             of L<Siebel::Srvrmgr::ListParser::Output> to see which classes/types are available.
45              
46             Logging of this class can be enabled by using L<Siebel::Srvrmgr> logging feature.
47              
48             =head2 Features
49              
50             Currently, this class can parse the output of the following Siebel Server Manager commands:
51              
52             =over
53              
54             =item *
55              
56             load preferences
57              
58             =item *
59              
60             list comp
61              
62             =item *
63              
64             list compdef
65              
66             =item *
67              
68             list comp type
69              
70             =item *
71              
72             list params
73              
74             =item *
75              
76             list server
77              
78             =item *
79              
80             list session
81              
82             =item *
83              
84             list task
85              
86             =back
87              
88             Also the initial text after connecting to Server Manager can be parsed.
89              
90             =head1 ATTRIBUTES
91              
92             =head2 parsed_tree
93              
94             An array reference of parsed data. Each index should be a reference to another data extructure, most probably an hash
95             reference, with parsed data related from one line read from output of C<srvrmgr> program.
96              
97             This is an read-only attribute.
98              
99             =cut
100              
101             has 'parsed_tree' => (
102             is => 'ro',
103             isa => 'ArrayRef',
104             reader => 'get_parsed_tree',
105             writer => '_set_parsed_tree'
106             );
107              
108             =pod
109              
110             =head2 has_tree
111              
112             A boolean value that identifies if the ListParser object has a parsed tree or not.
113              
114             =cut
115              
116             has 'has_tree' => (
117             is => 'ro',
118             isa => 'Bool',
119             default => 0,
120             writer => '_set_has_tree',
121             reader => 'has_tree'
122             );
123              
124             =pod
125              
126             =head2 last_command
127              
128             A string with the last command identified by the parser. It is used for several things, including changes in the state model machine.
129              
130             This is a read-only attribute.
131              
132             =cut
133              
134             has 'last_command' => (
135             is => 'ro',
136             isa => 'Str',
137             reader => 'get_last_command',
138             writer => 'set_last_command',
139             default => '',
140             trigger => \&_toggle_cmd_changed
141             );
142              
143             =pod
144              
145             =head2 is_cmd_changed
146              
147             A boolean value that identified when the last_command attribute has been changed (i.e another command was identified by the parser).
148              
149             =cut
150              
151             has 'is_cmd_changed' => ( isa => 'Bool', is => 'rw', default => 0 );
152              
153             =pod
154              
155             =head2 buffer
156              
157             An array reference with each one of the indexes being a C<Siebel::Srvrmgr::ListParser::Buffer> object.
158              
159             =cut
160              
161             has 'buffer' => (
162             is => 'rw',
163             isa => 'ArrayRef[Siebel::Srvrmgr::ListParser::Buffer]',
164             reader => 'get_buffer',
165             writer => '_set_buffer',
166             default => sub { return [] }
167             );
168              
169             =pod
170              
171             =head2 enterprise
172              
173             A reference to a L<Siebel::Srvrmgr::ListParser::Output::Greetings>. It is defined during initial parsing (can be available or not).
174              
175             This object has some details about the enterprise connected. Check the related Pod for more information.
176              
177             =cut
178              
179             has 'enterprise' => (
180             is => 'ro',
181             isa => 'Siebel::Srvrmgr::ListParser::Output::Enterprise',
182             reader => 'get_enterprise',
183             writer => '_set_enterprise'
184             );
185              
186             =pod
187              
188             =head2 fsa
189              
190             =cut
191              
192             has 'fsa' => (
193             is => 'ro',
194             isa => 'Siebel::Srvrmgr::ListParser::FSA',
195             reader => 'get_fsa',
196             writer => '_set_fsa',
197             );
198              
199             =pod
200              
201             =head2 clear_raw
202              
203             =cut
204              
205             has clear_raw => (
206             is => 'rw',
207             isa => 'Bool',
208             reader => 'clear_raw',
209             writer => 'set_clear_raw',
210             default => 1
211             );
212              
213             has field_delimiter => ( is => 'ro', isa => 'Chr', reader => 'get_field_del' );
214              
215             =pod
216              
217             =head1 METHODS
218              
219             =head2 is_cmd_changed
220              
221             Sets the boolean attribute with the same name. If no parameter is given, returns the value stored in the C<is_cmd_changed> attribute. If a parameter is given,
222             expects to received true (1) or false (0), otherwise it will return an exception.
223              
224             =head2 get_parsed_tree
225              
226             Returns the parsed_tree attribute.
227              
228             =head2 get_prompt_regex
229              
230             Returns the regular expression reference from the prompt_regex attribute.
231              
232             =head2 set_prompt_regex
233              
234             Sets the prompt_regex attribute. Expects an regular expression reference as parameter.
235              
236             =head2 get_hello_regex
237              
238             Returns the regular expression reference from the hello_regex attribute.
239              
240             =head2 set_hello_regex
241              
242             Sets the hello_regex attribute. Expects an regular expression reference as parameter.
243              
244             =head2 get_last_command
245              
246             Returns an string of the last command read by the parser.
247              
248             =head2 has_tree
249              
250             Returns a boolean value (1 for true, 0 for false) if the parser has or not a parsed tree.
251              
252             =head2 set_last_command
253              
254             Set the last command found in the parser received data. It also triggers that the command has changed (see method is_cmd_changed).
255              
256             =cut
257              
258             sub _toggle_cmd_changed {
259              
260 151     151   332 my ( $self, $new_value, $old_value ) = @_;
261              
262 151         6495 $self->is_cmd_changed(1);
263              
264             }
265              
266             =pod
267              
268             =head2 BUILD
269              
270             Automatically defines the state machine object based on L<Siebel::Srvrmgr::ListParser::FSA>.
271              
272             =cut
273              
274             sub BUILD {
275              
276 77     77 1 158 my $self = shift;
277              
278 77         830 my $copy_ref = Siebel::Srvrmgr::ListParser::OutputFactory->get_mapping();
279              
280 77         143 foreach my $cmd_alias ( keys( %{$copy_ref} ) ) {
  77         342  
281              
282 847         1368 my $regex = $copy_ref->{$cmd_alias}->[1];
283 847         1426 $copy_ref->{$cmd_alias} = $regex;
284              
285             }
286              
287 77         707 $self->_set_fsa( Siebel::Srvrmgr::ListParser::FSA->new($copy_ref) );
288              
289             }
290              
291             =pod
292              
293             =head2 set_buffer
294              
295             Sets the buffer attribute, inserting new C<Siebel::Srvrmgr::ListParser::Buffer> objects into the array reference as necessary.
296              
297             Expects an instance of a L<FSA::State> class as parameter (obligatory parameter).
298              
299             =cut
300              
301             sub set_buffer {
302              
303 12983     12983 1 18101 my $self = shift;
304 12983         17278 my $type = shift;
305 12983         17265 my $line = shift;
306              
307 12983         58038 my $logger = Siebel::Srvrmgr->gimme_logger( blessed($self) );
308              
309 12983 100       284348 if ( defined($line) ) {
310              
311 12933         526492 my $buffer_ref = $self->get_buffer();
312              
313             # already has something, get the last one
314 12933 100       17162 if ( scalar( @{$buffer_ref} ) >= 1 ) {
  12933         29034  
315              
316 12913         34500 $logger->debug('I already have data buffered');
317              
318 12913         79291 my $last_buffer = $buffer_ref->[ $#{$buffer_ref} ];
  12913         22029  
319              
320 12913         34046 $logger->debug(
321             'Command is the same, appending data to last buffer');
322              
323 12913 50       599753 if ( $last_buffer->get_type() eq $type ) {
324              
325 12913 100       23177 if ( $line ne '' ) {
326              
327 12514         37523 $last_buffer->set_content($line);
328             }
329             else {
330 399         1175 $logger->debug(
331             'Ignoring first blank line right after command submission'
332             );
333              
334             }
335              
336             }
337             else {
338              
339 0 0       0 if ( $logger->is_fatal() ) {
340              
341 0         0 $logger->fatal(
342             'Command has not changed but type of output has (got '
343             . $type
344             . ' instead of '
345             . $last_buffer->get_type()
346             . '). Data was ignored' );
347              
348             }
349              
350             }
351              
352             }
353             else {
354              
355 20         69 $logger->fatal(
356             'buffer is still uninitialized even though _create_buffer should already taken care of it'
357             );
358              
359             }
360              
361             }
362             else {
363              
364 50         337 $logger->warn('Undefined content from state received');
365              
366             }
367              
368             }
369              
370             # adds a new buffer to the buffer attribute
371             sub _create_buffer {
372              
373 135     135   762 my $self = shift;
374 135         234 my $type = shift;
375              
376 135         871 my $logger = Siebel::Srvrmgr->gimme_logger( blessed($self) );
377              
378 135 50       3973 if ( Siebel::Srvrmgr::ListParser::OutputFactory->can_create($type) ) {
379              
380 135         6649 my $buffer = Siebel::Srvrmgr::ListParser::Buffer->new(
381             {
382             type => $type,
383             cmd_line => $self->get_last_command()
384             }
385             );
386              
387 135         387 push( @{ $self->get_buffer() }, $buffer );
  135         6091  
388              
389 135 50       490 if ( $logger->is_debug ) {
390              
391 0         0 $logger->debug("Created buffer for $type output");
392              
393             }
394              
395             }
396             else {
397              
398 0         0 $logger->fatal(
399             "Siebel::Srvrmgr::ListParser::OutputFactory cannot create instances of $type type"
400             );
401              
402             }
403              
404             }
405              
406             =pod
407              
408             =head2 clear_buffer
409              
410             Removes the array reference from the buffer attribute and associates a new one with an empty array. This should be used for cleanup purpouses or attemp to free memory.
411              
412             =cut
413              
414             sub clear_buffer {
415              
416 129     129 1 17825 my $self = shift;
417              
418 129         5886 $self->_set_buffer( [] );
419              
420             }
421              
422             =pod
423              
424             =head2 count_parsed
425              
426             Returns an integer with the total number of objects available in the parsed_tree attribute.
427              
428             =cut
429              
430             sub count_parsed {
431              
432 5     5 1 12 my $self = shift;
433              
434 5         8 return scalar( @{ $self->get_parsed_tree() } );
  5         221  
435              
436             }
437              
438             =pod
439              
440             =head2 clear_parsed_tree
441              
442             Removes the reference on parsed_tree attribute. Also, sets has_tree attribute to false.
443              
444             =cut
445              
446             sub clear_parsed_tree {
447              
448 116     116 1 218 my $self = shift;
449              
450 116         5214 $self->_set_has_tree(0);
451              
452 116         5252 $self->_set_parsed_tree( [] );
453              
454             }
455              
456             =pod
457              
458             =head2 set_parsed_tree
459              
460             Sets the parsed_tree attribute, adding references as necessary. Also sets the has_tree attribute to true.
461              
462             This method should not be called directly unless you know what you're doing. See C<append_output> method.
463              
464             =cut
465              
466             sub set_parsed_tree {
467              
468 123     123 1 217 my $self = shift;
469 123         210 my $output = shift;
470              
471 123 100       4962 if ( $self->has_tree() ) {
472              
473 78         3415 my $old_parsed_tree = $self->get_parsed_tree();
474 78         150 push( @{$old_parsed_tree}, $output );
  78         207  
475 78         3455 $self->_set_parsed_tree($old_parsed_tree);
476              
477             }
478             else {
479              
480 45         2084 $self->_set_parsed_tree( [$output] );
481              
482             }
483              
484 123         5394 $self->_set_has_tree(1);
485              
486             }
487              
488             =pod
489              
490             =head2 append_output
491              
492             Appends an object to an existing parsed tree.
493              
494             Can use an optional parameter as L<Siebel::Srvrmgr::ListParser::Buffer> instance, othewise it will use the returned value from C<get_buffer> method.
495              
496             It uses L<Siebel::Srvrmgr::ListParser::OutputFactory> to create the proper
497             L<Siebel::Srvrmgr::ListParser::Output> object based on the L<Siebel::Srvrmgr::ListParser::Buffer> type.
498              
499             If the item received as parameter is a L<Siebel::Srvrmgr::ListParser::Output::Greetings> instance, it will be assigned to the C<enterprise>
500             attribute instead of being added to the C<parsed_tree> attribute.
501              
502             =cut
503              
504             sub append_output {
505              
506 50     50 1 108 my $self = shift;
507 50         96 my $buffer = shift;
508              
509 50 50       148 if ( defined($buffer) ) {
510              
511 0         0 my $output = Siebel::Srvrmgr::ListParser::OutputFactory->build(
512             $buffer->get_type(),
513             {
514             data_type => $buffer->get_type(),
515             raw_data => $buffer->get_content(),
516             cmd_line => $buffer->get_cmd_line()
517             },
518             $self->get_field_del()
519             );
520              
521 0 0       0 if ( $output->isa('Siebel::Srvrmgr::ListParser::Output::Enterprise') ) {
522              
523 0         0 $self->_set_enterprise($output);
524              
525             }
526             else {
527              
528 0         0 $self->set_parsed_tree($output);
529              
530             }
531              
532             }
533             else {
534             # :WORKAROUND:21/08/2013 16:29:35:: not very elegant, but should speed up thing for avoid calling method resolution multiple times
535              
536 50         2104 my $buffer_ref = $self->get_buffer();
537              
538 50         105 foreach my $buffer ( @{$buffer_ref} ) {
  50         213  
539              
540 135         6034 my $output = Siebel::Srvrmgr::ListParser::OutputFactory->build(
541             $buffer->get_type(),
542             {
543             data_type => $buffer->get_type(),
544             raw_data => $buffer->get_content(),
545             cmd_line => $buffer->get_cmd_line()
546             },
547             $self->get_field_del()
548             );
549              
550 135 100       18319 if (
551             $output->isa('Siebel::Srvrmgr::ListParser::Output::Enterprise')
552             )
553             {
554              
555 12         578 $self->_set_enterprise($output);
556              
557             }
558             else {
559              
560 123         512 $self->set_parsed_tree($output);
561              
562             }
563              
564             }
565              
566             }
567              
568 50         127 return 1;
569              
570             }
571              
572             =pod
573              
574             =head2 parse
575              
576             Parses one or more commands output executed through C<srvrmgr> program.
577              
578             Expects as parameter an array reference with the output of C<srvrmgr>, including the command executed. The array references indexes values should be rid off any
579             EOL character.
580              
581             It will create an L<FSA::Rules> object to parse the given array reference, calling C<append_output> method for each L<Siebel::Srvrmgr::ListParser::Buffer> object
582             found.
583              
584             This method will raise an exception if a given output cannot be identified by the parser.
585              
586             =cut
587              
588             sub parse {
589              
590 50     50 1 9190 my $self = shift;
591 50         136 my $data_ref = shift;
592              
593 50         594 my $logger = Siebel::Srvrmgr->gimme_logger( blessed($self) );
594              
595             $logger->logdie('Received an invalid buffer as parameter')
596             unless ( ( defined($data_ref) )
597             and ( ref($data_ref) eq 'ARRAY' )
598 50 50 33     2912 and ( scalar( @{$data_ref} ) > 0 ) );
  50   33     297  
599              
600 50 100       364 if ( string_has_bom( $data_ref->[0] ) ) {
601              
602 4         59 $data_ref->[0] = strip_bom_from_string( $data_ref->[0] );
603              
604             }
605              
606 50         3095 $self->get_fsa->set_data($data_ref);
607 50 50       2517 $self->get_fsa->start() unless ( $self->get_fsa()->curr_state() );
608 50         896 $data_ref = undef;
609              
610 50         122 my $found_prompt = 0;
611 50         121 my $prev_state_name;
612              
613 50         109 do {
614              
615 13134         513942 my $state = $self->get_fsa->try_switch();
616              
617 13134 100       94377 if ( defined($state) ) {
618              
619 286         843 $prev_state_name = $state->name;
620 286         13803 my $curr_msg = $self->get_fsa()->get_curr_line();
621 286         907 $found_prompt = $state->notes('found_prompt');
622              
623             # :TODO:03-10-2013:arfreitas: find a way to keep circular references between the two objects to avoid
624             # checking state change everytime with is_cmd_changed
625              
626             SWITCH: {
627              
628             # :WORKAROUND:03-10-2013:arfreitas: command_submission defines is_cmd_changed but it is not a
629             # Siebel::Srvrmgr::ListParser::Output subclass, so it's not worth to create a buffer object for it and discard later.
630             # Anyway, is expected that after a command is submitted, the next message is the output from it and it needs
631             # a buffer to be stored
632 286 100       3538 if ( $self->get_fsa->prev_state()->name() eq
  286         12215  
633             'command_submission' )
634             {
635              
636 123         1887 $self->_create_buffer( $state->name() );
637 123         1037 last SWITCH;
638             }
639              
640 163 100       3249 if ( $state->notes('is_cmd_changed') ) {
641              
642 151 50       2227 $logger->debug( 'calling set_last_command with ['
643             . $state->notes('last_command')
644             . ']' )
645             if ( $logger->is_debug() );
646              
647 151         1140 $self->set_last_command( $state->notes('last_command') );
648 151         359 last SWITCH;
649              
650             }
651              
652 12 50       209 if ( $state->notes('create_greetings') ) {
653              
654 12         243 $self->_create_buffer( $state->name );
655 12         139 $state->notes( create_greetings => 0 );
656 12         203 $state->notes( greetings_created => 1 );
657 12         188 last SWITCH;
658              
659             }
660              
661             }
662              
663 286 100       1046 if ( $state->notes('is_data_wanted') ) {
664              
665 135 50       2129 $logger->debug( 'calling set_buffer with ['
666             . $state->name() . '], ['
667             . $curr_msg
668             . ']' )
669             if ( $logger->is_debug() );
670 135         1001 $self->set_buffer( $state->name(), $curr_msg );
671              
672             }
673              
674             }
675             else { # state hasn't changed, but let's keep getting other lines
676              
677 12848         502777 $self->set_buffer( $prev_state_name,
678             $self->get_fsa()->get_curr_line() );
679              
680             }
681              
682             } until ( $self->get_fsa->done() );
683              
684 50         249 $self->append_output();
685 50         2116 $self->get_fsa->reset();
686              
687             # :WORKAROUND:21/06/2013 20:36:08:: if parse method is called twice, without calling clear_buffer, the buffer will be reused
688             # and the returned data will be invalid due removal of the last three lines by Siebel::Srvrmgr::ListParser::Output->parse
689             # This should help with memory utilization too
690 50         6058 $self->clear_buffer();
691              
692 50 100       2092 confess
693             'Received an invalid buffer to process: could not find the command prompt'
694             unless ($found_prompt);
695              
696 48         218 return 1;
697              
698             }
699              
700             =head2 DEMOLISH
701              
702             Due issues with memory leak and garbage collection, DEMOLISH was implemented to call additional methods from the API to clean buffer and parsed tree
703             data.
704              
705             =cut
706              
707             sub DEMOLISH {
708              
709 77     77 1 159 my $self = shift;
710 77         205 $self->{fsa} = undef;
711 77         546 $self->clear_buffer();
712 77         290 $self->clear_parsed_tree();
713              
714             }
715              
716             =pod
717              
718             =head1 CAVEATS
719              
720             Checkout the POD for the L<Siebel::Srvrmgr::ListParser::Output> objects to see details about which kind of output is expected if you're getting errors from the parser. There
721             are details regarding how the settings of srvrmgr are expect for output of list commands.
722              
723             =head1 SEE ALSO
724              
725             =over
726              
727             =item *
728              
729             L<Moose>
730              
731             =item *
732              
733             L<FSA::Rules>
734              
735             =item *
736              
737             L<Siebel::Srvrmgr::ListParser::Output>
738              
739             =item *
740              
741             L<Siebel::Srvrmgr::ListParser::OutputFactory>
742              
743             =item *
744              
745             L<Siebel::Srvrmgr::ListParser::Output::Greetings>
746              
747             =item *
748              
749             L<Siebel::Srvrmgr::ListParser::Buffer>
750              
751             =item *
752              
753             L<Siebel::Srvrmgr::Regexes>
754              
755             =back
756              
757             =head1 AUTHOR
758              
759             Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.org<E<gt>
760              
761             =head1 COPYRIGHT AND LICENSE
762              
763             This software is copyright (c) 2012 of Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.orgE<gt>
764              
765             This file is part of Siebel Monitoring Tools.
766              
767             Siebel Monitoring Tools is free software: you can redistribute it and/or modify
768             it under the terms of the GNU General Public License as published by
769             the Free Software Foundation, either version 3 of the License, or
770             (at your option) any later version.
771              
772             Siebel Monitoring Tools is distributed in the hope that it will be useful,
773             but WITHOUT ANY WARRANTY; without even the implied warranty of
774             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
775             GNU General Public License for more details.
776              
777             You should have received a copy of the GNU General Public License
778             along with Siebel Monitoring Tools. If not, see <http://www.gnu.org/licenses/>.
779              
780             =cut
781              
782             __PACKAGE__->meta->make_immutable;
783              
784             1;