File Coverage

blib/lib/App/Manoc/Netwalker/Control.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 App::Manoc::Netwalker::Control;
2             #ABSTRACT: Netwalker control interface
3              
4              
5 1     1   4041 use Moose;
  1         2  
  1         6  
6              
7             our $VERSION = '2.99.2'; ##TRIAL VERSION
8              
9 1     1   5926 use namespace::autoclean;
  1         3  
  1         7  
10              
11             with 'App::Manoc::Logger::Role';
12              
13 1     1   490 use IO::Socket;
  1         15085  
  1         4  
14 1     1   524 use POE qw(Wheel::ListenAccept Wheel::ReadWrite);
  0            
  0            
15              
16              
17             has config => (
18             is => 'ro',
19             isa => 'App::Manoc::Netwalker::Config',
20             required => 1
21             );
22              
23              
24             has poller => (
25             is => 'ro',
26             isa => 'App::Manoc::Netwalker::Poller::Workers',
27             required => 1,
28             );
29              
30              
31             has discoverer => (
32             is => 'ro',
33             isa => 'App::Manoc::Netwalker::Discover::Workers',
34             required => 1,
35             );
36              
37              
38             has server => (
39             is => 'rw',
40             isa => 'Ref',
41             );
42              
43              
44             has session => (
45             isa => 'POE::Session',
46             is => 'ro',
47             required => 1,
48             lazy => 1,
49             builder => '_build_session',
50             clearer => 'remove_server',
51             predicate => 'has_server',
52             );
53              
54              
55             has clients => (
56             traits => ['Hash'],
57             isa => 'HashRef',
58             is => 'rw',
59             lazy => 1,
60             required => 1,
61             default => sub { {} },
62             handles => {
63             set_client => 'set',
64             get_client => 'get',
65             remove_client => 'delete',
66             has_client => 'count',
67             num_client => 'count',
68             get_client_ids => 'keys',
69             },
70             );
71              
72              
73             sub MANOC_CONSOLE_HELLO { "OK Manoc Netwalker console" }
74              
75             sub _build_session {
76             my $self = shift;
77              
78             return POE::Session->create(
79             object_states => [
80             $self => [
81             qw(
82             _start
83             on_client_accept
84             on_server_error
85             on_client_input
86             on_client_error
87             )
88             ],
89             ],
90             );
91             }
92              
93             sub _start {
94             my ( $self, $job, $args, $kernel, $heap ) = @_[ OBJECT, ARG0, ARG1, KERNEL, HEAP ];
95              
96             my $port = $self->config->control_port;
97              
98             my $handle;
99             if ( $port =~ m|^/| ) {
100             # looks like a path, create a UNIX socket
101             $handle = IO::Socket::UNIX->new(
102             Type => SOCK_STREAM(),
103             Local => $port,
104             Listen => 1,
105             );
106             }
107             else {
108             # TCP socket
109             $handle = IO::Socket::INET->new(
110             LocalPort => $port,
111             Listen => 5,
112             ReuseAddr => 1,
113             );
114             }
115             $handle or $self->log->logdie("Cannot create control socket $port: $!");
116              
117             # Start the server.
118             my $server = POE::Wheel::ListenAccept->new(
119             Handle => $handle,
120             AcceptEvent => "on_client_accept",
121             ErrorEvent => "on_server_error",
122             );
123             $self->server($server);
124             }
125              
126              
127             sub on_client_accept {
128             my ( $self, $client_socket ) = @_[ OBJECT, ARG0 ];
129             my $io_wheel = POE::Wheel::ReadWrite->new(
130             Handle => $client_socket,
131             InputEvent => "on_client_input",
132             ErrorEvent => "on_client_error",
133             );
134              
135             $io_wheel->put(MANOC_CONSOLE_HELLO);
136              
137             $self->set_client( $io_wheel->ID => $io_wheel );
138             }
139              
140              
141             sub on_server_error {
142             my ( $self, $operation, $errnum, $errstr ) = @_[ OBJECT, ARG0, ARG1, ARG2 ];
143             warn "Server $operation error $errnum: $errstr\n";
144             $self->server(undef);
145             }
146              
147              
148             sub on_client_input {
149             my ( $self, $input, $wheel_id ) = @_[ OBJECT, ARG0, ARG1 ];
150              
151             my $client = $self->get_client($wheel_id);
152              
153             my @tokens = split( /\s+/, $input );
154             my $command = lc( shift @tokens );
155              
156             my $handler = "command_$command";
157             if ( $self->can($handler) ) {
158             my $output = $self->$handler(@tokens);
159             $client->put($output);
160             }
161             elsif ( $command eq 'close' ) {
162             $self->remove_client($wheel_id);
163             }
164             else {
165             $client->put("ERR Unknown command $command");
166             }
167             }
168              
169              
170             sub on_client_error {
171             my $self = $_[OBJECT];
172             my $wheel_id = $_[ARG3];
173              
174             # Handle client error, including disconnect.
175             $self->remove_client($wheel_id);
176             }
177              
178              
179             sub command_status {
180             my $self = shift;
181              
182             my $scoreboard = $self->poller->scoreboard_status;
183             my $output = "OK " . scalar( keys(%$scoreboard) ) . " elements";
184              
185             while ( my ( $k, $v ) = each(%$scoreboard) ) {
186             $output .= "\n$k $v";
187             }
188              
189             return $output;
190             }
191              
192              
193             sub command_enqueue {
194             my ( $self, $type, $id ) = @_;
195              
196             $type = lc($type);
197             if ( $type eq 'device' ) {
198             $self->poller->enqueue_device($id);
199             return "OK added device $id";
200             }
201             if ( $type eq 'server' ) {
202             $self->poller->enqueue_server($id);
203             return "OK added server $id";
204             }
205              
206             return "ERR unknown object $type";
207             }
208              
209              
210             sub command_quit {
211             my $self = $_[OBJECT];
212             my $wheel_id = $_[ARG3];
213              
214             # Handle client error, including disconnect.
215             $self->remove_client($wheel_id);
216             }
217              
218             sub BUILD {
219             shift->session();
220             }
221              
222             no Moose;
223             __PACKAGE__->meta->make_immutable;
224              
225             # Local Variables:
226             # mode: cperl
227             # indent-tabs-mode: nil
228             # cperl-indent-level: 4
229             # cperl-indent-parens-as-block: t
230             # End:
231              
232             __END__
233              
234             =pod
235              
236             =head1 NAME
237              
238             App::Manoc::Netwalker::Control - Netwalker control interface
239              
240             =head1 VERSION
241              
242             version 2.99.2
243              
244             =head1 DESCRIPTION
245              
246             This class implements a control server for Netwalker. It is based on a simple line oriented protocol.
247              
248             =head1 ATTRIBUTES
249              
250             =head2 config
251              
252             Netwalker configuration. Required.
253              
254             The value in config->control_port can be a port (TCP socket) or a path (UNIX socket)
255              
256             =head2 poller
257              
258             Reference to poller Workers object. Required.
259              
260             =head2 poller
261              
262             Reference to discovery workers object. Required.
263              
264             =head2 server
265              
266             A L<POE::Wheel::ListenAccept> creating during _start.
267              
268             =head2 session
269              
270             POE session. Required.
271              
272             =head2 clients
273              
274             Hash wheel-id to wheel, used by callbacks.
275              
276             =head1 METHODS
277              
278             =head2 on_client_accept
279              
280             Callback on new client connection.
281              
282             =head2 on_server_error( $operation, $errnum, $errstr )
283              
284             Callback on server error
285              
286             =head2 on_client_input( $input, $wheel_id )
287              
288             Callback for client input. Parses input line and call the corresponding command_<name> callback.
289              
290             =head2 on_client_error
291              
292             =head2 command_status
293              
294             Manages the C<STATUS> command.
295              
296             =head2 command_enqueue
297              
298             Manages the C<ENQUEUE DEVICE|SERVER <id>> command.
299              
300             =head2 command_quit
301              
302             Manages the C<QUIT> command closing the client connection.
303              
304             =head1 FUNCTIONS
305              
306             =head2 MANOC_CONSOLE_HELLO
307              
308             Return the welcome message
309              
310             =head1 AUTHORS
311              
312             =over 4
313              
314             =item *
315              
316             Gabriele Mambrini <gmambro@cpan.org>
317              
318             =item *
319              
320             Enrico Liguori
321              
322             =back
323              
324             =head1 COPYRIGHT AND LICENSE
325              
326             This software is copyright (c) 2017 by Gabriele Mambrini.
327              
328             This is free software; you can redistribute it and/or modify it under
329             the same terms as the Perl 5 programming language system itself.
330              
331             =cut