File Coverage

blib/lib/Net/Autoconfig/Device/Cisco.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             package Net::Autoconfig::Device::Cisco;
2              
3 1     1   31785 use 5.008008;
  1         4  
  1         40  
4 1     1   6 use strict;
  1         1  
  1         49  
5 1     1   12 use warnings;
  1         7  
  1         41  
6              
7 1     1   5 use base "Net::Autoconfig::Device";
  1         2  
  1         1615  
8             use Log::Log4perl qw(:levels);
9             use Expect;
10             use version; our $VERSION = version->new('v1.1.1');
11              
12             #################################################################################
13             ## Constants and Global Variables
14             #################################################################################
15              
16             use constant TRUE => 1;
17             use constant FALSE => 0;
18             use constant LONG_TIMEOUT => 30;
19             use constant MEDIUM_TIMEOUT => 15;
20             use constant SHORT_TIMEOUT => 5;
21              
22             ####################
23             # Expect Command Definitions
24             # These statements are strings, which need to be
25             # eval'ed within the methods to get their
26             # actual values. This provides a way to pre-declare
27             # common expect commands without having to copy-paste
28             # them into each and every method that uses them.
29             # This incurs a performance hit, but I think it's
30             # worth it.
31             #
32             # Yay!
33             ####################
34             my $expect_ssh_key_cmd = '[
35             -re => "continue connecting",
36             sub
37             {
38             $session->clear_accum();
39             $session->send("yes\n"); sleep(1);
40             }
41             ]';
42             my $expect_username_cmd = '[
43             -re => "name:",
44             sub
45             {
46             $session->clear_accum();
47             $session->send($self->username . "\n");
48             sleep(1);
49             }
50             ]';
51             my $expect_password_cmd = '[
52             -re => "word[.:.]",
53             sub
54             {
55             $session->clear_accum();
56             $session->send($self->password . "\n");
57             sleep(1);
58             }
59             ]';
60             my $expect_exec_mode_cmd = '[
61             -re => ">",
62             sub
63             {
64             $session->clear_accum();
65             $session->send("\n");
66             sleep(1);
67             $connected_to_device = TRUE;
68             }
69             ]';
70             my $expect_priv_mode_cmd = '[
71             -re => "#",
72             sub
73             {
74             $session->clear_accum();
75             $session->send("\n");
76             sleep(1);
77             $self->admin_status(TRUE);
78             $connected_to_device = TRUE;
79             }
80             ]';
81             my $expect_enable_cmd = '[
82             -re => ">",
83             sub
84             {
85             $session->clear_accum();
86             $session->send("enable\n");
87             sleep(1);
88             }
89             ]';
90             my $expect_enable_passwd_cmd = '[
91             -re => "[Pp]assword:",
92             sub
93             {
94             $session->clear_accum();
95             $session->send($self->enable_password . "\n");
96             sleep(1);
97             }
98             ]';
99             my $expect_already_enabled_cmd = '[
100             -re => "#",
101             sub
102             {
103             $session->clear_accum();
104             $session->send("\n");
105             sleep(1);
106             $already_enabled = TRUE;
107             }
108             ]';
109             my $expect_disable_paging_cmd = '[
110             -re => "#",
111             sub
112             {
113             $session->clear_accum();
114             $session->send("terminal length 0\n");
115             }
116             ]';
117             my $expect_timeout_cmd = '[
118             timeout =>
119             sub
120             {
121             $command_failed = TRUE;
122             }
123             ]';
124              
125             #################################################################################
126             # Methods
127             #################################################################################
128              
129             ############################################################
130             # Public Methods
131             ############################################################
132              
133             ########################################
134             # new
135             # public method
136             #
137             # create a new Net::Autoconfig::Device::Cisco object.
138             #
139             # If passed an array, it will assume those are key
140             # value pairs and assign them to the device.
141             #
142             # If no values are defined, then default ones are assigned.
143             #
144             # Returns:
145             # A Net::Autoconfig::Device::Cisco object
146             ########################################
147             sub new {
148             my $invocant = shift; # calling class
149             my $class = ref($invocant) || $invocant;
150             my $self = {
151             @_,
152             invalid_cmd_regex => '[iI]nvalid input detected',
153             };
154             my $log = Log::Log4perl->get_logger('Net::Autoconfig');
155              
156             $log->debug("Creating new Net::Autoconfig::Device::Cisco");
157             return bless $self, $class;
158             }
159              
160              
161              
162             ########################################
163             # connect
164             # public method
165             #
166             # overloads the parent method.
167             # Takes into account the ecentricities of
168             # cisco devices.'
169             ########################################
170             sub connect {
171             my $self = shift;
172             my $session; # a ref to the expect session
173             my $access_command; # the string to use to the telnet/ssh app.
174             my $result; # the value returned after executing an expect cmd
175             my @expect_commands; # the commands to run on the device
176             my $spawn_cmd; # command expect uses to connect to the device
177             my $log = Log::Log4perl->get_logger("Net::Autoconfig");
178              
179             # Expect success/failure flags
180             my $connected_to_device; # indicates a successful connection to the device
181             my $command_failed; # indicates a failed connection to the device
182              
183             # Do some sanity checking
184             if (not $self->hostname)
185             {
186             $log->warn("No hostname defined for this device.");
187             return "No hostname defined for this devince.";
188             }
189              
190             if (not $self->access_method)
191             {
192             $log->warn("Access method for " . $self->hostname . " not defined.");
193             return "Access method not defined.";
194             }
195            
196             if (not $self->access_cmd)
197             {
198             $log->warn("Access command for " . $self->hostname . " not defined.");
199             return "Access command not defined";
200             }
201              
202             # Setup the access command
203             if ($self->access_method =~ /^ssh$/)
204             {
205             $spawn_cmd = join(" ", $self->access_cmd, "-l", $self->username, $self->hostname);
206             }
207             else
208             {
209             $spawn_cmd = join(" ", $self->access_cmd, $self->hostname);
210             }
211              
212             # Okay, let's get on with connecting to the device
213             $session = $self->session;
214             if (&_invalid_session($session))
215             {
216             $log->info("Connecting to " . $self->hostname);
217             $log->debug("Using command '" . $self->access_cmd . "'");
218             $log->debug("Spawning new expect session with: '$spawn_cmd'");
219             eval
220             {
221             $session = new Expect;
222             $session->raw_pty(TRUE);
223             $session->spawn($spawn_cmd);
224             };
225             if ($@)
226             {
227             $log->warn("Connecting to " . $self->hostname . " failed: $@");
228             return $@;
229             }
230             }
231             else
232             {
233             $log->info("Session for ". $self->hostname . " already exists.");
234             }
235              
236             # Enable dumping data to the screen.
237             if ($log->is_trace() || $log->is_debug() )
238             {
239             $session->log_stdout(TRUE);
240             }
241             else
242             {
243             $session->log_stdout(FALSE);
244             }
245              
246             ####################
247             # Setup Expect command array
248             #
249             # The commands are defined for the class, but they need
250             # to be eval'ed before we can use them.
251             ####################
252             # Setup the expect commands to do the initial login.
253             # Up to four commands may need to be run:
254             # accept the ssh key
255             # send the username
256             # send the password
257             # verify connection (exec or priv exec mode)
258             ####################
259             push(@expect_commands, [
260             eval $expect_ssh_key_cmd,
261             eval $expect_username_cmd,
262             eval $expect_password_cmd,
263              
264             # Check to see if we already have access
265             eval $expect_exec_mode_cmd,
266             eval $expect_priv_mode_cmd,
267             ]);
268             push(@expect_commands, [
269             eval $expect_username_cmd,
270             eval $expect_password_cmd,
271             eval $expect_exec_mode_cmd,
272             eval $expect_priv_mode_cmd,
273             ]);
274             push(@expect_commands, [
275             eval $expect_password_cmd,
276             eval $expect_exec_mode_cmd,
277             eval $expect_priv_mode_cmd,
278             ]);
279             push(@expect_commands, [
280             eval $expect_exec_mode_cmd,
281             eval $expect_priv_mode_cmd,
282             ]);
283              
284             foreach my $command (@expect_commands)
285             {
286             $session->expect(MEDIUM_TIMEOUT, @$command, eval $expect_timeout_cmd);
287             if ($log->level == $TRACE)
288             {
289             $log->trace("Expect matching before: " . $session->before);
290             $log->trace("Expect matching match : " . $session->match);
291             $log->trace("Expect matching after : " . $session->after);
292             }
293             if ($connected_to_device)
294             {
295             $log->debug("Connected to device " . $self->hostname);
296             $self->session($session);
297             last;
298             }
299             elsif ($command_failed)
300             {
301             $log->warn("Failed to connect to device " . $self->hostname);
302             $log->debug("Failed on command: " , @$command);
303             $session->soft_close();
304             $self->session(undef);
305             last;
306             }
307             }
308              
309             return $connected_to_device ? undef : 'Failed to connect to device.';
310             }
311              
312             ########################################
313             # discover_dev_type
314             # public method
315             #
316             # overloads the parent method.
317             #
318             # Since this is already discovered,
319             # return what type of device it is.
320             ########################################
321             sub discover_dev_type {
322             my $self = shift;
323             return ref($self);
324             }
325              
326             ########################################
327             # get_admin_rights
328             # public method
329             #
330             # overloads the parent method.
331             # Takes into account the ecentricities of
332             # cisco devices.
333             #
334             # Returns:
335             # success = undef
336             # failure = reason for failure (aka a true value)
337             ########################################
338             sub get_admin_rights {
339             my $self = shift;
340             my $password = $self->enable_password;
341             my $log = Log::Log4perl->get_logger("Net::Autoconfig");
342             my $command_failed; # indicates of the command failed.
343             my $already_enabled; # indicates if already in admin mode
344             my @expect_commands; # the commands to run on the device
345             my $session; # the expect session
346              
347             # Do some sanity checking
348             $session = $self->session;
349             if (not $session)
350             {
351             $log->warn("No session defined for get admin rights.");
352             return "No session defined for get admin rights.";
353             }
354              
355             if ($self->admin_status)
356             {
357             $log->debug("Already have admin rights.");
358             return;
359             }
360              
361             $log->debug("Getting admin rights.");
362              
363             ####################
364             # Setup Expect command array
365             #
366             # The commands are defined for the class, but they need
367             # to be eval'ed before we can use them.
368             ####################
369             # Setup the expect commands to get admin rights
370             # send "enable"
371             # send the enable password
372             # verify priv mode
373             ####################
374             push(@expect_commands, [
375             eval $expect_enable_cmd,
376             eval $expect_already_enabled_cmd,
377             ]);
378             push(@expect_commands, [
379             eval $expect_enable_passwd_cmd,
380             ]);
381             push(@expect_commands, [
382             eval $expect_priv_mode_cmd,
383             ]);
384              
385             foreach my $command (@expect_commands)
386             {
387             $session->expect(MEDIUM_TIMEOUT, @$command, eval $expect_timeout_cmd);
388             if ($command_failed)
389             {
390             $log->warn("Command failed.");
391             $log->debug("Failed command(s): " . @$command);
392             $self->admin_status(FALSE);
393             return "Enable command failed.";
394             }
395             elsif ($already_enabled)
396             {
397             $log->info("Already have admin privileges");
398             last;
399             }
400             }
401              
402             $log->debug("Got admin rights");
403             $self->admin_status(TRUE);
404             return;
405             }
406              
407             ########################################
408             # disable_paging
409             # public method
410             #
411             # Disable terminal paging (press -Enter-
412             # to continue) messages. They cause problems
413             # when using expect.
414             #
415             # Returns:
416             # success = undef
417             # failure = reason for failure
418             ########################################
419             sub disable_paging {
420             my $self = shift;
421             my $session; # the object's expect session
422             my $log = Log::Log4perl->get_logger("Net::Autoconfig");
423             my $command_failed; # a flag to indicate if the command failed
424             my @commands; # an array of commands to execute
425              
426             $session = $self->session;
427             if (&_invalid_session($session))
428             {
429             return "Failed - session not defined";
430             }
431              
432             $log->debug("Disabling paging");
433              
434             $session->expect(MEDIUM_TIMEOUT, eval $expect_disable_paging_cmd, eval $expect_timeout_cmd);
435             if ($command_failed)
436             {
437             $log->warn("Failed to disable paging. The rest of the configuration could fail.");
438             return "Failed - paging command timed out";
439             }
440              
441             $session->send("\n");
442              
443             $log->debug("Paging disabled.");
444              
445             return;
446             }
447              
448             ############################################################
449             # Private Methods
450             ############################################################
451              
452              
453             ########################################
454             # _invalid_session
455             # private method
456             #
457             # Determine if this is a valid session.
458             # We're using expect, so it has to be an
459             # expect object reference, and it has to
460             # be defined.
461             #
462             # Returns:
463             # true if invalid
464             # undef if valid
465             ########################################
466             sub _invalid_session {
467             my $session = shift;
468              
469             if (not defined $session)
470             {
471             return TRUE;
472             }
473             elsif (not ref($session))
474             {
475             return TRUE;
476             }
477             elsif (not ref($session) eq 'Expect')
478             {
479             return TRUE;
480             }
481             else
482             {
483             return;
484             }
485             }
486              
487             # Modules must return true.
488             TRUE;
489              
490              
491             __END__