line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Appliance::Session; |
2
|
|
|
|
|
|
|
{ $Net::Appliance::Session::VERSION = '4.300004' } |
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
167069
|
use Moo; |
|
1
|
|
|
|
|
11304
|
|
|
1
|
|
|
|
|
4
|
|
5
|
1
|
|
|
1
|
|
1895
|
use Sub::Quote; |
|
1
|
|
|
|
|
4663
|
|
|
1
|
|
|
|
|
66
|
|
6
|
1
|
|
|
1
|
|
531
|
use MooX::Types::MooseLike::Base qw(Any Bool Int Str HashRef InstanceOf); |
|
1
|
|
|
|
|
6447
|
|
|
1
|
|
|
|
|
92
|
|
7
|
1
|
|
|
1
|
|
554
|
use Net::CLI::Interact; |
|
1
|
|
|
|
|
408691
|
|
|
1
|
|
|
|
|
742
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
with 'Net::Appliance::Session::Transport'; |
10
|
|
|
|
|
|
|
with 'Net::Appliance::Session::Engine'; |
11
|
|
|
|
|
|
|
with 'Net::Appliance::Session::Async'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# import Try::Tiny try/catch/finally into caller's namespace |
14
|
|
|
|
|
|
|
sub import { |
15
|
1
|
|
|
1
|
|
13
|
my $caller = caller; |
16
|
|
|
|
|
|
|
|
17
|
1
|
|
|
1
|
|
9
|
eval <<ENDEVAL; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
25
|
|
|
1
|
|
|
|
|
74
|
|
18
|
|
|
|
|
|
|
package $caller; |
19
|
|
|
|
|
|
|
use Class::Load (); |
20
|
|
|
|
|
|
|
Class::Load::load_class('Try::Tiny'); |
21
|
|
|
|
|
|
|
Try::Tiny->import(); |
22
|
|
|
|
|
|
|
ENDEVAL |
23
|
|
|
|
|
|
|
|
24
|
1
|
50
|
|
|
|
18
|
die $@ if $@; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
foreach my $slot (qw/ |
28
|
|
|
|
|
|
|
logged_in |
29
|
|
|
|
|
|
|
in_privileged_mode |
30
|
|
|
|
|
|
|
in_configure_mode |
31
|
|
|
|
|
|
|
privileged_paging |
32
|
|
|
|
|
|
|
close_called |
33
|
|
|
|
|
|
|
/) { |
34
|
|
|
|
|
|
|
has $slot => ( |
35
|
|
|
|
|
|
|
is => 'rw', |
36
|
|
|
|
|
|
|
isa => Bool, |
37
|
|
|
|
|
|
|
required => 0, |
38
|
|
|
|
|
|
|
default => quote_sub('0'), |
39
|
|
|
|
|
|
|
); |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
foreach my $slot (qw/ |
43
|
|
|
|
|
|
|
do_paging |
44
|
|
|
|
|
|
|
do_login |
45
|
|
|
|
|
|
|
do_privileged_mode |
46
|
|
|
|
|
|
|
do_configure_mode |
47
|
|
|
|
|
|
|
/) { |
48
|
|
|
|
|
|
|
has $slot => ( |
49
|
|
|
|
|
|
|
is => 'rw', |
50
|
|
|
|
|
|
|
isa => Bool, |
51
|
|
|
|
|
|
|
required => 0, |
52
|
|
|
|
|
|
|
default => quote_sub('1'), |
53
|
|
|
|
|
|
|
); |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
has wake_up => ( |
57
|
|
|
|
|
|
|
is => 'rw', |
58
|
|
|
|
|
|
|
isa => Int, |
59
|
|
|
|
|
|
|
required => 0, |
60
|
|
|
|
|
|
|
default => quote_sub('1'), |
61
|
|
|
|
|
|
|
); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
foreach my $slot (qw/ |
64
|
|
|
|
|
|
|
username |
65
|
|
|
|
|
|
|
password |
66
|
|
|
|
|
|
|
privileged_password |
67
|
|
|
|
|
|
|
/) { |
68
|
|
|
|
|
|
|
has $slot => ( |
69
|
|
|
|
|
|
|
is => 'rw', |
70
|
|
|
|
|
|
|
isa => Str, |
71
|
|
|
|
|
|
|
required => 0, |
72
|
|
|
|
|
|
|
predicate => 1, |
73
|
|
|
|
|
|
|
reader => "get_$slot", |
74
|
|
|
|
|
|
|
writer => "set_$slot", |
75
|
|
|
|
|
|
|
); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
foreach my $slot (qw/ |
79
|
|
|
|
|
|
|
transport |
80
|
|
|
|
|
|
|
personality |
81
|
|
|
|
|
|
|
/) { |
82
|
|
|
|
|
|
|
has $slot => ( |
83
|
|
|
|
|
|
|
is => 'rw', |
84
|
|
|
|
|
|
|
isa => Str, |
85
|
|
|
|
|
|
|
required => 1, |
86
|
|
|
|
|
|
|
); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
foreach my $slot (qw/ |
90
|
|
|
|
|
|
|
host |
91
|
|
|
|
|
|
|
app |
92
|
|
|
|
|
|
|
/) { |
93
|
|
|
|
|
|
|
has $slot => ( |
94
|
|
|
|
|
|
|
is => 'ro', |
95
|
|
|
|
|
|
|
isa => Str, |
96
|
|
|
|
|
|
|
required => 0, |
97
|
|
|
|
|
|
|
predicate => 1, |
98
|
|
|
|
|
|
|
); |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
has 'add_library' => ( |
102
|
|
|
|
|
|
|
is => 'ro', |
103
|
|
|
|
|
|
|
isa => Any, |
104
|
|
|
|
|
|
|
required => 0, |
105
|
|
|
|
|
|
|
predicate => 1, |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
has 'timeout' => ( |
109
|
|
|
|
|
|
|
is => 'ro', |
110
|
|
|
|
|
|
|
isa => Int, |
111
|
|
|
|
|
|
|
required => 0, |
112
|
|
|
|
|
|
|
predicate => 1, |
113
|
|
|
|
|
|
|
); |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
has 'connect_options' => ( |
116
|
|
|
|
|
|
|
is => 'ro', |
117
|
|
|
|
|
|
|
isa => HashRef, |
118
|
|
|
|
|
|
|
required => 0, |
119
|
|
|
|
|
|
|
default => sub { {} }, |
120
|
|
|
|
|
|
|
); |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
has 'nci_options' => ( |
123
|
|
|
|
|
|
|
is => 'ro', |
124
|
|
|
|
|
|
|
isa => HashRef, |
125
|
|
|
|
|
|
|
required => 0, |
126
|
|
|
|
|
|
|
default => sub { {} }, |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
has 'nci' => ( |
130
|
|
|
|
|
|
|
is => 'lazy', |
131
|
|
|
|
|
|
|
isa => InstanceOf['Net::CLI::Interact'], |
132
|
|
|
|
|
|
|
required => 1, |
133
|
|
|
|
|
|
|
predicate => 1, |
134
|
|
|
|
|
|
|
clearer => 1, |
135
|
|
|
|
|
|
|
handles => [qw/ |
136
|
|
|
|
|
|
|
cmd |
137
|
|
|
|
|
|
|
macro |
138
|
|
|
|
|
|
|
last_prompt |
139
|
|
|
|
|
|
|
last_response |
140
|
|
|
|
|
|
|
set_phrasebook |
141
|
|
|
|
|
|
|
set_global_log_at |
142
|
|
|
|
|
|
|
prompt_looks_like |
143
|
|
|
|
|
|
|
find_prompt |
144
|
|
|
|
|
|
|
/], |
145
|
|
|
|
|
|
|
); |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub _build_nci { |
148
|
0
|
|
|
0
|
|
|
my $self = shift; |
149
|
0
|
0
|
|
|
|
|
$self->connect_options->{host} = $self->host |
150
|
|
|
|
|
|
|
if $self->has_host; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
my $nci = Net::CLI::Interact->new({ |
153
|
|
|
|
|
|
|
transport => $self->transport, |
154
|
|
|
|
|
|
|
personality => $self->personality, |
155
|
|
|
|
|
|
|
connect_options => $self->connect_options, |
156
|
|
|
|
|
|
|
($self->has_app ? (app => $self->app) : ()), |
157
|
|
|
|
|
|
|
($self->has_add_library ? (add_library => $self->add_library) : ()), |
158
|
|
|
|
|
|
|
($self->has_timeout ? (timeout => $self->timeout) : ()), |
159
|
0
|
0
|
|
|
|
|
%{ $self->nci_options }, |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
160
|
|
|
|
|
|
|
}); |
161
|
|
|
|
|
|
|
|
162
|
0
|
|
0
|
|
|
|
$nci->logger->log('engine', 'notice', |
163
|
|
|
|
|
|
|
sprintf "NAS loaded, version %s", ($Net::Appliance::Session::VERSION || 'devel')); |
164
|
0
|
|
|
|
|
|
return $nci; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
1; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=pod |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head1 NAME |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Net::Appliance::Session - Run command-line sessions to network appliances |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head1 SYNOPSIS |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
use Net::Appliance::Session; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
my $s = Net::Appliance::Session->new({ |
180
|
|
|
|
|
|
|
personality => 'ios', |
181
|
|
|
|
|
|
|
transport => 'SSH', |
182
|
|
|
|
|
|
|
host => 'hostname.example', |
183
|
|
|
|
|
|
|
privileged_paging => 1, # only if using ASA/PIX OS 7+ |
184
|
|
|
|
|
|
|
# and there are other behaviour options, see below |
185
|
|
|
|
|
|
|
}); |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
try { |
188
|
|
|
|
|
|
|
$s->connect({ username => 'username', password => 'loginpass' }); |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
$s->begin_privileged({ password => 'privilegedpass' }); |
191
|
|
|
|
|
|
|
print $s->cmd('show access-list'); |
192
|
|
|
|
|
|
|
$s->end_privileged; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
catch { |
195
|
|
|
|
|
|
|
warn "failed to execute command: $_"; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
finally { |
198
|
|
|
|
|
|
|
$s->close; |
199
|
|
|
|
|
|
|
}; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
or, try the bundled C<nas> helper script (beta feature!): |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
nas --help |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 DESCRIPTION |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Use this module to establish an interactive command-line session with a |
208
|
|
|
|
|
|
|
network appliance. There is special support for moving into "privileged" mode |
209
|
|
|
|
|
|
|
and "configure" mode, along with the ability to send commands to the connected |
210
|
|
|
|
|
|
|
device and retrieve returned output. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
There are other CPAN modules that cover similar ground, but they are less |
213
|
|
|
|
|
|
|
robust and do not handle native SSH, Telnet and Serial Line connections with a |
214
|
|
|
|
|
|
|
single interface on both Unix and Windows platforms. |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Built-in commands come from a phrasebook which supports many network device |
217
|
|
|
|
|
|
|
vendors (Cisco, HP, etc) or you can install a new phrasebook. Most phases of |
218
|
|
|
|
|
|
|
the connection are configurable for different device behaviours. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head1 METHODS |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
As in the synopsis above, the first step is to create a new instance. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Recommended practice is to wrap all other calls (except C<close()>) in a |
225
|
|
|
|
|
|
|
C<try> block, to catch errors (typically time-outs waiting for CLI response). |
226
|
|
|
|
|
|
|
This module exports the C<try/catch/finally> methods (from L<Try::Tiny>) into |
227
|
|
|
|
|
|
|
your namespace as a simpler alternative to using C<eval()>. |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
For a full demonstration of usage, see the example script shipped with this |
230
|
|
|
|
|
|
|
distribution. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 Net::Appliance::Session->new( \%options ) |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
my $s = Net::Appliance::Session->new({ |
235
|
|
|
|
|
|
|
personality => 'ios', |
236
|
|
|
|
|
|
|
transport => 'SSH', |
237
|
|
|
|
|
|
|
host => 'hostname.example', |
238
|
|
|
|
|
|
|
}); |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
Prepares a new session for you, but will not connect to any device. Some |
241
|
|
|
|
|
|
|
options are required, others optional: |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=over 4 |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item C<< personality => $name >> (required) |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Tells the module which "language" to use when talking to the connected device, |
248
|
|
|
|
|
|
|
for example C<ios> for Cisco IOS devices. There's a list of all the supported |
249
|
|
|
|
|
|
|
platforms in the L<Phrasebook|Net::CLI::Interact::Manual::Phrasebook> |
250
|
|
|
|
|
|
|
documentation. It's also possible to write new phrasebooks. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=item C<< transport => $backend >> (required) |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
The name of the transport backend used for the session, which may be one of |
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>. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=item C<< app => $location >> (required on Windows) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
On Windows platforms, you B<must> download the C<plink.exe> program, and pass |
262
|
|
|
|
|
|
|
its location in this parameter. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=item C<< host => $hostname >> (required for Telnet and SSH transports) |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
When using the Telnet and SSH transports, you B<must> provide the IP or host |
267
|
|
|
|
|
|
|
name of the target device in this parameter. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=item C<< timeout => $seconds >> |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Configures a global default timeout value, in seconds, for interaction with |
272
|
|
|
|
|
|
|
the remote device. The default is 10 seconds. You can also set timeout on a |
273
|
|
|
|
|
|
|
per-command or per-macro call (see below). |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=item C<< connect_options => \%options >> |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
Some of the transport backends can take their own options. For example with a |
278
|
|
|
|
|
|
|
serial line connection you might specify the port speed, etc. See the |
279
|
|
|
|
|
|
|
respective manual pages for each transport backend for further details |
280
|
|
|
|
|
|
|
(L<SSH|Net::CLI::Interact::Transport::SSH>, |
281
|
|
|
|
|
|
|
L<Telnet|Net::CLI::Interact::Transport::Telnet>, |
282
|
|
|
|
|
|
|
L<Serial|Net::CLI::Interact::Transport::Serial>). |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=item C<< add_library => $directory | \@directories >> |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
If you've added to the built-in phrasebook with your own macros, then use |
287
|
|
|
|
|
|
|
this option to load your new phrasebook file(s). The path here should be the |
288
|
|
|
|
|
|
|
directory within which all your personalities are located, such as: |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
${directory}/cisco/ios/pb |
291
|
|
|
|
|
|
|
${directory}/other/device/pb |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Usually the phrasebook files are called "C<pb>" and to the C<personality> |
294
|
|
|
|
|
|
|
option you pass the containing directory name, for example C<ios> or C<device> |
295
|
|
|
|
|
|
|
in the examples shown. See L<Net::CLI::Interact::Manual::Tutorial> for |
296
|
|
|
|
|
|
|
further details. |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=item C<< nci_options => \%options >> |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Should you wish to reconfigure the L<Net::CLI::Interact> instance used inside |
301
|
|
|
|
|
|
|
of C<Net::Appliance::Session>, perhaps for an option not supported above, this |
302
|
|
|
|
|
|
|
generic setting is available. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=back |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head2 connect( \%options ) |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
$s->connect({ username => $myname, password => $mysecret }); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
To establish a connection to the device, and possibly also log in, call this |
311
|
|
|
|
|
|
|
method. Following a successful connection, paging of device output will be |
312
|
|
|
|
|
|
|
disabled using commands appropriate to the platform. This feature can be |
313
|
|
|
|
|
|
|
suppressed (see L</"CONFIGURATION">, below). |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Options available to this method, sometimes required, are: |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=over 4 |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=item C<< username => $name >> |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
The login username for the device. Whether this is required depends both on |
322
|
|
|
|
|
|
|
how the device is configured, and how you have configured this module to act. |
323
|
|
|
|
|
|
|
If it looks like the device presented a Username prompt. and you don't pass |
324
|
|
|
|
|
|
|
the username a Perl exception will be thrown. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
The username is cached within the module for possible use later on when |
327
|
|
|
|
|
|
|
entering "privileged" mode. |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item C<< password => $secret >> |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
The login password for the device. Whether this is required depends both on |
332
|
|
|
|
|
|
|
how the device is configured, and how you have configured this module to act. |
333
|
|
|
|
|
|
|
If it looks like the device presented a Username prompt. and you don't pass |
334
|
|
|
|
|
|
|
the username a Perl exception will be thrown. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
The password is cached within the module for possible use later on when |
337
|
|
|
|
|
|
|
entering "privileged" mode. |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item C<< privileged_password => $secret >> (optional) |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
In the situation where you've activated "privileged paging", yet your device |
342
|
|
|
|
|
|
|
uses a different password for privileged mode than login, you'll need to set |
343
|
|
|
|
|
|
|
that other password here. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Otherwise, because the module tries to disable paging, it first goes into |
346
|
|
|
|
|
|
|
privileged mode as you instructed, and fails with the wrong (login) password. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=back |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head2 begin_privileged and end_privileged |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
$s->begin_privileged; |
353
|
|
|
|
|
|
|
# do some work |
354
|
|
|
|
|
|
|
$s->end_privileged; |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Once you have connected to the device, change to "privileged" mode by calling |
357
|
|
|
|
|
|
|
the C<begin_privileged> method. The appropriate command will be issued for |
358
|
|
|
|
|
|
|
your device platform, from the phrasebook. Likewise to exit "privileged" mode |
359
|
|
|
|
|
|
|
call the C<end_privileged> method. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
Sometimes authentication is required to enter "privileged" mode. In that case, |
362
|
|
|
|
|
|
|
the module defaults to using the username and password first passed in the |
363
|
|
|
|
|
|
|
C<connect> method. However to either override those or set them in case they |
364
|
|
|
|
|
|
|
were not passed to C<connect>, use either or both of the following options to |
365
|
|
|
|
|
|
|
C<begin_privileged>: |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
$s->begin_privileged({ username => $myname, password => $mysecret }); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=head2 begin_configure and end_configure |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
$s->begin_configure; |
372
|
|
|
|
|
|
|
# make some changes |
373
|
|
|
|
|
|
|
$s->end_configure; |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
To enter "configuration" mode for your device platform, call the |
376
|
|
|
|
|
|
|
C<begin_configure> method. This checks you are already in "privileged" mode, |
377
|
|
|
|
|
|
|
as the module assumes this is necessary. If it isn't necessary then see |
378
|
|
|
|
|
|
|
L</"CONFIGURATION"> below to modify this behaviour. Likewise to exit |
379
|
|
|
|
|
|
|
"configure" mode, call the C<end_configure> method. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head2 cmd( $command ) |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
my $config = $s->cmd('show running-config'); |
384
|
|
|
|
|
|
|
my @interfaces = $s->cmd('show interfaces brief'); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
Execute a single command statement on the connected device. The statement is |
387
|
|
|
|
|
|
|
executed verbatim on the device, with a newline appended. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
In scalar context the response is returned as a single string. In list context |
390
|
|
|
|
|
|
|
the gathered response is returned as a list of lines. In both cases your local |
391
|
|
|
|
|
|
|
platform's newline character will end all lines. |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
You can also call the C<last_response> method which returns the same data with |
394
|
|
|
|
|
|
|
the same contextual behaviour. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
This method accepts a hashref of options following the C<$command>, which can |
397
|
|
|
|
|
|
|
include a C<timeout> value to permit long running commands to have all their |
398
|
|
|
|
|
|
|
output gathered. |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
To handle more complicated interactions, for example commands which prompt for |
401
|
|
|
|
|
|
|
confirmation or optional parameters, you should use a Macro. These are set up |
402
|
|
|
|
|
|
|
in the phrasebook and issued via the C<< $s->macro($name) >> method call. See |
403
|
|
|
|
|
|
|
the L<Phrasebook|Net::CLI::Interact::Phrasebook> and |
404
|
|
|
|
|
|
|
L<Cookbook|Net::CLI::Interact::Manual::Cookbook> manual pages for further |
405
|
|
|
|
|
|
|
details. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
If you receive response text with a "mangled" copy of the issued command at |
408
|
|
|
|
|
|
|
the start, then it's likely you need to set the terminal width. This prevents |
409
|
|
|
|
|
|
|
the connected device from line-wrapping long commands. Issue something like: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
$s->begin_privileged; |
412
|
|
|
|
|
|
|
$s->cmd('terminal width 510'); |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=head2 close |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$s->close; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Once you have finished work with the device, call this method. It attempts to |
419
|
|
|
|
|
|
|
back out of any "privileged" or "configuration" mode you've entered, re-enable |
420
|
|
|
|
|
|
|
paging (unless suppressed) and then disconnect. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
If a macro named C<"disconnect"> exists in the loaded phrasebook then it's |
423
|
|
|
|
|
|
|
called just before disconnection. This allows you to issue a command such as |
424
|
|
|
|
|
|
|
C<"exit"> to cleanly log out. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=head1 CONFIGURATION |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Each of the entries below may either be passed as a parameter in the options |
429
|
|
|
|
|
|
|
to the C<new> method, or called as a method in its own right and passed the |
430
|
|
|
|
|
|
|
appropriate setting. If doing the latter, it should be before you call the |
431
|
|
|
|
|
|
|
C<connect> method. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=over |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=item do_login |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
Defaults to true. Pass a zero (false) to disable logging in to the device with |
438
|
|
|
|
|
|
|
a username and password, should you get a command prompt immediately upon |
439
|
|
|
|
|
|
|
connection. |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item do_privileged_mode |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Defaults to true. If on connecting to the device your user is immediately in |
444
|
|
|
|
|
|
|
"privieleged" mode, then set this to zero (false), which permits immediate |
445
|
|
|
|
|
|
|
access to "configure" mode. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=item do_configure_mode |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
Defaults to true. If you set this to zero (false), the module assumes you're |
450
|
|
|
|
|
|
|
in "configure" mode immediately upon entering "privileged" mode. I can't think |
451
|
|
|
|
|
|
|
why this would be useful but you never know. |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item do_paging |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Defaults to true. Pass a zero (false) to disable the post-login |
456
|
|
|
|
|
|
|
reconfiguration of a device which avoids paged command output. If you cleanly |
457
|
|
|
|
|
|
|
C<close> the device connection then paging is re-enabled. Use this option to |
458
|
|
|
|
|
|
|
suppress these steps. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=item privileged_paging |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Defaults to false. On some series of devices, in particular the Cisco ASA and |
463
|
|
|
|
|
|
|
PIXOS7+ you must be in privileged mode in order to alter the pager. If that is |
464
|
|
|
|
|
|
|
the case for your device, call this method with a true value to instruct the |
465
|
|
|
|
|
|
|
module to better manage the situation. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=item pager_enable_lines |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Defaults to 24. The command issued to re-enable paging (on disconnect) |
470
|
|
|
|
|
|
|
typically takes a parameter which is the number of lines per page. If you want |
471
|
|
|
|
|
|
|
a different value, set it in this option. |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=item pager_disable_lines |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Defaults to zero. The command issued to disable paging typically takes a |
476
|
|
|
|
|
|
|
parameter which is the number of lines per page (zero begin to disable |
477
|
|
|
|
|
|
|
paging). If your device uses a different number here, set it in this option. |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=item wake_up |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
When first connecting to the device, the most common scenario is that a |
482
|
|
|
|
|
|
|
Username (or some other) prompt is shown. However if no output is forthcoming |
483
|
|
|
|
|
|
|
and nothing matches, the "enter" key is pressed, in the hope of triggering the |
484
|
|
|
|
|
|
|
display of a new prompt. This is typically most useful on Serial connected |
485
|
|
|
|
|
|
|
devices. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Set this configuration option to zero to suppress this behaviour, or to the |
488
|
|
|
|
|
|
|
number of times "enter" should be pressed and output waited for. The default |
489
|
|
|
|
|
|
|
is to press "enter" once. |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=back |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
=head1 ASYNCHRONOUS BEHAVIOUR |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
The standard, and recommended way to use this module is as above, whereby the |
496
|
|
|
|
|
|
|
application is blocked waiting for command response. It's also possible to |
497
|
|
|
|
|
|
|
send a command, and separately return to ask for output at a later time. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
$s->say('show clock'); |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
This will send the command C<show clock> to the connected device, followed by |
502
|
|
|
|
|
|
|
a newline character. |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
$s->gather(); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
This will gather and return output, with similar behaviour to C<cmd()>, above. |
507
|
|
|
|
|
|
|
That is, it blocks waiting for output and a prompt, will timeout, and accepts |
508
|
|
|
|
|
|
|
the same options. |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
You can still use C<last_response> after calling C<gather>, however be aware |
511
|
|
|
|
|
|
|
that the command (from C<say>) may be echoed at the start of the output, |
512
|
|
|
|
|
|
|
depending on device and connection transport. |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
To see a log of all the processes within this module, and a copy of all data |
517
|
|
|
|
|
|
|
sent to and received from the device, call the following method: |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
$s->set_global_log_at('notice'); |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
In place of C<notice> you can have other log levels (e.g. C<debug> for more, |
522
|
|
|
|
|
|
|
or C<info> for less), and via the embedded |
523
|
|
|
|
|
|
|
L<Logger|Net::CLI::Interact::Logger> at C<< $s->nci->logger >> it's possible |
524
|
|
|
|
|
|
|
to finely control the diagnostics. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 INTERNALS |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
See L<Net::CLI::Interact>. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head1 THANKS |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Over several years I have received many patches and suggestions for |
533
|
|
|
|
|
|
|
improvement from users of this module. My heartfelt thanks to all, for their |
534
|
|
|
|
|
|
|
contributions. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=head1 AUTHOR |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Oliver Gorwits <oliver@cpan.org> |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
This software is copyright (c) 2019 by Oliver Gorwits. |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
545
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=cut |
548
|
|
|
|
|
|
|
|