File Coverage

blib/lib/Net/CLI/Interact.pm
Criterion Covered Total %
statement 34 44 77.2
branch 4 8 50.0
condition 2 8 25.0
subroutine 11 14 78.5
pod 2 5 40.0
total 53 79 67.0


line stmt bran cond sub pod time code
1             package Net::CLI::Interact;
2             { $Net::CLI::Interact::VERSION = '2.300005' }
3              
4 1     1   103882 use Moo;
  1         11545  
  1         4  
5 1     1   1931 use Sub::Quote;
  1         4948  
  1         58  
6 1     1   496 use Class::Load ();
  1         19315  
  1         30  
7 1     1   516 use MooX::Types::MooseLike::Base qw(InstanceOf Maybe Str HashRef);
  1         6639  
  1         414  
8              
9             with 'Net::CLI::Interact::Role::Engine';
10              
11             has 'my_args' => (
12             is => 'rwp',
13             isa => HashRef,
14             );
15              
16             # stash all args in my_args
17             sub BUILDARGS {
18 2     2 0 6066 my ($class, @args) = @_;
19              
20             # accept single hash ref or naked hash
21 2 100       16 my $params = (ref {} eq ref $args[0] ? $args[0] : {@args});
22              
23 2         37 return { my_args => $params };
24             }
25              
26             sub default_log_categories {
27 0     0 0 0 return (qw/dialogue dump engine object phrasebook prompt transport/);
28             }
29              
30             has 'log_at' => (
31             is => 'rw',
32             isa => Maybe[Str],
33             default => quote_sub(q[ $ENV{'NCI_LOG_AT'} ]),
34             trigger => \&set_global_log_at,
35             );
36              
37             sub set_global_log_at {
38 2     2 1 25 my ($self, $level) = @_;
39 2 50 33     9 return unless defined $level and length $level;
40             $self->logger->log_flags({
41 0         0 map {$_ => $level} default_log_categories()
  0         0  
42             });
43             }
44              
45             sub BUILD {
46 2     2 0 4188 my $self = shift;
47 2         37 $self->set_global_log_at($self->log_at);
48 2   50     32 $self->logger->log('engine', 'notice',
49             sprintf "NCI loaded, version %s", ($Net::CLI::Interact::VERSION || 'devel'));
50             }
51              
52             has 'logger' => (
53             is => 'lazy',
54             isa => InstanceOf['Net::CLI::Interact::Logger'],
55             predicate => 1,
56             clearer => 1,
57             );
58              
59             sub _build_logger {
60 2     2   43 my $self = shift;
61 1     1   494 use Net::CLI::Interact::Logger;
  1         4  
  1         132  
62 2         29 return Net::CLI::Interact::Logger->new($self->my_args);
63             }
64              
65             has 'phrasebook' => (
66             is => 'lazy',
67             isa => InstanceOf['Net::CLI::Interact::Phrasebook'],
68             predicate => 1,
69             clearer => 1,
70             );
71              
72             sub _build_phrasebook {
73 0     0   0 my $self = shift;
74 1     1   588 use Net::CLI::Interact::Phrasebook;
  1         4  
  1         350  
75             return Net::CLI::Interact::Phrasebook->new({
76 0         0 %{ $self->my_args },
  0         0  
77             logger => $self->logger,
78             });
79             }
80              
81             # does not really *change* the phrasebook, just reconfig and nuke
82             sub set_phrasebook {
83 0     0 1 0 my ($self, $args) = @_;
84 0 0 0     0 return unless defined $args and ref {} eq ref $args;
85 0         0 $self->my_args->{$_} = $args->{$_} for keys %$args;
86 0         0 $self->clear_phrasebook;
87             }
88              
89             has 'transport' => (
90             is => 'lazy',
91             isa => quote_sub(q{ $_[0]->isa('Net::CLI::Interact::Transport') }),
92             predicate => 1,
93             clearer => 1,
94             );
95              
96             sub _build_transport {
97 2     2   41 my $self = shift;
98 2 50       13 die 'missing transport' unless exists $self->my_args->{transport};
99 2         10 my $tpt = 'Net::CLI::Interact::Transport::'. $self->my_args->{transport};
100 2         14 Class::Load::load_class($tpt);
101             return $tpt->new({
102 2         143 %{ $self->my_args },
  2         53  
103             logger => $self->logger,
104             });
105             }
106              
107             1;
108              
109             =pod
110              
111             =head1 NAME
112              
113             Net::CLI::Interact - Toolkit for CLI Automation
114              
115             =head1 PURPOSE
116              
117             This module exists to support developers of applications and libraries which
118             must interact with a command line interface.
119              
120             =head1 SYNOPSIS
121              
122             use Net::CLI::Interact;
123            
124             my $s = Net::CLI::Interact->new({
125             personality => 'cisco',
126             transport => 'Telnet',
127             connect_options => { host => '192.0.2.1' },
128             });
129            
130             # respond to a usename/password prompt
131             $s->macro('to_user_exec', {
132             params => ['my_username', 'my_password'],
133             });
134            
135             my $interfaces = $s->cmd('show ip interfaces brief');
136            
137             $s->macro('to_priv_exec', {
138             params => ['my_password'],
139             });
140             # matched prompt is updated automatically
141            
142             # paged output is slurped into one response
143             $s->macro('show_run');
144             my $config = $s->last_response;
145              
146             =head1 DESCRIPTION
147              
148             Automating command line interface (CLI) interactions is not a new idea, but
149             can be tricky to implement. This module aims to provide a simple and
150             manageable interface to CLI interactions, supporting:
151              
152             =over 4
153              
154             =item *
155              
156             SSH, Telnet and Serial-Line connections
157              
158             =item *
159              
160             Unix and Windows support
161              
162             =item *
163              
164             Reuseable device command phrasebooks
165              
166             =back
167              
168             If you're a new user, please read the
169             L<Tutorial|Net::CLI::Interact::Manual::Tutorial>. There's also a
170             L<Cookbook|Net::CLI::Interact::Manual::Cookbook> and a L<Phrasebook
171             Listing|Net::CLI::Interact::Manual::Phrasebook>. For a more complete worked
172             example check out the L<Net::Appliance::Session> distribution, for which this
173             module was written.
174              
175             =head1 INTERFACE
176              
177             =head2 new( \%options )
178              
179             Prepares a new session for you, but will not connect to any device. On
180             Windows platforms, you B<must> download the C<plink.exe> program, and pass
181             its location to the C<app> parameter. Other options are:
182              
183             =over 4
184              
185             =item C<< personality => $name >> (required)
186              
187             The family of device command phrasebooks to load. There is a built-in library
188             within this module, or you can provide a search path to other libraries. See
189             L<Net::CLI::Interact::Manual::Phrasebook> for further details.
190              
191             =item C<< transport => $backend >> (required)
192              
193             The name of the transport backend used for the session, which may be one of
194             L<Telnet|Net::CLI::Interact::Transport::Telnet>,
195             L<SSH|Net::CLI::Interact::Transport::SSH>, or
196             L<Serial|Net::CLI::Interact::Transport::Serial>.
197              
198             =item C<< connect_options => \%options >>
199              
200             If the transport backend can take any options (for example the target
201             hostname), then pass those options in this value as a hash ref. See the
202             respective manual pages for each transport backend for further details.
203              
204             =item C<< log_at => $log_level >>
205              
206             To make using the C<logger> somewhat easier, you can pass this argument the
207             name of a log I<level> (such as C<debug>, C<info>, etc) and all logging in the
208             library will be enabled at that level. Use C<debug> to learn about how the
209             library is working internally. See L<Net::CLI::Interact::Logger> for a list of
210             the valid level names.
211              
212             =item C<< timeout => $seconds >>
213              
214             Configures a default timeout value, in seconds, for interaction with the
215             remote device. The default is 10 seconds. You can also set timeout on a
216             per-command or per-macro call (see below).
217              
218             Note that this does not (currently) apply to the initial connection.
219              
220             =back
221              
222             =head2 cmd( $command )
223              
224             Execute a single command statement on the connected device, and consume output
225             until there is a match with the current I<prompt>. The statement is executed
226             verbatim on the device, with a newline appended.
227              
228             In scalar context the C<last_response> is returned (see below). In list
229             context the gathered response is returned as a list of lines. In both cases
230             your local platform's newline character will end all lines.
231              
232             =head2 macro( $name, \%options? )
233              
234             Execute the commands contained within the named Macro, which must be loaded
235             from a Phrasebook. Options to control the output, including variables for
236             substitution into the Macro, are passed in the C<%options> hash reference.
237              
238             In scalar context the C<last_response> is returned (see below). In list
239             context the gathered response is returned as a list of lines. In both cases
240             your local platform's newline character will end all lines.
241              
242             =head2 last_response
243              
244             Returns the gathered output after the most recent C<cmd> or C<macro>. In
245             scalar context all data is returned. In list context the gathered response is
246             returned as a list of lines. In both cases your local platform's newline
247             character will end all lines.
248              
249             =head2 transport
250              
251             Returns the L<Transport|Net::CLI::Interact::Transport> backend which was
252             loaded based on the C<transport> option to C<new>. See the
253             L<Telnet|Net::CLI::Interact::Transport::Telnet>,
254             L<SSH|Net::CLI::Interact::Transport::SSH>, or
255             L<Serial|Net::CLI::Interact::Transport::Serial> documentation for further
256             details.
257              
258             =head2 phrasebook
259              
260             Returns the Phrasebook object which was loaded based on the C<personality>
261             option given to C<new>. See L<Net::CLI::Interact::Phrasebook> for further
262             details.
263              
264             =head2 set_phrasebook( \%options )
265              
266             Allows you to (re-)configure the loaded phrasebook, perhaps changing the
267             personality or library, or other properties. The C<%options> Hash ref should
268             be any parameters from the L<Phrasebook|Net::CLI::Interact::Phrasebook>
269             module, but at a minimum must include a C<personality>.
270              
271             =head2 set_default_contination( $macro_name )
272              
273             Briefly, a Continuation handles the slurping of paged output from commands.
274             See the L<Net::CLI::Interact::Phrasebook> documentation for further details.
275              
276             Pass in the name of a defined Contination (Macro) to enable paging handling as
277             a default for all sent commands. This is an alternative to describing the
278             Continuation format in each Macro.
279              
280             To unset the default Continuation, call the C<clear_default_continuation>
281             method.
282              
283             =head2 logger
284              
285             This is the application's L<Logger|Net::CLI::Interact::Logger> object. A
286             powerful logging subsystem is available to your application, built upon the
287             L<Log::Dispatch> distribution. You can enable logging of this module's
288             processes at various levels, or add your own logging statements.
289              
290             =head2 set_global_log_at( $level )
291              
292             To make using the C<logger> somewhat easier, you can pass this method the
293             name of a log I<level> (such as C<debug>, C<info>, etc) and all logging in the
294             library will be enabled at that level. Use C<debug> to learn about how the
295             library is working internally. See L<Net::CLI::Interact::Logger> for a list of
296             the valid level names.
297              
298             =head1 FUTHER READING
299              
300             =head2 Prompt Matching
301              
302             Whenever a command statement is issued, output is slurped until a matching
303             prompt is seen in that output. Control of the Prompts is shared between the
304             definitions in L<Net::CLI::Interact::Phrasebook> dictionaries, and methods of
305             the L<Net::CLI::Interact::Role::Prompt> core component. See that module's
306             documentation for further details.
307              
308             =head2 Actions and ActionSets
309              
310             All commands and macros are composed from their phrasebook definitions into
311             L<Actions|Net::CLI::Interact::Action> and
312             L<ActionSets|Net::CLI::Interact::ActionSet> (iterable sequences of Actions).
313             See those modules' documentation for further details, in case you wish to
314             introspect their structures.
315              
316             =head1 COMPOSITION
317              
318             See the following for further interface details:
319              
320             =over 4
321              
322             =item *
323              
324             L<Net::CLI::Interact::Role::Engine>
325              
326             =back
327              
328             =head1 AUTHOR
329              
330             Oliver Gorwits <oliver@cpan.org>
331              
332             =head1 COPYRIGHT AND LICENSE
333              
334             This software is copyright (c) 2017 by Oliver Gorwits.
335              
336             This is free software; you can redistribute it and/or modify it under
337             the same terms as the Perl 5 programming language system itself.
338              
339             =cut
340