File Coverage

blib/lib/Siebel/Srvrmgr/ListParser.pm
Criterion Covered Total %
statement 142 150 94.6
branch 33 48 68.7
condition 2 6 33.3
subroutine 21 21 100.0
pod 9 9 100.0
total 207 234 88.4


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 22     22   1350471 use Moose;
  22         5485368  
  22         159  
18 22     22   119644 use Siebel::Srvrmgr::ListParser::OutputFactory;
  22         69  
  22         775  
19 22     22   7685 use Siebel::Srvrmgr::ListParser::Buffer;
  22         188412  
  22         1036  
20 22     22   8549 use Siebel::Srvrmgr;
  22         59  
  22         555  
21 22     22   191 use Log::Log4perl;
  22         77  
  22         127  
22 22     22   10760 use Siebel::Srvrmgr::ListParser::FSA;
  22         56  
  22         626  
23 22     22   169 use namespace::autoclean;
  22         25  
  22         174  
24 22     22   1765 use Carp;
  22         25  
  22         1133  
25 22     22   7605 use Siebel::Srvrmgr::Types;
  22         49  
  22         745  
26 22     22   24179 use String::BOM qw(string_has_bom strip_bom_from_string);
  22         318  
  22         93  
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              
261 151     151   240 my ( $self, $new_value, $old_value ) = @_;
262              
263 151         4283 $self->is_cmd_changed(1);
264              
265             }
266              
267             =pod
268              
269             =head2 BUILD
270              
271             Automatically defines the state machine object based on L<Siebel::Srvrmgr::ListParser::FSA>.
272              
273             =cut
274              
275             sub BUILD {
276              
277 77     77 1 116 my $self = shift;
278              
279 77         571 my $copy_ref = Siebel::Srvrmgr::ListParser::OutputFactory->get_mapping();
280              
281 77         102 foreach my $cmd_alias ( keys( %{$copy_ref} ) ) {
  77         282  
282              
283 770         795 my $regex = $copy_ref->{$cmd_alias}->[1];
284 770         768 $copy_ref->{$cmd_alias} = $regex;
285              
286             }
287              
288 77         565 $self->_set_fsa( Siebel::Srvrmgr::ListParser::FSA->new($copy_ref) );
289              
290             }
291              
292             =pod
293              
294             =head2 set_buffer
295              
296             Sets the buffer attribute, inserting new C<Siebel::Srvrmgr::ListParser::Buffer> objects into the array reference as necessary.
297              
298             Expects an instance of a L<FSA::State> class as parameter (obligatory parameter).
299              
300             =cut
301              
302             sub set_buffer {
303              
304 12983     12983 1 65437 my $self = shift;
305 12983         9891 my $type = shift;
306 12983         9815 my $line = shift;
307              
308 12983         30192 my $log_cfg = Siebel::Srvrmgr->logging_cfg();
309 12983 50       34545 confess 'Could not start logging facilities'
310             unless ( Log::Log4perl->init_once( \$log_cfg ) );
311 12983         47076 my $logger = Log::Log4perl->get_logger('Siebel::Srvrmgr::ListParser');
312              
313 12983 100       201919 if ( defined($line) ) {
314              
315 12933         370243 my $buffer_ref = $self->get_buffer();
316              
317             # already has something, get the last one
318 12933 100       9696 if ( scalar( @{$buffer_ref} ) >= 1 ) {
  12933         17988  
319              
320 12913         22883 $logger->debug('I already have data buffered');
321              
322 12913         50587 my $last_buffer = $buffer_ref->[ $#{$buffer_ref} ];
  12913         12653  
323              
324 12913         17052 $logger->debug(
325             'Command is the same, appending data to last buffer');
326              
327 12913 50       378445 if ( $last_buffer->get_type() eq $type ) {
328              
329 12913 100       14015 if ( $line ne '' ) {
330              
331 12514         23141 $last_buffer->set_content($line);
332             }
333             else {
334 399         740 $logger->debug(
335             'Ignoring first blank line right after command submission'
336             );
337              
338             }
339              
340             }
341             else {
342              
343 0 0       0 if ( $logger->is_fatal() ) {
344              
345 0         0 $logger->fatal(
346             'Command has not changed but type of output has (got '
347             . $type
348             . ' instead of '
349             . $last_buffer->get_type()
350             . '). Data was ignored' );
351              
352             }
353              
354             }
355              
356             }
357             else {
358              
359 20         52 $logger->fatal(
360             'buffer is still uninitialized even though _create_buffer should already taken care of it'
361             );
362              
363             }
364              
365             }
366             else {
367              
368 50         196 $logger->warn('Undefined content from state received');
369              
370             }
371              
372             }
373              
374             # adds a new buffer to the buffer attribute
375             sub _create_buffer {
376              
377 135     135   522 my $self = shift;
378 135         192 my $type = shift;
379              
380 135         683 my $log_cfg = Siebel::Srvrmgr->logging_cfg();
381 135 50       561 confess 'Could not start logging facilities'
382             unless ( Log::Log4perl->init_once( \$log_cfg ) );
383 135         701 my $logger = Log::Log4perl->get_logger('Siebel::Srvrmgr::ListParser');
384              
385 135 50       3039 if ( Siebel::Srvrmgr::ListParser::OutputFactory->can_create($type) ) {
386              
387 135         4843 my $buffer = Siebel::Srvrmgr::ListParser::Buffer->new(
388             {
389             type => $type,
390             cmd_line => $self->get_last_command()
391             }
392             );
393              
394 135         289 push( @{ $self->get_buffer() }, $buffer );
  135         3826  
395              
396 135 50       356 if ( $logger->is_debug ) {
397              
398 0         0 $logger->debug("Created buffer for $type output");
399              
400             }
401              
402             }
403             else {
404              
405 0         0 $logger->fatal(
406             "Siebel::Srvrmgr::ListParser::OutputFactory cannot create instances of $type type"
407             );
408              
409             }
410              
411             }
412              
413             =pod
414              
415             =head2 clear_buffer
416              
417             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.
418              
419             =cut
420              
421             sub clear_buffer {
422              
423 129     129 1 13674 my $self = shift;
424              
425 129         4462 $self->_set_buffer( [] );
426              
427             }
428              
429             =pod
430              
431             =head2 count_parsed
432              
433             Returns an integer with the total number of objects available in the parsed_tree attribute.
434              
435             =cut
436              
437             sub count_parsed {
438              
439 5     5 1 7 my $self = shift;
440              
441 5         7 return scalar( @{ $self->get_parsed_tree() } );
  5         152  
442              
443             }
444              
445             =pod
446              
447             =head2 clear_parsed_tree
448              
449             Removes the reference on parsed_tree attribute. Also, sets has_tree attribute to false.
450              
451             =cut
452              
453             sub clear_parsed_tree {
454              
455 116     116 1 155 my $self = shift;
456              
457 116         3635 $self->_set_has_tree(0);
458              
459 116         3440 $self->_set_parsed_tree( [] );
460              
461             }
462              
463             =pod
464              
465             =head2 set_parsed_tree
466              
467             Sets the parsed_tree attribute, adding references as necessary. Also sets the has_tree attribute to true.
468              
469             This method should not be called directly unless you know what you're doing. See C<append_output> method.
470              
471             =cut
472              
473             sub set_parsed_tree {
474              
475 123     123 1 175 my $self = shift;
476 123         171 my $output = shift;
477              
478 123 100       3613 if ( $self->has_tree() ) {
479              
480 78         2253 my $old_parsed_tree = $self->get_parsed_tree();
481 78         106 push( @{$old_parsed_tree}, $output );
  78         178  
482 78         2129 $self->_set_parsed_tree($old_parsed_tree);
483              
484             }
485             else {
486              
487 45         1499 $self->_set_parsed_tree( [$output] );
488              
489             }
490              
491 123         3401 $self->_set_has_tree(1);
492              
493             }
494              
495             =pod
496              
497             =head2 append_output
498              
499             Appends an object to an existing parsed tree.
500              
501             Can use an optional parameter as L<Siebel::Srvrmgr::ListParser::Buffer> instance, othewise it will use the returned value from C<get_buffer> method.
502              
503             It uses L<Siebel::Srvrmgr::ListParser::OutputFactory> to create the proper
504             L<Siebel::Srvrmgr::ListParser::Output> object based on the L<Siebel::Srvrmgr::ListParser::Buffer> type.
505              
506             If the item received as parameter is a L<Siebel::Srvrmgr::ListParser::Output::Greetings> instance, it will be assigned to the C<enterprise>
507             attribute instead of being added to the C<parsed_tree> attribute.
508              
509             =cut
510              
511             sub append_output {
512              
513 50     50 1 98 my $self = shift;
514 50         73 my $buffer = shift;
515              
516 50 50       140 if ( defined($buffer) ) {
517              
518 0         0 my $output = Siebel::Srvrmgr::ListParser::OutputFactory->build(
519             $buffer->get_type(),
520             {
521             data_type => $buffer->get_type(),
522             raw_data => $buffer->get_content(),
523             cmd_line => $buffer->get_cmd_line()
524             },
525             $self->get_field_del()
526             );
527              
528 0 0       0 if ( $output->isa('Siebel::Srvrmgr::ListParser::Output::Enterprise') ) {
529              
530 0         0 $self->_set_enterprise($output);
531              
532             }
533             else {
534              
535 0         0 $self->set_parsed_tree($output);
536              
537             }
538              
539             }
540             else {
541             # :WORKAROUND:21/08/2013 16:29:35:: not very elegant, but should speed up thing for avoid calling method resolution multiple times
542              
543 50         1638 my $buffer_ref = $self->get_buffer();
544              
545 50         73 foreach my $buffer ( @{$buffer_ref} ) {
  50         138  
546              
547 135         3969 my $output = Siebel::Srvrmgr::ListParser::OutputFactory->build(
548             $buffer->get_type(),
549             {
550             data_type => $buffer->get_type(),
551             raw_data => $buffer->get_content(),
552             cmd_line => $buffer->get_cmd_line()
553             },
554             $self->get_field_del()
555             );
556              
557 135 100       16527 if (
558             $output->isa('Siebel::Srvrmgr::ListParser::Output::Enterprise')
559             )
560             {
561              
562 12         393 $self->_set_enterprise($output);
563              
564             }
565             else {
566              
567 123         477 $self->set_parsed_tree($output);
568              
569             }
570              
571             }
572              
573             }
574              
575 50         104 return 1;
576              
577             }
578              
579             =pod
580              
581             =head2 parse
582              
583             Parses one or more commands output executed through C<srvrmgr> program.
584              
585             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
586             EOL character.
587              
588             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
589             found.
590              
591             This method will raise an exception if a given output cannot be identified by the parser.
592              
593             =cut
594              
595             sub parse {
596              
597 50     50 1 4922 my $self = shift;
598 50         79 my $data_ref = shift;
599              
600 50         429 my $logger = Siebel::Srvrmgr->gimme_logger( ref($self) );
601              
602 50         209 $logger->logdie('Received an invalid buffer as parameter')
603             unless ( ( defined($data_ref) )
604             and ( ref($data_ref) eq 'ARRAY' )
605 50 50 33     1880 and ( scalar( @{$data_ref} ) > 0 ) );
      33        
606              
607 50 100       276 if ( string_has_bom( $data_ref->[0] ) ) {
608              
609 4         48 $data_ref->[0] = strip_bom_from_string( $data_ref->[0] );
610              
611             }
612              
613 50         2086 $self->get_fsa->notes( all_data => $data_ref );
614 50         2139 $self->get_fsa->notes( line_num => 0 );
615 50 50       1820 $self->get_fsa->start() unless ( $self->get_fsa()->curr_state() );
616 50         607 $data_ref = undef;
617              
618 50         96 my $found_prompt = 0;
619 50         76 my $prev_state_name;
620              
621 50         77 do {
622              
623 13134         331646 my $state = $self->get_fsa->try_switch();
624              
625 13134 100       59177 if ( defined($state) ) {
626              
627 286         633 $prev_state_name = $state->name;
628 286         1038 my $curr_msg = $state->notes('line');
629 286         2208 $found_prompt = $state->notes('found_prompt');
630              
631             # :TODO:03-10-2013:arfreitas: find a way to keep circular references between the two objects to avoid
632             # checking state change everytime with is_cmd_changed
633              
634             SWITCH: {
635              
636             # :WORKAROUND:03-10-2013:arfreitas: command_submission defines is_cmd_changed but it is not a
637             # Siebel::Srvrmgr::ListParser::Output subclass, so it's not worth to create a buffer object for it and discard later.
638             # Anyway, is expected that after a command is submitted, the next message is the output from it and it needs
639             # a buffer to be stored
640 286 100       1780 if ( $self->get_fsa->prev_state()->name() eq
  286         8814  
641             'command_submission' )
642             {
643              
644 123         1243 $self->_create_buffer( $state->name() );
645 123         692 last SWITCH;
646             }
647              
648 163 100       2395 if ( $state->notes('is_cmd_changed') ) {
649              
650 151 50       1481 $logger->debug( 'calling set_last_command with ['
651             . $state->notes('last_command')
652             . ']' )
653             if ( $logger->is_debug() );
654              
655 151         851 $self->set_last_command( $state->notes('last_command') );
656 151         298 last SWITCH;
657              
658             }
659              
660 12 50       129 if ( $state->notes('create_greetings') ) {
661              
662 12         113 $self->_create_buffer( $state->name );
663 12         97 $state->notes( create_greetings => 0 );
664 12         133 $state->notes( greetings_created => 1 );
665 12         120 last SWITCH;
666              
667             }
668              
669             }
670              
671 286 100       694 if ( $state->notes('is_data_wanted') ) {
672              
673 135 50       1351 $logger->debug( 'calling set_buffer with ['
674             . $state->name() . '], ['
675             . $curr_msg
676             . ']' )
677             if ( $logger->is_debug() );
678 135         630 $self->set_buffer( $state->name(), $curr_msg );
679              
680             }
681              
682             }
683             else { # state hasn't changed, but let's keep getting other lines
684              
685 12848         321530 $self->set_buffer( $prev_state_name,
686             $self->get_fsa->notes('line') );
687              
688             }
689              
690             } until ( $self->get_fsa->done() );
691              
692 50         181 $self->append_output();
693 50         1460 $self->get_fsa->reset();
694              
695             # :WORKAROUND:21/06/2013 20:36:08:: if parse method is called twice, without calling clear_buffer, the buffer will be reused
696             # and the returned data will be invalid due removal of the last three lines by Siebel::Srvrmgr::ListParser::Output->parse
697             # This should help with memory utilization too
698 50         4732 $self->clear_buffer();
699              
700 50 100       1569 confess
701             'Received an invalid buffer to process: could not find the command prompt'
702             unless ($found_prompt);
703              
704 48         186 return 1;
705              
706             }
707              
708             =head2 DEMOLISH
709              
710             Due issues with memory leak and garbage collection, DEMOLISH was implemented to call additional methods from the API to clean buffer and parsed tree
711             data.
712              
713             =cut
714              
715             sub DEMOLISH {
716              
717 77     77 1 126 my $self = shift;
718 77         159 $self->{fsa} = undef;
719 77         412 $self->clear_buffer();
720 77         230 $self->clear_parsed_tree();
721              
722             }
723              
724             =pod
725              
726             =head1 CAVEATS
727              
728             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
729             are details regarding how the settings of srvrmgr are expect for output of list commands.
730              
731             =head1 SEE ALSO
732              
733             =over
734              
735             =item *
736              
737             L<Moose>
738              
739             =item *
740              
741             L<FSA::Rules>
742              
743             =item *
744              
745             L<Siebel::Srvrmgr::ListParser::Output>
746              
747             =item *
748              
749             L<Siebel::Srvrmgr::ListParser::OutputFactory>
750              
751             =item *
752              
753             L<Siebel::Srvrmgr::ListParser::Output::Greetings>
754              
755             =item *
756              
757             L<Siebel::Srvrmgr::ListParser::Buffer>
758              
759             =item *
760              
761             L<Siebel::Srvrmgr::Regexes>
762              
763             =back
764              
765             =head1 AUTHOR
766              
767             Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.org<E<gt>
768              
769             =head1 COPYRIGHT AND LICENSE
770              
771             This software is copyright (c) 2012 of Alceu Rodrigues de Freitas Junior, E<lt>arfreitas@cpan.orgE<gt>
772              
773             This file is part of Siebel Monitoring Tools.
774              
775             Siebel Monitoring Tools is free software: you can redistribute it and/or modify
776             it under the terms of the GNU General Public License as published by
777             the Free Software Foundation, either version 3 of the License, or
778             (at your option) any later version.
779              
780             Siebel Monitoring Tools is distributed in the hope that it will be useful,
781             but WITHOUT ANY WARRANTY; without even the implied warranty of
782             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
783             GNU General Public License for more details.
784              
785             You should have received a copy of the GNU General Public License
786             along with Siebel Monitoring Tools. If not, see <http://www.gnu.org/licenses/>.
787              
788             =cut
789              
790             __PACKAGE__->meta->make_immutable;
791              
792             1;