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