File Coverage

blib/lib/Siebel/Srvrmgr/ListParser.pm
Criterion Covered Total %
statement 128 136 94.1
branch 31 44 70.4
condition 2 6 33.3
subroutine 20 20 100.0
pod 9 9 100.0
total 190 215 88.3


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