File Coverage

blib/lib/Control/CLI/AvayaData.pm
Criterion Covered Total %
statement 29 1851 1.5
branch 12 1632 0.7
condition 1 464 0.2
subroutine 6 61 9.8
pod 47 47 100.0
total 95 4055 2.3


line stmt bran cond sub pod time code
1             package Control::CLI::AvayaData;
2              
3 1     1   35452 use strict;
  1         4  
  1         54  
4 1     1   11 use warnings;
  1         4  
  1         68  
5 1     1   9 use Exporter qw( import );
  1         10  
  1         58  
6 1     1   8 use Carp;
  1         4  
  1         143  
7 1     1   1332 use Control::CLI qw( :all );
  1         108298  
  1         40101  
8              
9             my $Package = __PACKAGE__;
10             our $VERSION = '2.03';
11             our @ISA = qw(Control::CLI);
12             our %EXPORT_TAGS = (
13             use => [qw(useTelnet useSsh useSerial useIPv6)],
14             prompt => [qw(promptClear promptHide promptCredential)],
15             args => [qw(parseMethodArgs suppressMethodArgs)],
16             coderef => [qw(validCodeRef callCodeRef)],
17             _rest => [qw(passphraseRequired parse_errmode stripLastLine poll)],
18             );
19             push @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS;
20             Exporter::export_ok_tags('all');
21              
22             ########################################### Global Class Variables ###########################################
23              
24             my %LoginPatterns = ( # Patterns to check for during device login (Telnet/Serial) and initial connection to CLI
25             bell => "\x07",
26             banner => 'Enter Ctrl-Y to begin',
27             menu => 'Use arrow keys to highlight option, press or to select option',
28             submenu => 'Press Ctrl-R to return to previous menu. Press Ctrl-C to return to Main Menu',
29             username => 'Enter Username: ',
30             password => "Enter Password: \e",
31             lastlogin => 'Failed retries since last login:',
32             localfail => 'Incorrect',
33             radiusfail => 'Access Denied from RADIUS',
34             radiustimeout1 => 'no response from RADIUS servers',
35             radiustimeout2 => 'No reply from RADIUS server',
36             srbanner => "\((?:Secure Router|VSP4K)",
37             xlrbanner => "\x0d************************************\n",
38             ersbanner => "\x0d* Ethernet Routing Switch",
39             passportbanner => "\x0d* Passport 8",
40             pp1600banner => "\x0d* Passport 16", # The 6 ensures this does not trigger on old Accelar 1x00
41             vspbanner => "All Rights Reserved.\n\x0dVirtual Services Platform",
42             consoleLogMsg1 => "connected via console port", #On serial port: GlobalRouter SW INFO user rwa connected via console port
43             consoleLogMsg2 => "Blocked unauthorized ACLI access",
44             more1 => '----More (q=Quit, space/return=Continue)----',
45             more2 => '--More--',
46             wlan9100banner => 'Avaya Wi-Fi Access Point',
47             );
48             my %Prm = ( # Hash containing list of named parameters returned by attributes
49             bstk => 'BaystackERS',
50             pers => 'PassportERS',
51             xlr => 'Accelar',
52             sr => 'SecureRouter',
53             trpz => 'WLAN2300',
54             xirrus => 'WLAN9100',
55             generic => 'generic',
56             );
57             my %Attribute = (
58             Global => [
59             'family_type',
60             'is_nncli',
61             'is_acli',
62             'model',
63             'sw_version',
64             'fw_version',
65             'slots',
66             'ports',
67             'sysname',
68             'base_mac',
69             'baudrate',
70             'max_baud',
71             ],
72              
73             $Prm{pers} => [
74             'is_voss',
75             'is_apls',
76             'apls_box_type',
77             'brand_name',
78             'is_master_cpu',
79             'is_dual_cpu',
80             'cpu_slot',
81             'is_ha',
82             'stp_mode',
83             'oob_ip',
84             'oob_virt_ip',
85             'oob_standby_ip',
86             'is_oob_connected',
87             ],
88              
89             $Prm{bstk} => [
90             'unit_number',
91             'base_unit',
92             'switch_mode',
93             'stack_size',
94             'stp_mode',
95             'mgmt_vlan',
96             'mgmt_ip',
97             'oob_ip',
98             'is_oob_connected',
99             ],
100              
101             $Prm{xlr} => [
102             'is_master_cpu',
103             'is_dual_cpu',
104             ],
105             );
106              
107             my @InitPromptOrder = ("$Prm{pers}_cli", "$Prm{pers}_nncli", $Prm{bstk}, 'generic');
108             my %InitPrompt = ( # Initial prompt pattern expected at login
109             $Prm{bstk} => '\x0d?([^\n\x0d\x0a]{1,50}?)()(?:\((.+?)\))?[>#]$',
110             "$Prm{pers}_cli" => '\x0d?([^\n\x0d\x0a]+):([1356])((?:\/[\w\d\.-]+)*)[>#] $',
111             "$Prm{pers}_nncli" => '\x0d?([^\n\x0d\x0a]+):([12356])(?:\((.+?)\))?[>#]$',
112             $Prm{xlr} => '\x0d?([^\n\x0d\x0a]+?)()((?:\/[\w\d-]+)*)[>#] $',
113             $Prm{sr} => '\x0d? *\x0d([^\n\x0d\x0a]+?)()((?:\/[\w\d\s-]+(?: \(\d+\/\d+\))?)*)# $',
114             $Prm{trpz} => '([^\n\x0d\x0a]+)[>#] $',
115             $Prm{xirrus} => '(?:\x10\x00)?([^\n\x0d\x0a]+?)()(?:\((.+?)\))?# $',
116             $Prm{generic} => '[^\n\x0d\x0a]*[\?\$%#>]\s?$',
117             );
118             my %Prompt = ( # Prompt pattern templates; SWITCHNAME gets replaced with actual switch prompt during login
119             $Prm{bstk} => 'SWITCHNAME(?:\((.+?)\))?[>#]$',
120             "$Prm{pers}_cli" => 'SWITCHNAME:[1356]((?:\/[\w\d\.-]+)*)[>#] $',
121             "$Prm{pers}_nncli" => 'SWITCHNAME:[12356](?:\((.+?)\))?[>#]$',
122             $Prm{xlr} => 'SWITCHNAME((?:\/[\w\d-]+)*)[>#] $',
123             $Prm{sr} => '\x0d? *\x0dSWITCHNAME((?:\/[\w\d\s-]+(?: \(\d+\/\d+\))?)*)# $',
124             $Prm{trpz} => 'SWITCHNAME[>#] $',
125             $Prm{xirrus} => '(?:\x10\x00)?SWITCHNAME(?:\((.+?)\))?# $',
126             $Prm{generic} => '[^\n\x0d\x0a]*[\?\$%#>]\s?$',
127             );
128             my $LastPromptClense = '^(?:\x0d? *\x0d|\x10\x00)'; # When capturing lastprompt, SecureRouter and Xirrus sometimes precede the prompt with these characters
129              
130             my %MorePrompt = ( # The following characters are automatically backslashed in more_prompt(): ().
131             $Prm{bstk} => '----More (q=Quit, space/return=Continue)----',
132             "$Prm{pers}_cli" => '\n\x0d?--More-- (q = quit) ',
133             "$Prm{pers}_nncli" => '\n\x0d?--More-- (q = quit) |--More--',
134             $Prm{xlr} => '--More-- (q = quit) ',
135             $Prm{sr} => 'Press any key to continue (q : quit) :\x00|Press any key to continue (q : quit \| enter : next line) :\x00',
136             $Prm{trpz} => 'press any key to continue, q to quit.',
137             $Prm{xirrus} => '--MORE--\x10?',
138             $Prm{generic} => '----More (q=Quit, space/return=Continue)----|--More-- (q = quit) |Press any key to continue (q : quit) :\x00|press any key to continue, q to quit.|--MORE--',
139             );
140             my %MorePromptDelay = ( # Only when a possible more prompt can be matched as subset of other more prompt patterns; for these an extra silent read is required
141             $Prm{bstk} => undef,
142             "$Prm{pers}_cli" => undef,
143             "$Prm{pers}_nncli" => '--More--',
144             $Prm{xlr} => undef,
145             $Prm{sr} => undef,
146             $Prm{trpz} => undef,
147             $Prm{xirrus} => undef,
148             $Prm{generic} => '--More--',
149             );
150             our %ErrorPatterns = ( # Patterns which indicated the last command sent generated a syntax error on the host device
151             $Prm{bstk} => '^('
152             . '\s+\^\n.+'
153             . '|% Invalid input detected at \'\^\' marker\.'
154             . '|% Cannot modify settings'
155             . '|% Bad (?:port|unit) number\.'
156             . '|% MLT \d+ does not exist or it is not enabled'
157             . '|% No such VLAN'
158             . '|% Bad VLAN list format\.'
159             . '|% View name does not exist' # snmp-server user admin read-view root write-view root notify-view root
160             . '|% Partial configuration of \'.+?\' already exists\.' # same as above
161             . '|% View already exists, you must first delete it\.' # snmp-server view root 1
162             . '|% User \w+ does not exist' # no snmp-server user admindes
163             . '|% User \'.+?\' already exists' # snmp-server user admin md5 passwdvbn read-view root write-view root notify-view root
164             . ')',
165             $Prm{pers} => '^('
166             . '\x07?\s+\^\n.+'
167             . '|% Invalid input detected at \'\^\' marker\.'
168             . '|.+? not found in path .+'
169             . '|(?:parameter|object) .+? is out of range'
170             . '|\x07?Error ?: .+'
171             . '|Unable to .+'
172             . '|% Not allowed on secondary cpu\.'
173             . '|% Incomplete command\.'
174             . '|% Vrf "[^"]+" does not exist'
175             . '| ERROR: copy failed \(code:0x[\da-fA-F]+\)'
176             . '|Save config to file [^ ]+ failed\.'
177             . '|% Vlan \d+ does not exist'
178             . '|Command not allowed MSTP RSTP mode\.' # On MSTP switch : vlan create 101 type port 1
179             . '|% Permission denied\.' # TACACS commands not allowed
180             . '|% Only (?:gigabit|fast) ethernet ports allowed' # config interface gig on fastEth port or vice-versa
181             . '|There are \d+ releases already on system. Please remove 1 to proceed'
182             . '|can\'t \w+ ".+?" 0x\d+' # delete /flash/.ssh -y : can't remove "/flash/.ssh" 0x300042
183             . '|".+?" is ambiguous in path /' # AccDist3:5#% do
184             . '|Password change aborted\.' # Creating snmpv3 usm user with enh-secure-mode and password does not meet complexity requirements
185             . ')',
186             $Prm{xlr} => '^('
187             . '.+? not found'
188             . '|(?:parameter|object) .+? is out of range'
189             . ')',
190             $Prm{sr} => '^('
191             . '\s+\^\n.+'
192             . '|Error : Command .+? does not exist'
193             . '|Config is locked by some other user'
194             . ')',
195             $Prm{trpz} => '^('
196             . '\s+\^\n.+'
197             . '|Unrecognized command:.+'
198             . '|Unrecognized command in this mode:.+'
199             . ')',
200             $Prm{xirrus} => '^('
201             . '\s+\^\n.+'
202             . ')',
203             );
204             our $CmdConfirmPrompt = '[\(\[] *(?:[yY](?:es)? *(?:[\\\/]|or) *[nN]o?|[nN]o? *(?:[\\\/]|or) *[yY](?:es)?) *[\)\]] *[?:] *$'; # Y/N prompt
205             our $CmdInitiatedPrompt = '[?:=][ \t]*$'; # Prompt for additional user info
206             our $WakeConsole = "\n"; # Sequence to send when connecting to console to wake device
207              
208             my $LoginReadAttempts = 10; # Number of read attempts for readwait() method used in login()
209             my $CmdPromptReadAttempts = 5; # Number of read attempts for readwait() method used in poll_cmd()
210             my $ReadwaitTimer = 100; # Timer to use when calling readwait()
211             my $CmdTimeoutRatio = 0.1; # In cmd() if read times out, a 2nd read is attempted with timeout * this ratio
212              
213             my $Space = " ";
214             my $CTRL_C = "\cC";
215             my $CTRL_U = "\cU";
216             my $CTRL_X = "\cX";
217             my $CTRL_Y = "\cY";
218             my $CTRL_Z = "\cZ";
219              
220             my %Default = ( # Hash of default object seetings which can be modified on a per object basis
221             morePaging => 0, # For --more-- prompt, number of pages accepted before sending q to quit
222             # 0 = accept all pages; 1 = send q after 1st page, i.e. only 1 page; etc
223             progressDots => 0, # After how many bytes received, an activity dot is printed; 0 = disabled
224             return_result => 0, # Whether cmd methods return true/false result or output of command
225             cmd_confirm_prompt => $CmdConfirmPrompt,
226             cmd_initiated_prompt => $CmdInitiatedPrompt,
227             cmd_feed_timeout => 10, # Command requests for data, we have none, after X times we give up
228             wake_console => $WakeConsole,
229             );
230              
231             our @ConstructorArgs = ( @Control::CLI::ConstructorArgs, 'return_result', 'more_prompt', 'more_paging', 'debug_file',
232             'cmd_confirm_prompt', 'cmd_initiated_prompt', 'cmd_feed_timeout', 'console', 'wake_console',
233             );
234              
235             # Debug levels can be set using the debug() method or via debug argument to new() constructor
236             # Debug levels defined:
237             # 0 : No debugging
238             # bit 1 : Control::CLI - Debugging activated for polling methods + readwait() + Win32/Device::SerialPort constructor $quiet flag reset
239             # bit 2 : Control::CLI - Debugging is activated on underlying Net::SSH2 and Win32::SerialPort / Device::SerialPort
240             # bit 4 : Control::CLI::AvayaData - Basic debugging
241             # bit 8 : Control::CLI::AvayaData - Extended debugging of login() & cmd() methods
242              
243              
244             ############################################# Constructors/Destructors #######################################
245              
246             sub new {
247 1     1 1 674 my $pkgsub = "${Package}::new";
248 1         2 my $invocant = shift;
249 1   33     9 my $class = ref($invocant) || $invocant;
250 1         3 my (%args, %cliArgs);
251 1         4 my $debugLevel = $Default{debug};
252 1 50       5 if (@_ == 1) { # Method invoked with just the connection type argument
253 0         0 $cliArgs{use} = shift;
254             }
255             else {
256 1         7 %args = parseMethodArgs($pkgsub, \@_, \@ConstructorArgs);
257 1         112 my @suppressArgs = ('prompt', 'return_result', 'more_prompt', 'more_paging', 'cmd_confirm_prompt',
258             'cmd_initiated_prompt', 'cmd_feed_timeout', 'console', 'wake_console', 'debug_file');
259 1         5 %cliArgs = suppressMethodArgs(\@_, \@suppressArgs);
260             }
261 1 50       56 my $self = $class->SUPER::new(%cliArgs) or return;
262             $self->{$Package} = {
263             # Lower Case ones can be set by user; Upper case ones are set internaly in the class
264             morePaging => $Default{morePaging},
265             progressDots => $Default{progressDots},
266             prompt => undef,
267             prompt_qr => undef,
268             morePrompt => undef,
269             morePrompt_qr => undef,
270             morePromptDelay_qr => undef,
271             last_cmd_success => undef,
272             last_cmd_errmsg => undef,
273             return_result => $Default{return_result},
274             cmd_confirm_prompt => $Default{cmd_confirm_prompt},
275             cmd_confirm_prompt_qr => qr/$Default{cmd_confirm_prompt}/,
276             cmd_initiated_prompt => $Default{cmd_initiated_prompt},
277             cmd_initiated_prompt_qr => qr/$Default{cmd_initiated_prompt}/,
278             cmd_feed_timeout => $Default{cmd_feed_timeout},
279             console => undef,
280             wake_console => $Default{wake_console},
281 1         711 PROMPTTYPE => undef,
282             ENABLEPWD => undef,
283             ORIGBAUDRATE => undef,
284             ATTRIB => undef,
285             ATTRIBFLAG => undef,
286             CONFIGCONTEXT => '',
287             DEBUGLOGFH => undef,
288             };
289 1         7 foreach my $arg (keys %args) { # Accepted arguments on constructor
290 2 50       36 if ($arg eq 'prompt') { $self->prompt($args{$arg}) }
  0 50       0  
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
291 0         0 elsif ($arg eq 'return_result') { $self->return_result($args{$arg}) }
292 0         0 elsif ($arg eq 'more_prompt') { $self->more_prompt($args{$arg}) }
293 0         0 elsif ($arg eq 'more_paging') { $self->more_paging($args{$arg}) }
294 0         0 elsif ($arg eq 'cmd_confirm_prompt') { $self->cmd_confirm_prompt($args{$arg}) }
295 0         0 elsif ($arg eq 'cmd_initiated_prompt') { $self->cmd_initiated_prompt($args{$arg}) }
296 0         0 elsif ($arg eq 'cmd_feed_timeout') { $self->cmd_feed_timeout($args{$arg}) }
297 0         0 elsif ($arg eq 'console') { $self->console($args{$arg}) }
298 0         0 elsif ($arg eq 'wake_console') { $self->wake_console($args{$arg}) }
299 0         0 elsif ($arg eq 'debug_file') { $self->debug_file($args{$arg}) }
300             }
301 1         8 return $self;
302             }
303              
304              
305             # sub DESTROY {} # We don't need to override Control::CLI's destroy method
306              
307              
308             ############################################### Object methods ###############################################
309              
310             sub connect { # All the steps necessary to connect to a CLI session on an Avaya Networking device
311 0     0 1   my $pkgsub = "${Package}::connect";
312 0           my $self = shift;
313 0           my %args;
314 0 0         if (@_ == 1) { # Method invoked in the shorthand form
315 0           $args{host} = shift;
316 0 0         if ($args{host} =~ /^(.+?)\s+(\d+)$/) {
317 0           ($args{host}, $args{port}) = ($1, $2);
318             }
319             }
320             else {
321 0           my @validArgs = ('host', 'port', 'username', 'password', 'publickey', 'privatekey', 'passphrase',
322             'prompt_credentials', 'baudrate', 'parity', 'databits', 'stopbits', 'handshake',
323             'errmode', 'connection_timeout', 'timeout', 'read_attempts', 'wake_console',
324             'return_reference', 'blocking', 'data_with_error', 'terminal_type', 'window_size', 'callback', 'forcebaud');
325 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
326             }
327              
328             # Initialize the base POLL structure
329             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
330             $pkgsub,
331             __PACKAGE__->can('connect_poll'),
332             defined $args{blocking} ? $args{blocking} : $self->{blocking},
333             defined $args{connection_timeout} ? $args{connection_timeout} : $self->{connection_timeout},
334             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
335             1,
336             wantarray,
337             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
338             undef, # n/a
339 0 0         );
    0          
    0          
    0          
340             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
341             # Set method argument keys
342             host => $args{host},
343             port => $args{port},
344             username => $args{username},
345             password => $args{password},
346             publickey => $args{publickey},
347             privatekey => $args{privatekey},
348             passphrase => $args{passphrase},
349             baudrate => $args{baudrate},
350             parity => $args{parity},
351             databits => $args{databits},
352             stopbits => $args{stopbits},
353             handshake => $args{handshake},
354             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
355             terminal_type => $args{terminal_type},
356             window_size => $args{window_size},
357             callback => $args{callback},
358             forcebaud => $args{forcebaud},
359             login_timeout => defined $args{timeout} ? $args{timeout} : $self->{timeout},
360             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
361             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
362             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
363             # Declare method storage keys which will be used
364 0 0         stage => $self->{LOGINSTAGE} ? 1 : 0,
    0          
    0          
    0          
    0          
    0          
365             };
366 0 0 0       if (!$self->{LOGINSTAGE} && $self->{TYPE} ne 'SERIAL' && useIPv6 && defined $args{blocking} && !$args{blocking}) {
      0        
      0        
      0        
367 0           carp "$pkgsub: IO::Socket::IP is required for non-blocking connect";
368             }
369 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
370 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
371 0           return __PACKAGE__->can('poll_connect')->($self, $pkgsub); # Do not call a sub-classed version
372             }
373            
374              
375             sub connect_poll { # Poll status of connection (non-blocking mode)
376 0     0 1   my $pkgsub = "${Package}::connect_poll";
377 0           my $self = shift;
378 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
379              
380 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('connect_poll')) {
381 0           return $self->error("$pkgsub: Method connect() needs to be called first with blocking false");
382             }
383 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
384 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
385 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
386              
387             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
388 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
389              
390             # We get here only if we are not complete: $self->{POLL}{complete} == 0
391 0           return __PACKAGE__->can('poll_connect')->($self, $pkgsub); # Do not call a sub-classed version
392             }
393              
394              
395             sub disconnect { # Perform check on restoring buadrate on device before doing Control::CLI's disconnect
396 0     0 1   my $self = shift;
397 0 0         $self->_restoreDeviceBaudrate if $self->connection_type eq 'SERIAL';
398 0           return $self->SUPER::disconnect(@_);
399             }
400              
401              
402             sub login { # Handles steps necessary to get to CLI session, including menu, banner and Telnet/Serial login
403 0     0 1   my $pkgsub = "${Package}::login";
404 0           my $self =shift;
405 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'return_reference',
406             'read_attempts', 'wake_console', 'blocking', 'data_with_error');
407 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
408              
409             # Initialize the base POLL structure
410             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
411             $pkgsub,
412             __PACKAGE__->can('login_poll'),
413             defined $args{blocking} ? $args{blocking} : $self->{blocking},
414             defined $args{timeout} ? $args{timeout} : $self->{timeout},
415             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
416             1,
417             wantarray,
418             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
419             undef, # n/a
420 0 0         );
    0          
    0          
    0          
421             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
422             # Set method argument keys
423             username => $args{username},
424             password => $args{password},
425             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
426             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
427             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
428             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
429             # Declare method storage keys which will be used
430 0 0         stage => 0,
    0          
    0          
    0          
431             login_attempted => undef,
432             password_sent => undef,
433             login_error => '',
434             family_type => undef,
435             cpu_slot => undef,
436             detectionFromPrompt => undef,
437             };
438 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
439 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
440 0           return __PACKAGE__->can('poll_login')->($self, $pkgsub); # Do not call a sub-classed version
441             }
442              
443              
444             sub login_poll { # Poll status of login (non-blocking mode)
445 0     0 1   my $pkgsub = "${Package}::login_poll";
446 0           my $self = shift;
447 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
448              
449 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('login_poll')) {
450 0           return $self->error("$pkgsub: Method login() needs to be called first with blocking false");
451             }
452 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
453 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
454 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
455              
456             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
457 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
458              
459             # We get here only if we are not complete: $self->{POLL}{complete} == 0
460 0           return __PACKAGE__->can('poll_login')->($self, $pkgsub); # Do not call a sub-classed version
461             }
462              
463              
464             sub cmd { # Sends a CLI command to host and returns result or output data
465 0     0 1   my $pkgsub = "${Package}::cmd";
466 0           my $self = shift;
467 0           my %args;
468 0 0         if (@_ == 1) { # Method invoked with just the command argument
469 0           $args{command} = shift;
470             }
471             else {
472 0           my @validArgs = ('command', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_confirm_prompt', 'more_pages',
473             'timeout', 'errmode', 'return_reference', 'return_result', 'progress_dots', 'blocking', 'poll_syntax');
474 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
475             }
476 0 0         $args{command} = '' unless defined $args{command};
477              
478             # Initialize the base POLL structure
479             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
480             $pkgsub,
481             __PACKAGE__->can('cmd_poll'),
482             defined $args{blocking} ? $args{blocking} : $self->{blocking},
483             defined $args{timeout} ? $args{timeout} : $self->{timeout},
484             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
485             (defined $args{return_result} ? $args{return_result} : $self->{$Package}{return_result}) ? 2 : 1,
486             undef, # This is set below
487             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
488             undef, # n/a
489 0 0         );
    0          
    0          
    0          
    0          
    0          
490             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
491             # Set method argument keys
492             command => $args{command},
493             prompt => defined $args{prompt} ? $args{prompt} : $self->{$Package}{prompt_qr},
494             more_prompt => defined $args{more_prompt} ? $args{more_prompt} : $self->{$Package}{morePrompt_qr},
495             more_prompt_delay => defined $args{more_prompt} ? undef : $self->{$Package}{morePromptDelay_qr},
496             more_pages => defined $args{more_pages} ? $args{more_pages} : $self->{$Package}{morePaging},
497             reset_prompt => $args{reset_prompt} && defined $self->{$Package}{PROMPTTYPE},
498             yn_prompt => defined $args{cmd_confirm_prompt} ? $args{cmd_confirm_prompt} : $self->{$Package}{cmd_confirm_prompt_qr},
499             cmd_prompt => undef,
500             feed_data => undef,
501             progress_dots => defined $args{progress_dots} ? $args{progress_dots} : $self->{$Package}{progressDots},
502             # Declare method storage keys which will be used
503             stage => 0,
504             lastLine => '',
505             outputNewline => '',
506             progress => undef,
507             alreadyCmdTimeout => 0,
508             ynPromptCount => undef,
509             cmdPromptCount => undef,
510             cmdEchoRemoved => 0,
511             lastPromptEchoedCmd => undef,
512             cache_timeout => $self->{POLL}{timeout},
513 0 0 0       };
    0          
    0          
    0          
    0          
    0          
514 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
515 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
516 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
517              
518 0           my ($ok, $output) = $self->poll_cmd($pkgsub);
519             # We have a different syntax for scalar output in blocking and non-blocking modes
520 0 0         if ($args{poll_syntax}) { # New syntax
521 0 0         return wantarray ? ($ok, $output) : $ok;
522             }
523             else { # Old syntax
524 0 0         return wantarray ? ($ok, $output) : $output;
525             }
526             }
527              
528              
529             sub cmd_prompted { # Sends a CLI command to host, feed additional data and return any output
530 0     0 1   my $pkgsub = "${Package}::cmd_prompted";
531 0           my $pollsub = "${Package}::cmd";
532 0           my $self = shift;
533 0           my ($cmd, @feedData, $errmode, $reset_prompt, $pollSyntax);
534 0           my $morePages = $self->{$Package}{morePaging};
535 0           my $progressDots = $self->{$Package}{progressDots};
536 0           my $timeout = $self->{timeout};
537 0           my $blocking = $self->{blocking};
538 0           my $returnRef = $self->{return_reference};
539 0           my $returnRes = $self->{$Package}{return_result};
540 0           my $prompt = $self->{$Package}{prompt_qr};
541 0           my $morePrompt = $self->{$Package}{morePrompt_qr};
542 0           my $morePromptDelay = $self->{$Package}{morePromptDelay_qr};
543 0           my $cmdPrompt = $self->{$Package}{cmd_initiated_prompt_qr};
544 0 0 0       if (lc($_[0]) ne 'command' && lc($_[0]) ne 'poll_syntax') { # No command or poll_syntax argument, assume list form
545 0           $cmd = shift;
546 0           @feedData = @_;
547             }
548             else { # Method invoked with multiple arguments form
549 0           my @validArgs = ('command', 'feed', 'feed_list', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_initiated_prompt', 'more_pages',
550             'timeout', 'errmode', 'return_reference', 'return_result', 'progress_dots', 'blocking', 'poll_syntax');
551 0           my @args = parseMethodArgs($pkgsub, \@_, \@validArgs);
552 0           for (my $i = 0; $i < $#args; $i += 2) {
553 0 0         $cmd = $args[$i + 1] if $args[$i] eq 'command';
554 0 0         push @feedData, $args[$i + 1] if $args[$i] eq 'feed';
555 0 0 0       push @feedData, @{$args[$i + 1]} if $args[$i] eq 'feed_list' && ref($args[$i + 1]) eq "ARRAY";
  0            
556 0 0         $prompt = $args[$i + 1] if $args[$i] eq 'prompt';
557 0 0         $morePages = $args[$i + 1] if $args[$i] eq 'more_pages';
558 0 0         $timeout = $args[$i + 1] if $args[$i] eq 'timeout';
559 0 0         $blocking = $args[$i + 1] if $args[$i] eq 'blocking';
560 0 0         $returnRef = $args[$i + 1] if $args[$i] eq 'return_reference';
561 0 0         $returnRes = $args[$i + 1] if $args[$i] eq 'return_result';
562 0 0         $reset_prompt = $args[$i + 1] if $args[$i] eq 'reset_prompt';
563 0 0         ($morePrompt, $morePromptDelay) = ($args[$i + 1], undef) if $args[$i] eq 'more_prompt';
564 0 0         $progressDots = $args[$i + 1] if $args[$i] eq 'progress_dots';
565 0 0         $cmdPrompt = $args[$i + 1] if $args[$i] eq 'cmd_initiated_prompt';
566 0 0         $errmode = parse_errmode($pkgsub, $args[$i + 1]) if $args[$i] eq 'errmode';
567 0 0         $pollSyntax = $args[$i + 1] if $args[$i] eq 'poll_syntax';
568             }
569             }
570 0 0         $cmd = '' unless defined $cmd;
571              
572             # Initialize the base POLL structure
573 0 0 0       $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
574             $pollsub,
575             __PACKAGE__->can('cmd_poll'),
576             $blocking,
577             $timeout,
578             $errmode,
579             $returnRes ? 2 : 1,
580             $blocking || wantarray, # Always true in blocking mode; if wantarray otherwise
581             $returnRef,
582             undef, # n/a
583             );
584             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
585             # Set method argument keys
586             command => $cmd,
587             prompt => $prompt,
588             more_prompt => $morePrompt,
589             more_prompt_delay => $morePromptDelay,
590             more_pages => $morePages,
591             reset_prompt => $reset_prompt && defined $self->{$Package}{PROMPTTYPE},
592             yn_prompt => undef,
593             cmd_prompt => $cmdPrompt,
594             feed_data => \@feedData,
595             progress_dots => $progressDots,
596             # Declare method storage keys which will be used
597             stage => 0,
598             lastLine => '',
599             outputNewline => '',
600             progress => undef,
601             alreadyCmdTimeout => 0,
602             ynPromptCount => undef,
603             cmdPromptCount => undef,
604             cmdEchoRemoved => 0,
605             lastPromptEchoedCmd => undef,
606             cache_timeout => $self->{POLL}{timeout},
607 0   0       };
608 0   0       $self->{POLL}{output_requested} = !$pollSyntax || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
609 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
610 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
611              
612 0           my ($ok, $output) = __PACKAGE__->can('poll_cmd')->($self, $pkgsub); # Do not call a sub-classed version
613             # We have a different syntax for scalar output in blocking and non-blocking modes
614 0 0         if ($pollSyntax) { # New syntax
615 0 0         return wantarray ? ($ok, $output) : $ok;
616             }
617             else { # Old syntax
618 0 0         return wantarray ? ($ok, $output) : $output;
619             }
620             }
621              
622              
623             sub cmd_poll { # Poll status of cmd (non-blocking mode)
624 0     0 1   my $pkgsub = "${Package}::cmd_poll";
625 0           my $self = shift;
626 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
627              
628 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('cmd_poll')) {
629 0           return $self->error("$pkgsub: Method cmd() needs to be called first with blocking false");
630             }
631 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
632 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
633 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
634              
635             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
636 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
637              
638             # We get here only if we are not complete: $self->{POLL}{complete} == 0
639 0           return __PACKAGE__->can('poll_cmd')->($self, $pkgsub); # Do not call a sub-classed version
640             }
641              
642              
643             sub attribute { # Read attributes for host device
644 0     0 1   my $pkgsub = "${Package}::attribute";
645 0           my $self = shift;
646 0           my %args;
647 0 0         if (@_ == 1) { # Method invoked with just the command argument
648 0           $args{attribute} = shift;
649             }
650             else {
651 0           my @validArgs = ('attribute', 'reload', 'blocking', 'timeout', 'errmode', 'poll_syntax');
652 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
653             }
654              
655             # Initialize the base POLL structure
656             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
657             $pkgsub,
658             __PACKAGE__->can('attribute_poll'),
659             defined $args{blocking} ? $args{blocking} : $self->{blocking},
660             defined $args{timeout} ? $args{timeout} : $self->{timeout},
661 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
662             2,
663             undef, # This is set below
664             0,
665             undef, # n/a
666             );
667             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
668             # Set method argument keys
669             attribute => $args{attribute},
670             reload => $args{reload},
671             # Declare method storage keys which will be used
672 0           stage => 0,
673             debugMsg => 0,
674             };
675 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
676 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
677 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
678              
679 0           my ($ok, $attribute) = __PACKAGE__->can('poll_attribute')->($self, $pkgsub); # Do not call a sub-classed version
680             # We have a different syntax for scalar output in blocking and non-blocking modes
681 0 0         if ($args{poll_syntax}) { # New syntax
682 0 0         return wantarray ? ($ok, $attribute) : $ok;
683             }
684             else { # Old syntax
685 0 0         return wantarray ? ($ok, $attribute) : $attribute;
686             }
687             }
688              
689              
690             sub attribute_poll { # Poll status of attribute (non-blocking mode)
691 0     0 1   my $pkgsub = "${Package}::attribute_poll";
692 0           my $self = shift;
693 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
694              
695 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('attribute_poll')) {
696 0           return $self->error("$pkgsub: Method attribute() needs to be called first with blocking false");
697             }
698 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
699 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
700 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
701              
702             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
703 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
704              
705             # We get here only if we are not complete: $self->{POLL}{complete} == 0
706 0           return __PACKAGE__->can('poll_attribute')->($self, $pkgsub); # Do not call a sub-classed version
707             }
708              
709              
710             sub change_baudrate { # Change baud rate on device and on current connection, if serial
711 0     0 1   my $pkgsub = "${Package}::change_baudrate";
712 0           my $self = shift;
713 0           my (%args);
714 0 0         if (@_ == 1) { # Method invoked with just the command argument
715 0           $args{baudrate} = shift;
716             }
717             else {
718 0           my @validArgs = ('baudrate', 'parity', 'databits', 'stopbits', 'handshake', 'forcebaud',
719             'timeout', 'errmode', 'local_side_only', 'blocking', 'poll_syntax');
720 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
721             }
722              
723             # Initialize the base POLL structure
724             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
725             $pkgsub,
726             __PACKAGE__->can('change_baudrate_poll'),
727             defined $args{blocking} ? $args{blocking} : $self->{blocking},
728             defined $args{timeout} ? $args{timeout} : $self->{timeout},
729 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
730             2,
731             undef, # This is set below
732             0,
733             undef, # n/a
734             );
735             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
736             # Set method argument keys
737             baudrate => $args{baudrate},
738             parity => $args{parity},
739             databits => $args{databits},
740             stopbits => $args{stopbits},
741             handshake => $args{handshake},
742             forcebaud => $args{forcebaud},
743             local_side_only => $args{local_side_only},
744             # Declare method storage keys which will be used
745             stage => 0,
746             userExec => undef,
747             privExec => undef,
748 0 0         maxMode => $args{baudrate} eq 'max' ? 1:0,
749             };
750 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
751 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
752 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
753              
754 0           my ($ok, $baudrate) = __PACKAGE__->can('poll_change_baudrate')->($self, $pkgsub); # Do not call a sub-classed version
755             # We have a different syntax for scalar output in blocking and non-blocking modes
756 0 0         if ($args{poll_syntax}) { # New syntax
757 0 0         return wantarray ? ($ok, $baudrate) : $ok;
758             }
759             else { # Old syntax
760 0 0         return wantarray ? ($ok, $baudrate) : $baudrate;
761             }
762             }
763              
764              
765             sub change_baudrate_poll { # Poll status of change_baudrate (non-blocking mode)
766 0     0 1   my $pkgsub = "${Package}::change_baudrate_poll";
767 0           my $self = shift;
768 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
769              
770 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('change_baudrate_poll')) {
771 0           return $self->error("$pkgsub: Method change_baudrate() needs to be called first with blocking false");
772             }
773 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
774 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
775 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
776              
777             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
778 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
779              
780             # We get here only if we are not complete: $self->{POLL}{complete} == 0
781 0           return __PACKAGE__->can('poll_change_baudrate')->($self, $pkgsub); # Do not call a sub-classed version
782             }
783              
784              
785             sub enable { # Enter PrivExec mode (handle enable password for WLAN2300)
786 0     0 1   my $pkgsub = "${Package}::enable";
787 0           my $self = shift;
788 0           my %args;
789 0 0         if (@_ == 1) { # Method invoked with just the command argument
790 0           $args{password} = shift;
791             }
792             else {
793 0           my @validArgs = ('password', 'prompt_credentials', 'timeout', 'errmode', 'blocking');
794 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
795             }
796              
797             # Initialize the base POLL structure
798             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
799             $pkgsub,
800             __PACKAGE__->can('enable_poll'),
801             defined $args{blocking} ? $args{blocking} : $self->{blocking},
802             defined $args{timeout} ? $args{timeout} : $self->{timeout},
803 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
804             0, # no output
805             0, # no output
806             undef, # n/a
807             undef, # n/a
808             );
809             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
810             # Set method argument keys
811             enable_password => defined $args{password} ? $args{password} : $self->{$Package}{ENABLEPWD},
812             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
813             # Declare method storage keys which will be used
814 0 0         stage => 0,
    0          
815             login_attempted => undef,
816             password_sent => undef,
817             login_failed => undef,
818             };
819 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
820 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
821 0           return __PACKAGE__->can('poll_enable')->($self, $pkgsub); # Do not call a sub-classed version
822             }
823              
824              
825             sub enable_poll { # Poll status of enable (non-blocking mode)
826 0     0 1   my $pkgsub = "${Package}::enable_poll";
827 0           my $self = shift;
828 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
829              
830 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('enable_poll')) {
831 0           return $self->error("$pkgsub: Method enable() needs to be called first with blocking false");
832             }
833 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
834 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
835              
836             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
837 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
838              
839             # We get here only if we are not complete: $self->{POLL}{complete} == 0
840 0           return __PACKAGE__->can('poll_enable')->($self, $pkgsub); # Do not call a sub-classed version
841             }
842              
843              
844             sub device_more_paging { # Enable/Disable more paging on host device
845 0     0 1   my $pkgsub = "${Package}::device_more_paging";
846 0           my $self = shift;
847 0           my (%args, $familyType);
848 0 0         if (@_ == 1) { # Method invoked with just the command argument
849 0           $args{enable} = shift;
850             }
851             else {
852 0           my @validArgs = ('enable', 'timeout', 'errmode', 'blocking');
853 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
854             }
855              
856             # Initialize the base POLL structure
857             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
858             $pkgsub,
859             __PACKAGE__->can('device_more_paging_poll'),
860             defined $args{blocking} ? $args{blocking} : $self->{blocking},
861             defined $args{timeout} ? $args{timeout} : $self->{timeout},
862 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
863             0, # no output
864             0, # no output
865             undef, # n/a
866             undef, # n/a
867             );
868             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
869             # Set method argument keys
870             enable => $args{enable},
871             # Declare method storage keys which will be used
872 0           stage => 0,
873             cmdString => undef,
874             };
875 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
876 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
877 0           return __PACKAGE__->can('poll_device_more_paging')->($self, $pkgsub); # Do not call a sub-classed version
878             }
879              
880              
881             sub device_more_paging_poll { # Poll status of device_more_paging (non-blocking mode)
882 0     0 1   my $pkgsub = "${Package}::device_more_paging_poll";
883 0           my $self = shift;
884 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
885              
886 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('device_more_paging_poll')) {
887 0           return $self->error("$pkgsub: Method device_more_paging() needs to be called first with blocking false");
888             }
889 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
890 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
891              
892             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
893 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
894              
895             # We get here only if we are not complete: $self->{POLL}{complete} == 0
896 0           return __PACKAGE__->can('poll_device_more_paging')->($self, $pkgsub); # Do not call a sub-classed version
897             }
898              
899              
900             sub device_peer_cpu { # Connect to peer CPU on ERS8x00 / VSP9000
901 0     0 1   my $pkgsub = "${Package}::device_peer_cpu";
902 0           my $self = shift;
903 0           my $familyType;
904 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'blocking');
905 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
906              
907             # Initialize the base POLL structure
908             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
909             $pkgsub,
910             __PACKAGE__->can('device_peer_cpu_poll'),
911             defined $args{blocking} ? $args{blocking} : $self->{blocking},
912             defined $args{timeout} ? $args{timeout} : $self->{timeout},
913 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
914             0, # no output
915             0, # no output
916             undef, # n/a
917             undef, # n/a
918             );
919             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
920             # Set method argument keys
921             username => defined $args{username} ? $args{username} : $self->username,
922             password => defined $args{password} ? $args{password} : $self->password,
923             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
924             # Declare method storage keys which will be used
925 0 0         stage => 0,
    0          
    0          
926             };
927 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
928 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
929 0           return __PACKAGE__->can('poll_device_peer_cpu')->($self, $pkgsub); # Do not call a sub-classed version
930             }
931              
932              
933             sub device_peer_cpu_poll { # Poll status of device_peer_cpu (non-blocking mode)
934 0     0 1   my $pkgsub = "${Package}::device_peer_cpu_poll";
935 0           my $self = shift;
936 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
937              
938 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('device_peer_cpu_poll')) {
939 0           return $self->error("$pkgsub: Method device_peer_cpu() needs to be called first with blocking false");
940             }
941 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
942 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
943              
944             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
945 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
946              
947             # We get here only if we are not complete: $self->{POLL}{complete} == 0
948 0           return __PACKAGE__->can('poll_device_peer_cpu')->($self, $pkgsub); # Do not call a sub-classed version
949             }
950              
951              
952             sub debug_file { # Set debug output file
953 0     0 1   my ($self, $fh) = @_;
954 0           my $pkgsub = "${Package}::debug_file";
955              
956 0 0         unless (defined $fh) { # No input = return current filehandle
957 0           return $self->{$Package}{DEBUGLOGFH};
958             }
959 0 0 0       unless (ref $fh or length $fh) { # Empty input = stop logging
960 0           $self->{$Package}{DEBUGLOGFH} = undef;
961 0           return;
962             }
963 0 0 0       if (!ref($fh) && !defined(fileno $fh)) { # Open a new filehandle if input is a filename
964 0           my $logfile = $fh;
965 0           $fh = IO::Handle->new;
966 0 0         open($fh, '>', "$logfile") or return $self->error("$pkgsub: Unable to open output log file: $!");
967             }
968 0           $fh->autoflush();
969 0           $self->{$Package}{DEBUGLOGFH} = $fh;
970 0           return $fh;
971             }
972              
973              
974             #################################### Methods to set/read Object variables ####################################
975              
976             sub flush_credentials { # Clear the stored username, password, passphrases, and enable password, if any
977 0     0 1   my $self = shift;
978 0           $self->SUPER::flush_credentials;
979 0           $self->{$Package}{ENABLEPWD} = undef;
980 0           return 1;
981             }
982              
983              
984             sub prompt { # Read/Set object prompt
985 0     0 1   my ($self, $newSetting) = @_;
986 0           my $currentSetting = $self->{$Package}{prompt};
987 0 0         if (defined $newSetting) {
988 0           $self->debugMsg(4, "\nPrompt Regex set to:\n$newSetting\n");
989 0           $self->{$Package}{prompt} = $newSetting;
990 0           $self->{$Package}{prompt_qr} = qr/$newSetting/;
991             }
992 0           return $currentSetting;
993             }
994              
995              
996             sub more_prompt { # Read/Set object more prompt
997 0     0 1   my ($self, $newSetting, $delayPrompt) = @_;
998 0           my $currentSetting = $self->{$Package}{morePrompt};
999 0 0         if (defined $newSetting) {
1000 0           $newSetting =~ s/([\(\)\.])/\\$1/g;
1001 0           $self->debugMsg(4, "More Prompt Regex set to:\n$newSetting\n");
1002 0           $self->{$Package}{morePrompt} = $newSetting;
1003 0 0         $self->{$Package}{morePrompt_qr} = $newSetting ? qr/$newSetting/ : undef;
1004 0 0         $self->{$Package}{morePromptDelay_qr} = $delayPrompt ? qr/$delayPrompt/ : undef;
1005             }
1006 0           return $currentSetting;
1007             }
1008              
1009              
1010             sub more_paging { # Set the number of pages to read in the resence of --more-- prompts from host
1011 0     0 1   my ($self, $newSetting) = @_;
1012 0           my $currentSetting = $self->{$Package}{morePaging};
1013 0 0         $self->{$Package}{morePaging} = $newSetting if defined $newSetting;
1014 0           return $currentSetting;
1015             }
1016              
1017              
1018             sub progress_dots { # Enable/disable activity dots
1019 0     0 1   my ($self, $newSetting) = @_;
1020 0           my $currentSetting = $self->{$Package}{progressDots};
1021 0 0         $self->{$Package}{progressDots} = $newSetting if defined $newSetting;
1022 0           return $currentSetting;
1023             }
1024              
1025              
1026             sub return_result { # Set/read return_result mode
1027 0     0 1   my ($self, $newSetting) = @_;
1028 0           my $currentSetting = $self->{$Package}{return_result};
1029 0 0         $self->{$Package}{return_result} = $newSetting if defined $newSetting;
1030 0           return $currentSetting;
1031             }
1032              
1033              
1034             sub cmd_confirm_prompt { # Read/Set object cmd_confirm_prompt prompt
1035 0     0 1   my ($self, $newSetting) = @_;
1036 0           my $currentSetting = $self->{$Package}{cmd_confirm_prompt};
1037 0 0         if (defined $newSetting) {
1038 0           $self->{$Package}{cmd_confirm_prompt} = $newSetting;
1039 0           $self->{$Package}{cmd_confirm_prompt_qr} = qr/$newSetting/;
1040             }
1041 0           return $currentSetting;
1042             }
1043              
1044              
1045             sub cmd_initiated_prompt { # Read/Set object cmd_initiated_prompt prompt
1046 0     0 1   my ($self, $newSetting) = @_;
1047 0           my $currentSetting = $self->{$Package}{cmd_initiated_prompt};
1048 0 0         if (defined $newSetting) {
1049 0           $self->{$Package}{cmd_initiated_prompt} = $newSetting;
1050 0           $self->{$Package}{cmd_initiated_prompt_qr} = qr/$newSetting/;
1051             }
1052 0           return $currentSetting;
1053             }
1054              
1055              
1056             sub cmd_feed_timeout { # Read/Set object value of cmd_feed_timeout
1057 0     0 1   my ($self, $newSetting) = @_;
1058 0           my $currentSetting = $self->{$Package}{cmd_feed_timeout};
1059 0 0         $self->{$Package}{cmd_feed_timeout} = $newSetting if defined $newSetting;
1060 0           return $currentSetting;
1061             }
1062              
1063              
1064             sub console { # Read/Set value of console
1065 0     0 1   my ($self, $newSetting) = @_;
1066 0           my $currentSetting = $self->{$Package}{console};
1067 0 0         $self->{$Package}{console} = $newSetting if defined $newSetting;
1068 0           return $currentSetting;
1069             }
1070              
1071              
1072             sub wake_console { # Read/Set object value of wake_console
1073 0     0 1   my ($self, $newSetting) = @_;
1074 0           my $currentSetting = $self->{$Package}{wake_console};
1075 0 0         $self->{$Package}{wake_console} = $newSetting if defined $newSetting;
1076 0           return $currentSetting;
1077             }
1078              
1079              
1080             sub last_cmd_success { # Return the result of the last command sent via cmd methods
1081 0     0 1   my ($self, $newSetting) = @_;
1082 0           my $currentSetting = $self->{$Package}{last_cmd_success};
1083 0 0         $self->{$Package}{last_cmd_success} = $newSetting if defined $newSetting;
1084 0           return $currentSetting;
1085             }
1086              
1087              
1088             sub last_cmd_errmsg { # Set/read the last generated error message from host
1089 0     0 1   my ($self, $newSetting) = @_;
1090 0           my $currentSetting = $self->{$Package}{last_cmd_errmsg};
1091 0 0         $self->{$Package}{last_cmd_errmsg} = $newSetting if defined $newSetting;
1092 0           return $currentSetting;
1093             }
1094              
1095              
1096             ################################# Methods to read read-only Object variables #################################
1097              
1098             sub config_context { # Return the configuration context contained in the last prompt
1099 0     0 1   my $self = shift;
1100 0           return $self->{$Package}{CONFIGCONTEXT};
1101             }
1102              
1103              
1104             sub enable_password { # Read the enable password (WLAN2300)
1105 0     0 1   my $self = shift;
1106 0           return $self->{$Package}{ENABLEPWD};
1107             }
1108              
1109              
1110             #################################### Private poll methods for sub classes ####################################
1111              
1112             sub poll_connect { # Internal method to connect to host and perform login (used for both blocking & non-blocking modes)
1113 0     0 1   my $self = shift;
1114 0           my $pkgsub = shift;
1115 0           my $pollsub = "${Package}::connect";
1116              
1117 0 0         unless ($self->{POLLING}) { # Sanity check
1118 0           my (undef, $fileName, $lineNumber) = caller;
1119 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1120             }
1121              
1122 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1123 0           my @validArgs = ('host', 'port', 'username', 'password', 'publickey', 'privatekey', 'passphrase',
1124             'prompt_credentials', 'baudrate', 'parity', 'databits', 'stopbits', 'handshake',
1125             'errmode', 'connection_timeout', 'login_timeout', 'read_attempts', 'wake_console',
1126             'data_with_error', 'terminal_type', 'window_size', 'callback', 'forcebaud');
1127 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1128 0 0 0       if (@_ && !%args) { # Legacy syntax
1129             ($args{host}, $args{port}, $args{username}, $args{password}, $args{publickey}, $args{privatekey},
1130             $args{passphrase}, $args{baudrate}, $args{parity}, $args{databits}, $args{stopbits},
1131             $args{handshake}, $args{prompt_credentials}, $args{read_attempts}, $args{wake_console},
1132 0           $args{connection_timeout}, $args{login_timeout}, $args{errmode}) = @_;
1133             }
1134             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1135             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1136             # Set method argument keys
1137             host => $args{host},
1138             port => $args{port},
1139             username => defined $args{username} ? $args{username} : $self->{USERNAME},
1140             password => defined $args{password} ? $args{password} : $self->{PASSWORD},
1141             publickey => $args{publickey},
1142             privatekey => $args{privatekey},
1143             passphrase => defined $args{passphrase} ? $args{passphrase} : $self->{PASSPHRASE},
1144             baudrate => $args{baudrate},
1145             parity => $args{parity},
1146             databits => $args{databits},
1147             stopbits => $args{stopbits},
1148             handshake => $args{handshake},
1149             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
1150             terminal_type => $args{terminal_type},
1151             window_size => $args{window_size},
1152             callback => $args{callback},
1153             forcebaud => $args{forcebaud},
1154             login_timeout => defined $args{login_timeout} ? $args{login_timeout} : $self->{timeout},
1155             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
1156             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
1157             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
1158             # Declare method storage keys which will be used
1159             stage => $self->{LOGINSTAGE} ? 1 : 0,
1160             # Declare keys to be set if method called from another polled method
1161             errmode => $args{errmode},
1162 0 0         };
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1163             # Cache poll structure keys which this method will use
1164 0           $self->poll_struct_cache($pollsub, $args{connection_timeout});
1165             }
1166 0           my $connect = $self->{POLL}{$pollsub};
1167 0 0         local $self->{errmode} = $connect->{errmode} if defined $connect->{errmode};
1168              
1169 0 0         if ($connect->{stage} < 1) { # Connect stage
1170             my $ok = $self->SUPER::poll_connect($pkgsub,
1171             Host => $connect->{host},
1172             Port => $connect->{port},
1173             Username => $connect->{username},
1174             Password => $connect->{password},
1175             PublicKey => $connect->{publickey},
1176             PrivateKey => $connect->{privatekey},
1177             Passphrase => $connect->{passphrase},
1178             BaudRate => $connect->{baudrate},
1179             ForceBaud => $connect->{forcebaud},
1180             Parity => $connect->{parity},
1181             DataBits => $connect->{databits},
1182             StopBits => $connect->{stopbits},
1183             Handshake => $connect->{handshake},
1184             Prompt_credentials => $connect->{prompt_credentials},
1185             Terminal_type => $connect->{terminal_type},
1186             Window_size => $connect->{window_size},
1187             Callback => $connect->{callback},
1188 0           );
1189 0 0         return $self->poll_return($ok) unless $ok; # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode
1190             # Unless console already set, set it now; will determine whether or not wake_console is sent upon login
1191 0 0 0       $self->console( $self->connection_type eq 'SERIAL' ||
1192             ($self->connection_type eq 'TELNET' && $self->port != 23) ||
1193             ($self->connection_type eq 'SSH' && $self->port != 22) ) unless defined $self->console;
1194 0           $connect->{stage}++; # Ensure we don't come back here in non-blocking mode
1195             }
1196              
1197             # Login stage
1198             my ($ok, $outRef) = $self->poll_login($pkgsub,
1199             Username => $connect->{username},
1200             Password => $connect->{password},
1201             Read_attempts => $connect->{read_attempts},
1202             Wake_console => $connect->{wake_console},
1203             Prompt_credentials => $connect->{prompt_credentials},
1204             Data_with_error => $connect->{data_with_error},
1205             Timeout => $connect->{login_timeout},
1206 0           );
1207 0 0         $self->{POLL}{output_buffer} = $$outRef if $ok;
1208 0           return $self->poll_return($ok);
1209             }
1210              
1211              
1212             sub poll_login { # Method to handle login for poll methods (used for both blocking & non-blocking modes)
1213 0     0 1   my $self = shift;
1214 0           my $pkgsub = shift;
1215 0           my $pollsub = "${Package}::login";
1216              
1217 0 0         unless ($self->{POLLING}) { # Sanity check
1218 0           my (undef, $fileName, $lineNumber) = caller;
1219 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1220             }
1221              
1222 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1223 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'read_attempts', 'wake_console', 'data_with_error');
1224 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1225 0 0 0       if (@_ && !%args) { # Legacy syntax
1226             ($args{username}, $args{password}, $args{read_attempts}, $args{wake_console},
1227 0           $args{prompt_credentials}, $args{data_with_error}, $args{timeout}, $args{errmode}) = @_;
1228             }
1229             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1230             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1231             # Set method argument keys
1232             username => defined $args{username} ? $args{username} : $self->{USERNAME},
1233             password => defined $args{password} ? $args{password} : $self->{PASSWORD},
1234             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
1235             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
1236             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
1237             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
1238             # Declare method storage keys which will be used
1239             stage => 0,
1240             login_attempted => undef,
1241             password_sent => undef,
1242             login_error => '',
1243             family_type => undef,
1244             cpu_slot => undef,
1245             detectionFromPrompt => undef,
1246             # Declare keys to be set if method called from another polled method
1247             errmode => $args{errmode},
1248 0 0         };
    0          
    0          
    0          
    0          
    0          
1249             # Cache poll structure keys which this method will use
1250 0           $self->poll_struct_cache($pollsub, $args{timeout});
1251             }
1252 0           my $login = $self->{POLL}{$pollsub};
1253 0 0         local $self->{errmode} = $login->{errmode} if defined $login->{errmode};
1254 0 0         return $self->poll_return($self->error("$pkgsub: No connection to login to")) if $self->eof;
1255              
1256 0           my $usernamePrompt = $self->username_prompt;
1257 0           my $passwordPrompt = $self->password_prompt;
1258              
1259 0 0         if ($login->{stage} < 1) { # Initial loginstage & setup - do only once
1260 0           $login->{stage}++; # Ensure we don't come back here in non-blocking mode
1261 0 0         if ($self->{LOGINSTAGE}) {
1262 0           $login->{family_type} = $self->{$Package}{ATTRIB}{'family_type'}; # Might be already set from previous login attempt
1263             }
1264             else { # Flush all attributes, as we assume we are connecting to a new device
1265 0           $self->{$Package}{ATTRIB} = undef;
1266 0           $self->{$Package}{ATTRIBFLAG} = undef;
1267             }
1268             # Handle resuming previous login attempt
1269 0 0 0       if ($self->{LOGINSTAGE} eq 'username' && $login->{username}) { # Resume login from where it was left
    0 0        
    0 0        
1270 0 0         $self->print(line => $login->{username}, errmode => 'return')
1271             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
1272 0           $self->{LOGINSTAGE} = '';
1273 0           $login->{login_attempted} = 1;
1274             }
1275             elsif ($self->{LOGINSTAGE} eq 'password' && $login->{password}) { # Resume login from where it was left
1276 0 0         $self->print(line => $login->{password}, errmode => 'return')
1277             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
1278 0           $self->{LOGINSTAGE} = '';
1279 0           $login->{login_attempted} = 1;
1280             }
1281             elsif ($self->console && $login->{wake_console}) {
1282 0           $self->debugMsg(8,"\nlogin() Sending wake_console sequence >$login->{wake_console}<\n");
1283 0 0         $self->put(string => $login->{wake_console}, errmode => 'return') # Bring connection into life
1284             or return $self->poll_return($self->error("$pkgsub: Unable to send bring alive character sequence // ".$self->errmsg));
1285             }
1286             }
1287 0 0         if ($login->{stage} < 2) { # Main login loop
1288 0           my ($pattern, $patdepth, $deepest);
1289 0           my ($promptType, $capturedPrompt, $switchName, $cliType, $configContext);
1290 0           LOGINLOOP: while (1) {
1291             # Wait until we have read in all available data
1292 0           my $ok = $self->poll_readwait($pkgsub, 1, $login->{read_attempts}, $ReadwaitTimer, $login->{login_error}.'Failed reading login prompt', $login->{data_with_error});
1293 0 0         return $self->poll_return($ok) unless $ok; # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode
1294              
1295 0           $self->debugMsg(8,"\nlogin() Connection input to process:\n>", \$self->{POLL}{read_buffer}, "<\n");
1296 0           $self->{POLL}{output_buffer} .= $self->{POLL}{read_buffer}; # This buffer preserves all the output, in case it is requested
1297 0 0         $self->{POLL}{local_buffer} = $self->{POLL}{read_buffer} =~ /\n/ ? '' : stripLastLine(\$self->{POLL}{local_buffer}); # Flush or keep lastline
1298 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer}; # If read was single line, this buffer appends it to lastline from previous read
1299              
1300             # Pattern matching; try and detect patterns, and record their depth in the input stream
1301 0           $pattern = '';
1302 0           $deepest = -1;
1303 0           foreach my $key (keys %LoginPatterns) {
1304 0 0         if (($patdepth = rindex($self->{POLL}{read_buffer}, $LoginPatterns{$key})) >= 0) { # We have a match
1305 0           $self->debugMsg(8,"\nlogin() Matched pattern $key @ depth $patdepth\n");
1306 0 0         unless ($login->{family_type}) { # Only if family type not already detected
1307             # If a banner is seen, try and extract attributes from it also
1308 0 0 0       if ($key eq 'banner' || $key eq 'menu' || $key eq 'submenu') {
    0 0        
    0 0        
    0 0        
    0          
    0          
1309 0           $login->{family_type} = $Prm{bstk};
1310 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1311 0           $self->_setAttrib('family_type', $login->{family_type});
1312 0           $self->_setAttrib('is_nncli', 1);
1313 0 0         if ($key eq 'banner') {
1314 0 0         $self->{POLL}{read_buffer} =~ /\*\*\* ((?:[^\*\n]+?) (?:Switch|Controller|Platform) (?:WC)?\d+.*?)\s+/ &&
1315             $self->_setModelAttrib($1);
1316 0 0         $self->{POLL}{read_buffer} =~ /FW:([\d\.]+)\s+SW:v([\d\.]+)/ && do {
1317 0           $self->_setAttrib('fw_version', $1);
1318 0           $self->_setAttrib('sw_version', $2);
1319             };
1320             }
1321             }
1322             elsif ($key eq 'srbanner') {
1323 0           $login->{family_type} = $Prm{sr};
1324 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1325 0           $self->_setAttrib('family_type', $login->{family_type});
1326 0           $self->_setAttrib('is_nncli', 1);
1327 0 0         $self->{POLL}{read_buffer} =~ /\((Secure Router \d+)\)/ && $self->_setModelAttrib($1);
1328 0 0         $self->{POLL}{read_buffer} =~ /Version: (.+)/ && $self->_setAttrib('sw_version', $1);
1329             }
1330             elsif ($key eq 'xlrbanner') {
1331 0           $login->{family_type} = $Prm{xlr};
1332 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1333 0           $self->_setAttrib('family_type', $login->{family_type});
1334 0           $self->_setAttrib('is_nncli', 0);
1335 0 0         $self->{POLL}{read_buffer} =~ /\* Software Release (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
1336             }
1337             elsif ($key eq 'ersbanner' || $key eq 'passportbanner' || $key eq 'pp1600banner') {
1338 0           $login->{family_type} = $Prm{pers};
1339 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1340 0           $self->_setAttrib('family_type', $login->{family_type});
1341 0           $self->_setAttrib('is_nncli', 0);
1342 0 0         $self->{POLL}{read_buffer} =~ /\* Software Release (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
1343             }
1344             elsif ($key eq 'vspbanner') {
1345 0           $login->{family_type} = $Prm{pers};
1346 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1347 0           $self->_setAttrib('family_type', $login->{family_type});
1348 0           $self->_setAttrib('is_nncli', 1);
1349 0 0         $self->{POLL}{read_buffer} =~ /Software Release Build (.+?) / && $self->_setAttrib('sw_version', $1);
1350             }
1351             elsif ($key eq 'wlan9100banner') {
1352 0           $login->{family_type} = $Prm{xirrus};
1353 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1354 0           $self->_setAttrib('family_type', $login->{family_type});
1355 0           $self->_setAttrib('is_nncli', 1);
1356 0 0         $self->{POLL}{read_buffer} =~ /AvayaOS Version (.+?) / && $self->_setAttrib('sw_version', $1);
1357             }
1358             }
1359 0 0         if ($patdepth > $deepest) { # We have a deeper match, we keep it
1360 0           ($pattern, $deepest) = ($key, $patdepth);
1361             }
1362             }
1363             }
1364 0 0         $self->debugMsg(8,"\nlogin() Retaining pattern: $pattern\n") if $deepest > -1;
1365              
1366             # Now try and match other prompts expected to be seen at the very end of received input stream
1367 0 0         if ($self->{POLL}{read_buffer} =~ /$usernamePrompt/) { # Handle Modular login prompt
    0          
1368 0           $self->debugMsg(8,"\nlogin() Matched Login prompt\n\n");
1369 0           $pattern = 'username';
1370             }
1371             elsif ($self->{POLL}{read_buffer} =~ /$passwordPrompt/) { # Handle Modular password prompt
1372 0           $self->debugMsg(8,"\nlogin() Matched Password prompt\n\n");
1373 0           $pattern = 'password';
1374             }
1375              
1376             # Now handle any pattern matches we had above
1377 0 0 0       if ($pattern eq 'banner' || $pattern eq 'bell') { # We got the banner, send a CTRL-Y to get in
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1378 0           $self->debugMsg(8,"\nlogin() Processing Stackable Banner\n\n");
1379 0 0         $self->put(string => $CTRL_Y, errmode => 'return')
1380             or return $self->poll_return($self->error("$pkgsub: Unable to send CTRL-Y sequence // ".$self->errmsg));
1381 0           next;
1382             }
1383             elsif ($pattern eq 'menu') { # We got the menu, send a 'c' and get into CLI
1384 0           $self->debugMsg(8,"\nlogin() Processing Stackable Menu\n\n");
1385 0 0         $self->put(string => 'c', errmode => 'return')
1386             or return $self->poll_return($self->error("$pkgsub: Unable to select 'Command Line Interface...' // ".$self->errmsg));
1387 0           next;
1388             }
1389             elsif ($pattern eq 'submenu') { # We are in a sub-menu page, send a 'CTRL_C' to get to main menu page
1390 0           $self->debugMsg(8,"\nlogin() Processing Stackable Sub-Menu page\n\n");
1391 0 0         $self->put(string => $CTRL_C, errmode => 'return')
1392             or return $self->poll_return($self->error("$pkgsub: Unable to go back to main menu page // ".$self->errmsg));
1393 0           next;
1394             }
1395             elsif ($pattern =~ /^more\d$/) { # We are connecting on the console port, and we are in the midst of more-paged output
1396 0           $self->debugMsg(8,"\nlogin() Quitting residual more-paged output for serial port access\n");
1397 0 0         $self->put(string => 'q', errmode => 'return')
1398             or return $self->poll_return($self->error("$pkgsub: Unable to quit more-paged output found after serial connect // ".$self->errmsg));
1399 0           next;
1400             }
1401             elsif ($pattern =~ /^consoleLogMsg\d$/) { # We are connecting on the console port, and this log message is spoiling our 1st prompt
1402 0           $self->debugMsg(8,"\nlogin() Sending extra carriage return after password for serial port access\n");
1403             # On Modular VSPs Console port, immediately after login you get log message :SW INFO user rwa connected via console port
1404             # As this message is appended to the very 1st prompt, we are not able to lock on that initial prompt
1405             # So we feed an extra carriage return so that we can lock on a fresh new prompt
1406 0 0         $self->print(errmode => 'return')
1407             or return $self->poll_return($self->error("$pkgsub: Unable to get new prompt after console log message // ".$self->errmsg));
1408 0           next;
1409             }
1410             elsif ($pattern eq 'lastlogin') { # Last login splash screen; skip it with RETURN key
1411             # This screen appears on ERS4800 release 5.8
1412 0           $self->debugMsg(8,"\nlogin() Processing Last Login screen\n\n");
1413 0 0         $self->print(errmode => 'return')
1414             or return $self->poll_return($self->error("$pkgsub: Unable to send Carriage Return // ".$self->errmsg));
1415 0           next;
1416             }
1417             elsif ($pattern eq 'username') { # Handle login prompt
1418 0           $self->debugMsg(8,"\nlogin() Processing Login/Username prompt\n\n");
1419 0 0         if ($login->{login_attempted}) {
1420 0           $self->{LOGINSTAGE} = 'username';
1421 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1422             }
1423 0 0         unless ($login->{username}) {
1424 0 0         if ($self->{TYPE} eq 'SSH') { # If an SSH connection, we already have the username
1425 0           $login->{username} = $self->{USERNAME};
1426             }
1427             else {
1428 0 0         unless ($login->{prompt_credentials}) {
1429 0           $self->{LOGINSTAGE} = 'username';
1430 0           return $self->poll_return($self->error("$pkgsub: Username required"));
1431             }
1432 0           $login->{username} = promptCredential($login->{prompt_credentials}, 'Clear', 'Username');
1433             }
1434             }
1435 0 0         $self->print(line => $login->{username}, errmode => 'return')
1436             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
1437 0           $self->{LOGINSTAGE} = '';
1438 0           $login->{login_attempted} = 1;
1439 0           next;
1440             }
1441             elsif ($pattern eq 'password') { # Handle password prompt
1442 0           $self->debugMsg(8,"\nlogin() Processing Password prompt\n\n");
1443 0 0         if ($login->{password_sent}) {
1444 0           $self->{LOGINSTAGE} = 'password';
1445 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1446             }
1447 0 0         unless ($login->{password}) {
1448 0 0         unless ($login->{prompt_credentials}) {
1449 0           $self->{LOGINSTAGE} = 'password';
1450 0           return $self->poll_return($self->error("$pkgsub: Password required"));
1451             }
1452 0           $login->{password} = promptCredential($login->{prompt_credentials}, 'Hide', 'Password');
1453             }
1454 0 0         $self->print(line => $login->{password}, errmode => 'return')
1455             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
1456 0           $self->{LOGINSTAGE} = '';
1457 0           $login->{password_sent} = 1;
1458 0           next;
1459             }
1460             elsif ($pattern eq 'localfail') { # Login failure
1461 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1462             }
1463             elsif ($pattern eq 'radiusfail') { # Radius Login failure
1464 0           return $self->poll_return($self->error("$pkgsub: Switch got access denied from RADIUS"));
1465             }
1466             elsif ($pattern =~ /^radiustimeout\d$/) { # Radius timeout
1467 0           $login->{login_error} = "Switch got no response from RADIUS servers\n";
1468 0           next; # In this case don't error, as radius falback might still get us in
1469             }
1470              
1471             # Then try and match CLI prompts; this is the only exit point of the loop
1472 0 0         if ($login->{family_type}) { # A family type was already detected from banner
1473 0 0         if ($login->{family_type} eq $Prm{pers}) {
1474 0           foreach my $type ('cli', 'nncli') {
1475 0           $promptType = "$login->{family_type}_$type";
1476 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$promptType})/) {
1477 0           ($capturedPrompt, $switchName, $login->{cpu_slot}, $configContext) = ($1, $2, $3, $4);
1478 0           $cliType = $type;
1479 0           last;
1480             }
1481             }
1482             }
1483             else {
1484 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$login->{family_type}})/) {
1485 0           ($capturedPrompt, $switchName, $configContext) = ($1, $2, $4);
1486 0           $promptType = $login->{family_type};
1487             }
1488             }
1489             }
1490             else { # A family type has not been detected yet; try and detect from received prompt
1491 0           foreach my $key (@InitPromptOrder) {
1492 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$key})/) {
1493 0           ($capturedPrompt, $switchName, $login->{cpu_slot}, $configContext) = ($1, $2, $3, $4);
1494 0           $promptType = $key;
1495 0           ($login->{family_type} = $key) =~ s/_(\w+)$//;
1496 0           $cliType = $1;
1497 0           $login->{detectionFromPrompt} = 1;
1498 0           last;
1499             }
1500             }
1501             }
1502 0 0         if ($capturedPrompt) { # We have a prompt, we can exit loop
1503 0           $self->debugMsg(8,"\nlogin() Got CLI prompt for family type $login->{family_type} !\n");
1504 0           $self->_setDevicePrompts($promptType, $switchName);
1505 0           $capturedPrompt =~ s/^\x0d//; # Remove initial carriage return if there
1506 0           $capturedPrompt =~ s/\x0d$//; # Remove trailing carriage return if there (possible if we match on not the last prompt, as we do /m matching above
1507 0           ($self->{LASTPROMPT} = $capturedPrompt) =~ s/$LastPromptClense//o;
1508 0           $self->{$Package}{CONFIGCONTEXT} = $configContext;
1509 0           $self->{$Package}{PROMPTTYPE} = $promptType;
1510 0           $self->debugMsg(4,"login() Prompt type = $self->{$Package}{PROMPTTYPE}\n");
1511              
1512 0 0         $self->_setAttrib('cpu_slot', $login->{cpu_slot}) if $login->{family_type} eq $Prm{pers};
1513 0 0         if ($login->{detectionFromPrompt}) {
1514 0 0 0       if ($login->{family_type} eq $Prm{bstk} || (defined $cliType && $cliType eq 'nncli')) {
      0        
1515 0           $self->_setAttrib('is_nncli', 1);
1516             }
1517             else {
1518 0           $self->_setAttrib('is_nncli', 0);
1519             }
1520             }
1521 0           last LOGINLOOP;
1522             }
1523             }
1524 0 0 0       if ($login->{family_type} eq $Prm{generic} || ($login->{detectionFromPrompt} && $self->{LASTPROMPT} !~ /^@/) ) { # Can't tell, need extended discovery
      0        
1525 0           $login->{stage}++; # Move to next section in non-blocking mode
1526             }
1527             else {
1528 0           $login->{stage} += 2; # Move to section after next
1529             }
1530 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1531             }
1532 0 0         if ($login->{stage} < 3) { # Extended discovery
1533 0           my ($ok, $familyType) = $self->discoverDevice($pkgsub);
1534 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1535 0           $login->{family_type} = $familyType;
1536 0 0 0       if ($login->{family_type} eq $Prm{generic} && ($self->{errmode} eq 'croak' || $self->{errmode} eq 'die')) {
      0        
1537 0           carp "\n$pkgsub Warning! Device type not detected; using $Prm{generic}\n";
1538             }
1539 0           $login->{stage} += 2; # Move to final section
1540             }
1541 0 0         if ($login->{stage} < 4) { # Family type was detected, not just from the prompt (though we do rely on prompt alone for Standby CPUs on PassportERS)
1542 0 0 0       if ($login->{family_type} eq $Prm{pers} || $login->{family_type} eq $Prm{xlr}) {
1543 0 0         $self->_setAttrib('is_master_cpu', $self->{LASTPROMPT} =~ /^@/ ? 0 : 1);
1544 0 0         $self->_setAttrib('is_dual_cpu', 1) if $self->{LASTPROMPT} =~ /^@/;
1545             }
1546 0 0         $self->_setAttrib('family_type', $login->{family_type}) if $login->{detectionFromPrompt};
1547             }
1548              
1549             # Store credentials if these were used
1550 0 0         ($self->{USERNAME}, $self->{PASSWORD}) = ($login->{username}, $login->{password}) if $login->{login_attempted};
1551 0           return $self->poll_return(1);
1552             }
1553              
1554              
1555             sub poll_cmd { # Method to handle cmd for poll methods (used for both blocking & non-blocking modes)
1556 0     0 1   my $self = shift;
1557 0           my $pkgsub = shift;
1558 0           my $pollsub = "${Package}::cmd";
1559              
1560 0 0         unless ($self->{POLLING}) { # Sanity check
1561 0           my (undef, $fileName, $lineNumber) = caller;
1562 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1563             }
1564              
1565 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1566 0           my @validArgs = ('command', 'feed_list', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_confirm_prompt',
1567             'cmd_initiated_prompt', 'more_pages', 'timeout', 'errmode', 'progress_dots');
1568 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1569 0 0 0       if (@_ && !%args) { # Legacy syntax
1570             ($args{command}, $args{more_pages}, $args{prompt}, $args{reset_prompt}, $args{timeout}, $args{errmode},
1571 0           $args{feed_list}, $args{cmd_confirm_prompt}, $args{cmd_initiated_prompt}) = @_;
1572             }
1573 0 0 0       $args{feed_list} = [$args{feed_list}] if defined $args{feed_list} && !ref($args{feed_list}) eq "ARRAY"; # We want it as an array reference
1574             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1575             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1576             # Set method argument keys
1577             command => $args{command},
1578             prompt => defined $args{prompt} ? $args{prompt} : $self->{$Package}{prompt_qr},
1579             more_prompt => defined $args{more_prompt} ? $args{more_prompt} : $self->{$Package}{morePrompt_qr},
1580             more_prompt_delay => defined $args{more_prompt} ? undef : $self->{$Package}{morePromptDelay_qr},
1581             more_pages => defined $args{more_pages} ? $args{more_pages} : $self->{$Package}{morePaging},
1582             reset_prompt => $args{reset_prompt} && defined $self->{$Package}{PROMPTTYPE},
1583             yn_prompt => defined $args{cmd_confirm_prompt} ? $args{cmd_confirm_prompt} : $self->{$Package}{cmd_confirm_prompt_qr},
1584             cmd_prompt => defined $args{cmd_initiated_prompt} ? $args{cmd_initiated_prompt} : $self->{$Package}{cmd_initiated_prompt_qr},
1585             feed_data => $args{feed_list},
1586             progress_dots => defined $args{progress_dots} ? $args{progress_dots} : $self->{$Package}{progressDots},
1587             # Declare method storage keys which will be used
1588             stage => 0,
1589             lastLine => '',
1590             outputNewline => '',
1591             progress => undef,
1592             alreadyCmdTimeout => 0,
1593             ynPromptCount => undef,
1594             cmdPromptCount => undef,
1595             cmdEchoRemoved => 0,
1596             lastPromptEchoedCmd => undef,
1597             cache_timeout => defined $args{timeout} ? $args{timeout} : $self->{POLL}{timeout},
1598             # Declare keys to be set if method called from another polled method
1599             errmode => $args{errmode},
1600 0 0 0       };
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1601             # Cache poll structure keys which this method will use
1602 0           $self->poll_struct_cache($pollsub, $args{timeout});
1603             }
1604              
1605 0           my $cmd = $self->{POLL}{$pollsub};
1606 0 0         local $self->{errmode} = $cmd->{errmode} if defined $cmd->{errmode};
1607 0 0         return $self->poll_return($self->error("$pkgsub: No connection to send cmd to")) if $self->eof;
1608 0 0         $cmd->{prompt} = $InitPrompt{$self->{$Package}{PROMPTTYPE}} if $cmd->{reset_prompt};
1609 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
1610 0           my $newLineLastLine = 0;
1611              
1612 0 0         if ($cmd->{stage} < 1) { # Send command - do only once
1613 0           $cmd->{stage}++; # Ensure we don't come back here in non-blocking mode
1614 0 0         if (defined $cmd->{command}) {
1615 0           my $command = $cmd->{command};
1616             # In NNCLI mode, if command ends with ?, append CTRL-X otherwise partial command will appear after next prompt
1617 0 0 0       if ($command =~ /\?\s*$/ && $self->{$Package}{ATTRIB}{'is_nncli'}) {
1618 0 0         if ($familyType eq $Prm{sr}) { $command .= $CTRL_U }
  0            
1619 0           else { $command .= $CTRL_X }
1620             }
1621             # Flush any unread data which might be pending
1622 0           $self->read(blocking => 0);
1623             # Send the command
1624 0           $self->debugMsg(8,"\ncmd() Sending command:>", \$command, "<\n");
1625 0 0         $self->print(line => $command, errmode => 'return')
1626             or return $self->poll_return($self->error("$pkgsub: Unable to send CLI command: $command // ".$self->errmsg));
1627             }
1628             }
1629 0           CMDLOOP: while (1) {
1630             # READ in data
1631 0 0         if ($cmd->{stage} == 1) { # Normal data read
    0          
1632 0           my $ok = $self->poll_read($pkgsub); # We always come back even in case of error
1633 0 0 0       return $self->poll_return($ok) if defined $ok && $ok == 0; # Come out only in case of non-blocking not ready
1634 0 0         unless (defined $ok) { # We catch timeout event here
1635 0 0 0       if ($cmd->{alreadyCmdTimeout} || !length $familyType || $familyType eq $Prm{generic}) {
      0        
1636 0           return $self->poll_return($self->error("$pkgsub: Failed after sending command // ".$self->errmsg));
1637             }
1638 0           $self->debugMsg(4, "\ncmd() Initial cmd timeout; attempting reset_prompt\n");
1639 0 0         $self->print(errmode => 'return') # Send a carriage return and we have a 2nd try at catching prompt
1640             or return $self->poll_return($self->error("$pkgsub: Unable to send Carriage Return // ".$self->errmsg));
1641 0           $self->{POLL}{timeout} = $cmd->{cache_timeout} * $CmdTimeoutRatio; # Re-arm timeout
1642 0           $cmd->{prompt} = $InitPrompt{$self->{$Package}{PROMPTTYPE}};
1643 0           $cmd->{alreadyCmdTimeout} = 1; # Ensures that at next timeout we generate the error mode action (so cannot loop)
1644 0           $cmd->{reset_prompt} = 1;
1645 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1646 0           next CMDLOOP;
1647             }
1648             }
1649             elsif ($cmd->{stage} == 2) { # cmd_prompt / Wait if more data coming
1650 0           my $ok = $self->poll_readwait($pkgsub, 0, $CmdPromptReadAttempts, $ReadwaitTimer, "Cmd_prompt; unable to check for more data");
1651 0 0         return $self->poll_return($ok) unless $ok; # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode
1652 0           $cmd->{stage} = 1; # Whatever the outcome below, we will revert to normal data reads
1653 0 0         unless (length $self->{POLL}{read_buffer}) { # No more data => no false trigger
1654 0           $self->debugMsg(8,"\ncmd() Detected CMD embedded prompt");
1655 0           my $feed;
1656 0 0         if ($feed = shift(@{$cmd->{feed_data}})) {
  0            
1657 0           $self->debugMsg(8,"cmd() - Have data to feed:>", \$feed, "<\n");
1658             }
1659             else {
1660 0 0         if (++$cmd->{cmdPromptCount} > $self->{$Package}{cmd_feed_timeout}) {
1661 0           return $self->poll_return($self->error("$pkgsub: Command embedded prompt timeout"));
1662             }
1663 0           $feed = '';
1664 0           $self->debugMsg(8,"cmd() - No data to feed!\n");
1665             }
1666 0 0         $self->print(line => $feed, errmode => 'return')
1667             or return $self->poll_return($self->error("$pkgsub: Unable to feed data at cmd prompt // ".$self->errmsg));
1668              
1669 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1670 0           next CMDLOOP;
1671             }
1672             }
1673             else { # Stage = 3 ; more prompt delay / 1 non-blocking read to ascertain if partial more prompt can be processed or not
1674 0           my $ok = $self->poll_readwait($pkgsub, 0, 1, $ReadwaitTimer, "Delayed More prompt; unable to check for more data");
1675 0 0         return $self->poll_return($ok) unless $ok; # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode
1676 0           $cmd->{stage} = 1; # We immediately revert to normal data reads
1677             }
1678              
1679             # Process data
1680 0 0         if ($cmd->{progress_dots}) { # Print dots for progress
1681 0 0         _printDot() unless defined $cmd->{progress};
1682 0 0         if ( ( $cmd->{progress} += length($self->{POLL}{read_buffer}) ) > $cmd->{progress_dots}) {
1683 0           _printDot();
1684 0           $cmd->{progress} -= $cmd->{progress_dots};
1685             }
1686             }
1687              
1688 0 0         unless ($cmd->{cmdEchoRemoved}) { # If the echoed cmd was not yet removed
1689 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer}; # Append to local_buffer
1690 0 0         if ($self->{POLL}{local_buffer} =~ s/(^.*\n)//) { # if we can remove it now
1691 0           $self->debugMsg(8,"\ncmd() Stripped echoed command\n");
1692 0           $cmd->{lastPromptEchoedCmd} = $self->{LASTPROMPT} . $1;
1693 0 0         $cmd->{lastPromptEchoedCmd} =~ s/\x10?\x00//g if $familyType eq $Prm{xirrus}; # WLAN9100 in telnet
1694 0           $cmd->{cmdEchoRemoved} = 1;
1695 0           $self->{POLL}{read_buffer} = $self->{POLL}{local_buffer}; # Re-prime read_buffer so that we fall through below with what's left
1696 0           $self->{POLL}{local_buffer} = ''; # Empty local_buffer so that we fall through below
1697 0 0         next CMDLOOP unless length $self->{POLL}{read_buffer}; # If we have remaining data, fall through, else do next cycle
1698             }
1699             else { # if we can't then no point processing patterns below
1700 0           next CMDLOOP; # Do next read
1701             }
1702             }
1703             # If we get here, it means that the echo-ed cmd has been removed
1704             # read_buffer will either hold remaining output after removing echoed cmd
1705             # or it will hold the most recent data read
1706              
1707 0           my $output = $cmd->{lastLine}.$self->{POLL}{read_buffer}; # New output appended to previous lastLine
1708 0           $self->{POLL}{output_buffer} .= $cmd->{outputNewline}; # Re-add newLine if had been withheld
1709              
1710 0 0         if (length $output) { # Clean up patterns
1711 0           $output =~ s/^(?:\x08 \x08)*//; # Remove backspace chars following a more prompt, if any
1712 0 0         $output =~ s/^\x0d *\x00\x0d// if $familyType eq $Prm{sr}; # Remove Secure Router CR+spaces+0+CR sequence following more prompt
1713 0 0         if ($familyType eq $Prm{xirrus}) {
1714 0           $output =~ s/\x10?\x00//g; # Remove weird chars that WLAN9100 peppers output with, with telnet only,
1715             # ...in some case even not at beginning of line..
1716 0           $output =~ s/^ ?\x0d *\x0d//; # Remove WLAN9100 CR+spaces+CR sequence following more prompt
1717             # .. with telnet, the WLAN9100 echoes back the space which was sent to page
1718             }
1719             # Note, order of these matches is important
1720 0           $output =~ s/^\x0d+//mg; # Remove spurious CarriageReturns at beginning of line, a BPS/470 special
1721 0           $output =~ s/\x0d+$//mg; # Remove spurious CarriageReturns at end of each line, 5500, 4500...
1722             }
1723 0           $cmd->{lastLine} = stripLastLine(\$output); # We strip a new lastLine from it
1724              
1725             # Here we either hold data in $output or in $cmd->{lastLine} or both
1726 0 0         $self->debugMsg(8,"\ncmd() Output to process:\n>", \$output, "<\n") if length $output;
1727 0 0         $self->debugMsg(8,"\ncmd() Lastline stripped:\n>", \$cmd->{lastLine}, "<\n") if length $cmd->{lastLine};
1728              
1729 0 0         if (length $output) { # Append output now
1730 0           $self->{POLL}{local_buffer} .= $output; # Append to local_buffer
1731 0           $self->{POLL}{output_buffer} .= $output; # Append to output_buffer as well
1732             }
1733            
1734             # Since some more prompt pattern matches can include an initial \n newline which needs removing, we need lastLine to hold that \n
1735 0 0 0       if (length $cmd->{lastLine} && $self->{POLL}{local_buffer} =~ s/\n\n$/\n/) { # If output had x2 trailing newlines, strip last ...
1736 0           $cmd->{lastLine} = "\n" . $cmd->{lastLine}; # ... and pre-pend it to lastLine
1737 0           $cmd->{outputNewline} = chop $self->{POLL}{output_buffer}; # And chop & store \n from output_buffer
1738 0           $newLineLastLine = 1; # and remember it
1739 0           $self->debugMsg(8,"\ncmd() Lastline adjusted:\n>", \$cmd->{lastLine}, "<\n");
1740             }
1741             else {
1742 0           $cmd->{outputNewline} = ''; # Clear it
1743 0           $newLineLastLine = 0; # Clear it
1744             }
1745              
1746 0 0         next CMDLOOP unless length $cmd->{lastLine};
1747              
1748 0 0         if ($cmd->{lastLine} =~ s/($cmd->{prompt})//) {
1749 0           my ($cap1, $cap2, $cap3) = ($1, $2, $3); # We need to store these in temporary variables
1750 0           ($self->{LASTPROMPT} = $cap1) =~ s/$LastPromptClense//o; # Remove initial carriage return if there
1751 0 0         $self->_setDevicePrompts($self->{$Package}{PROMPTTYPE}, $cap2) if $cmd->{reset_prompt};
1752 0 0         $self->{$Package}{CONFIGCONTEXT} = $cmd->{reset_prompt} ? $cap3 : $cap2;
1753 0 0 0       unless ($newLineLastLine && !length $cmd->{lastLine}) { # Only if we did not gobble the \n ...
1754 0           $self->{POLL}{output_buffer} .= $cmd->{outputNewline}; # ... re-add to output its final \n
1755             }
1756 0           $self->debugMsg(8,"\ncmd() prompt detected; cmd complete!\n");
1757 0           last CMDLOOP;
1758             }
1759 0 0 0       if ($cmd->{more_prompt_delay} && !$cmd->{morePromptDelayed} && $cmd->{lastLine} =~ /(?:$cmd->{more_prompt_delay})$/) { # We have a more prompt which requires a delay
      0        
1760 0           $self->debugMsg(8,"\ncmd() more prompt delay pattern detected; forcing 1 cycle readwait\n");
1761 0           $cmd->{stage} = 3; # Force a 1 cycle readwait at next cycle
1762 0           $cmd->{morePromptDelayed} = 1; # Make sure we don't come back here at next cycle
1763 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1764 0           next CMDLOOP;
1765             }
1766 0 0 0       if ($cmd->{more_prompt} && $cmd->{lastLine} =~ s/(?:$cmd->{more_prompt})$//) { # We have a more prompt
1767 0           $cmd->{morePromptDelayed} = 0; # Reset this flag
1768 0 0         if (length $cmd->{lastLine}) { # We did not gobble the \n
1769 0           $self->{POLL}{local_buffer} .= $cmd->{lastLine}; # Re-add it now
1770 0           $cmd->{lastLine} = ''; # And clear lastLine, otherwise it will compromise pattern to delete more prompt del char sequence
1771 0 0         $self->{POLL}{output_buffer} .= $cmd->{outputNewline} if $newLineLastLine;
1772             }
1773 0 0         $cmd->{outputNewline} = '' if $newLineLastLine; # Either way (\n gobbled or not) we clear it
1774 0 0 0       if ($cmd->{more_pages} == 0 || $cmd->{more_pages}-- > 1) { # We get the next page
1775 0           $self->debugMsg(8,"\ncmd() More prompt detected; feeding 'SPACE'\n");
1776 0 0         $self->put(string => $Space, errmode => 'return')
1777             or return $self->poll_return($self->error("$pkgsub: Unable to page at more prompt // ".$self->errmsg));
1778             }
1779             else { # We quit here
1780 0           $self->debugMsg(8,"\ncmd() More prompt detected; feeding 'Q'\n");
1781 0 0         $self->put(string => 'q', errmode => 'return')
1782             or return $self->poll_return($self->error("$pkgsub: Unable to quit more prompt // ".$self->errmsg));
1783             }
1784 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1785 0           next CMDLOOP;
1786             }
1787 0 0 0       if ($cmd->{yn_prompt} && $cmd->{lastLine} =~ /$cmd->{yn_prompt}/) { # We have a Y/N prompt
1788 0 0         if (++$cmd->{ynPromptCount} > $self->{$Package}{cmd_feed_timeout}) {
1789 0           return $self->poll_return($self->error("$pkgsub: Y/N confirm prompt timeout"));
1790             }
1791 0           $self->debugMsg(8,"\ncmd() Y/N prompt detected; feeding 'Y'\n");
1792 0 0         $self->print(line => 'y', errmode => 'return')
1793             or return $self->poll_return($self->error("$pkgsub: Unable to confirm at Y/N prompt // ".$self->errmsg));
1794 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1795 0           next CMDLOOP;
1796             }
1797 0 0 0       if ($cmd->{cmd_prompt} && $cmd->{lastLine} =~ /$cmd->{cmd_prompt}/) { # We have a prompt for additional input
1798             # But, this pattern risks matching against transient data; so check if more data coming
1799 0           $self->debugMsg(8,"\ncmd() cmd-prompt detected; forcing readwait\n");
1800 0           $cmd->{stage} = 2; # Force a readwait at next cycle
1801 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1802 0           next CMDLOOP;
1803             }
1804              
1805             # Having lastLine with \n newline can screw up cleanup patterns above, so after above prompt matching we have it removed here
1806 0 0         $self->{POLL}{local_buffer} .= "\n" if $cmd->{lastLine} =~ s/^\n//; # If it's there we take it off
1807             }# CMDLOOP
1808 0           $self->{POLL}{output_result} = $self->_determineOutcome(\$self->{POLL}{local_buffer}, $cmd->{lastPromptEchoedCmd});
1809 0           return $self->poll_return(1);
1810             }
1811              
1812              
1813             sub poll_attribute { # Method to handle attribute for poll methods (used for both blocking & non-blocking modes)
1814 0     0 1   my $self = shift;
1815 0           my $pkgsub = shift;
1816 0           my $pollsub = "${Package}::attribute";
1817              
1818 0 0         unless ($self->{POLLING}) { # Sanity check
1819 0           my (undef, $fileName, $lineNumber) = caller;
1820 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1821             }
1822              
1823 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1824 0           my @validArgs = ('attribute', 'reload', 'timeout', 'errmode');
1825 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1826 0 0 0       if (@_ && !%args) { # Legacy syntax
1827 0           ($args{attribute}, $args{reload}, $args{timeout}, $args{errmode}) = @_;
1828             }
1829             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1830             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1831             # Set method argument keys
1832             attribute => $args{attribute},
1833             reload => $args{reload},
1834             # Declare method storage keys which will be used
1835             stage => 0,
1836             debugMsg => 0,
1837             # Declare keys to be set if method called from another polled method
1838             errmode => $args{errmode},
1839 0           };
1840             # Cache poll structure keys which this method will use
1841 0           $self->poll_struct_cache($pollsub, $args{timeout});
1842             }
1843 0           my $attrib = $self->{POLL}{$pollsub};
1844 0 0         local $self->{errmode} = $attrib->{errmode} if defined $attrib->{errmode};
1845 0 0         return $self->poll_return($self->error("$pkgsub: No connection for attributes")) if $self->eof;
1846 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
1847              
1848 0 0         if ($attrib->{stage} < 1) { # 1st stage
1849 0 0         return $self->poll_return($self->error("$pkgsub: No attribute provided")) unless defined $attrib->{attribute};
1850 0 0         return $self->poll_return(1) unless $familyType; # Value returned is undef
1851              
1852 0           $attrib->{stage} += 2; # Assume no login() required and that we move directly to 3rd stage
1853 0 0         if ($attrib->{reload}) { # Force reload, either via forced login() or resetting ATTRIBFLAG
1854 0 0 0       if ($attrib->{attribute} eq 'family_type' || $attrib->{attribute} eq 'is_nncli' || $attrib->{attribute} eq 'is_acli'
      0        
      0        
      0        
1855             || $attrib->{attribute} eq 'is_master_cpu' || $attrib->{attribute} eq 'cpu_slot') {
1856 0 0         $self->print or return $self->poll_return($self->error("$pkgsub: Unable to refresh device connection"));
1857 0           $attrib->{stage}--; # Move to 2nd stage
1858             }
1859             else {
1860 0           $self->{$Package}{ATTRIBFLAG}{$attrib->{attribute}} = undef;
1861             }
1862             }
1863             }
1864              
1865 0 0         if ($attrib->{stage} < 2) { # 2nd stage - wait for login to complete
1866 0           my $ok = $self->poll_login($pkgsub);
1867 0 0         return $self->poll_return($ok) unless $ok;
1868 0           $attrib->{stage}++; # Move to 3rd stage
1869             }
1870              
1871 0 0         if ($attrib->{stage} < 3) { # 3rd stage
1872             # If the attribute is set already, return it at once and quit
1873 0 0         if (defined $self->{$Package}{ATTRIBFLAG}{$attrib->{attribute}}) {
1874 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1875 0           return $self->poll_return(1);
1876             }
1877             # Go no further if generic family type
1878 0 0         return $self->poll_return(1) if $familyType eq $Prm{generic}; # Value returned is undef
1879 0           $attrib->{stage}++; # Move to next stage
1880             }
1881              
1882             # Otherwise go set the attribute
1883 0 0         if ($familyType eq $Prm{pers}) {
    0          
    0          
    0          
    0          
    0          
1884 0 0         $attrib->{attribute} eq 'is_ha' && do {
1885 0 0         unless ($attrib->{debugMsg}) {
1886 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show ha-state\n");
1887 0           $attrib->{debugMsg} = 1;
1888             }
1889 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show ha-state', 'show ha-state');
1890 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1891 0 0         if ($$outref =~ /Current CPU State : Disabled State./) {
    0          
1892 0           $self->_setAttrib('is_ha', 0);
1893             }
1894             elsif ($$outref =~ /Current CPU State/) {
1895 0           $self->_setAttrib('is_ha', 1);
1896             }
1897             else { # For example on ERS8300 or ERS1600
1898 0           $self->_setAttrib('is_ha', undef);
1899             }
1900 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1901 0           return $self->poll_return(1);
1902             };
1903 0 0         $attrib->{attribute} eq 'sw_version' && do {
1904 0 0         unless ($attrib->{debugMsg}) {
1905 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys sw / show sys software\n");
1906 0           $attrib->{debugMsg} = 1;
1907             }
1908 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show sys sw', 'show sys software');
1909 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1910 0 0         $$outref =~ /Version : Build (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
1911 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1912 0           return $self->poll_return(1);
1913             };
1914 0 0         $attrib->{attribute} eq 'fw_version' && do {
1915 0 0         if ($attrib->{stage} < 4) { # 4th stage
1916 0 0         unless ($attrib->{debugMsg}) {
1917 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show bootconfig info / show boot config general\n");
1918 0           $attrib->{debugMsg} = 1;
1919             }
1920 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show bootconfig info', 'show boot config general');
1921 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1922 0 0         if ($$outref =~ /Version:\s+(?i:v|REL)?(.+)/) {
1923 0           $self->_setAttrib('fw_version', $1);
1924 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1925 0           return $self->poll_return(1);
1926             }
1927             else {
1928 0           $attrib->{stage}++; # Move to next stage
1929 0           $attrib->{debugMsg} = 0;
1930             }
1931             }
1932 0 0         unless ($attrib->{debugMsg}) {
1933 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show bootconfig info / show boot config info\n");
1934 0           $attrib->{debugMsg} = 1;
1935             }
1936 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show bootconfig info', 'show boot config info'); # On 8300 it's 'show boot config info'
1937 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1938 0 0         if ($$outref =~ /Version:\s+(?i:v|REL)?(.+)/) {
1939 0           $self->_setAttrib('fw_version', $1);
1940             }
1941             else { # VSP9000 has no fw_version (when command executed on standby CPU)
1942 0           $self->_setAttrib('fw_version', undef);
1943             }
1944 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1945 0           return $self->poll_return(1);
1946             };
1947 0 0         $attrib->{attribute} eq 'stp_mode' && do {
1948 0 0         unless ($attrib->{debugMsg}) {
1949 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show bootconfig flags / show boot flags\n");
1950 0           $attrib->{debugMsg} = 1;
1951             }
1952 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show bootconfig flags', 'show boot config flags');
1953 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1954 0 0         if ($$outref =~ /flags spanning-tree-mode (mstp|rstp)/) {
1955 0           $self->_setAttrib('stp_mode', $1);
1956             }
1957             else {
1958 0           $self->_setAttrib('stp_mode', 'stpg');
1959             }
1960 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1961 0           return $self->poll_return(1);
1962             };
1963 0 0         $attrib->{attribute} eq 'baudrate' && do {
1964 0 0         unless ($attrib->{debugMsg}) {
1965 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show bootconfig sio / show boot config sio\n");
1966 0           $attrib->{debugMsg} = 1;
1967             }
1968 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show bootconfig sio', 'show boot config sio', 1);
1969 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1970 0 0         if ($$outref =~ /sio (?:console )?baud (\d+)/) { # On VSP/8600 it's "sio console baud 9600"; on 8300/1600 "sio baud 9600"
1971 0           $self->_setAttrib('baudrate', $1);
1972             }
1973 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1974 0           return $self->poll_return(1);
1975             };
1976 0 0         $attrib->{attribute} eq 'max_baud' && do {
1977 0 0         if ($attrib->{stage} < 4) { # 4th stage
1978 0 0         unless ($attrib->{debugMsg}) {
1979 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: config bootconfig sio console baud ? / boot config sio console baud ?\n");
1980 0           $attrib->{debugMsg} = 1;
1981             }
1982 0           my ($ok, $outref) = $self->cmdConfig($pkgsub, 'config bootconfig sio console baud ?', "boot config sio console baud ?$CTRL_C", 1);
1983 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1984             # VSP9k : <9600 - 115200> Baud rate {9600 | 19200 | 38400 | 57600 | 115200}
1985             # 8600acli : <1200-115200> Rate
1986             # 8600ppcli : = what rate {1200..115200}
1987 0 0         if ($$outref =~ /(?:-|\.\.)\s?(\d+)[>}]/) {
1988 0           $self->_setAttrib('max_baud', $1);
1989 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
1990 0           return $self->poll_return(1);
1991             }
1992             else {
1993 0           $attrib->{stage}++; # Move to next stage
1994 0           $attrib->{debugMsg} = 0;
1995             }
1996             }
1997 0 0         unless ($attrib->{debugMsg}) {
1998 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: config bootconfig sio baud ? / boot config sio baud ?\n");
1999 0           $attrib->{debugMsg} = 1;
2000             }
2001 0           my ($ok, $outref) = $self->cmdConfig($pkgsub, 'config bootconfig sio baud ?', "boot config sio baud ?$CTRL_C", 1);
2002 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2003             # 8300nncli : <1200-115200> rate
2004             # 8300ppcli : = what rate {2400|4800|9600|19200|38400|57600|115200} IN {1200..115200}
2005             # 1600 : = what rate {1200..115200}
2006 0 0         if ($$outref =~ /(?:-|\.\.)\s?(\d+)[>}]/) {
2007 0           $self->_setAttrib('max_baud', $1);
2008             }
2009 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2010 0           return $self->poll_return(1);
2011             };
2012 0 0         if ($self->{$Package}{ATTRIB}{'is_master_cpu'}) { # On Master CPU
2013 0 0 0       ($attrib->{attribute} eq 'is_dual_cpu' || $attrib->{attribute} eq 'base_mac') && do {
2014 0 0         unless ($attrib->{debugMsg}) {
2015 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys info / show sys-info (4 pages)\n");
2016 0           $attrib->{debugMsg} = 1;
2017             }
2018 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show sys info', 'show sys-info', 4);
2019 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2020 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && do {
2021 0           my $model = $1; # Record it, we need to set the model type after is_apls
2022 0 0         if ($$outref =~ / BoxType: (.+)/gc) { # APLS boxes show a boxtype on same line
2023 0           $self->_setBoxTypeAttrib($1);
2024 0           $self->_setAttrib('is_apls', 1);
2025 0           $self->_setModelAttrib($model); # Must be set after is_apls so that is_voss gets set as well
2026             }
2027             else {
2028 0           $self->_setAttrib('apls_box_type', undef);
2029 0           $self->_setAttrib('is_apls', 0);
2030 0           $self->_setModelAttrib($model);
2031             }
2032             };
2033 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2034 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) {
2035 0 0         if ($$outref =~ /BrandName:?\s+: (.+)/gc) { # On VOSS VSPs we read it
2036 0           (my $brandname = $1) =~ s/, Inc\.$//; # Remove 'Inc.'
2037 0           $self->_setAttrib('brand_name', $brandname);
2038             }
2039             else { # VSP9000 case, it is_voss, but reports no BrandName, so we set it..
2040 0           $self->_setAttrib('brand_name', 'Avaya');
2041             }
2042             }
2043             else { # Non-VOSS PassportERS
2044 0           $self->_setAttrib('brand_name', undef);
2045             }
2046 0 0         $$outref =~ /BaseMacAddr\s+: (.+)/g && $self->_setBaseMacAttrib($1);
2047 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
      0        
2048             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2049             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2050             ) {
2051 0           $self->_setAttrib('is_dual_cpu', 1);
2052             }
2053             else {
2054 0           $self->_setAttrib('is_dual_cpu', 0);
2055             }
2056             # Attributes below are beyond demanded pages of output, but we still check them in case more paging was disabled
2057 0 0         $$outref =~ /Virtual IP\s+: (.+)/g && $self->_setAttrib('oob_virt_ip', $1);
2058 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2059 0           return $self->poll_return(1);
2060             };
2061             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'is_apls' || $attrib->{attribute} eq 'is_voss' ||
2062             $attrib->{attribute} eq 'apls_box_type' || $attrib->{attribute} eq 'brand_name' || # Any new attributes added here, need to be added on exit to this if block below
2063             (!$self->{$Package}{ATTRIBFLAG}{'model'} && ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports')) || # We need 'model' attrib for port/slot ones
2064             (!$self->{$Package}{ATTRIBFLAG}{'is_voss'} && $attrib->{attribute} =~ /^(?:is_)?oob_/) # We need 'is_voss' attrib for oob ones
2065 0 0 0       ) && do {
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
2066 0 0         unless ($attrib->{debugMsg}) {
2067 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys info / show sys-info (1 page)\n");
2068 0           $attrib->{debugMsg} = 1;
2069             }
2070 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show sys info', 'show sys-info', 1);
2071 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2072 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && do {
2073 0           my $model = $1; # Record it, we need to set the model type after is_apls
2074 0 0         if ($$outref =~ / BoxType: (.+)/gc) { # APLS boxes show a boxtype on same line
2075 0           $self->_setBoxTypeAttrib($1);
2076 0           $self->_setAttrib('is_apls', 1);
2077 0           $self->_setModelAttrib($model); # Must be set after is_apls so that is_voss gets set as well
2078             }
2079             else {
2080 0           $self->_setAttrib('apls_box_type', undef);
2081 0           $self->_setAttrib('is_apls', 0);
2082 0           $self->_setModelAttrib($model);
2083             }
2084             };
2085 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2086 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) {
2087 0 0         if ($$outref =~ /BrandName:?\s+: (.+)/gc) { # On VOSS VSPs we read it
2088 0           (my $brandname = $1) =~ s/, Inc\.$//; # Remove 'Inc.'
2089 0           $self->_setAttrib('brand_name', $brandname);
2090             }
2091             else { # VSP9000 case, it is_voss, but reports no BrandName, so we set it..
2092 0           $self->_setAttrib('brand_name', 'Avaya');
2093             }
2094             }
2095             else { # Non-VOSS PassportERS
2096 0           $self->_setAttrib('brand_name', undef);
2097             }
2098 0 0         $$outref =~ /BaseMacAddr\s+: (.+)/g && $self->_setBaseMacAttrib($1); # Might not match on 8600 as on page 2
2099             # Attributes below are beyond demanded pages of output, but we still check them in case more paging was disabled
2100 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
    0 0        
2101             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2102             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2103             ) {
2104 0           $self->_setAttrib('is_dual_cpu', 1);
2105             }
2106             elsif ($$outref =~ /System Error Info :/) { # Output which follows Card Info, i.e. the output was there but no CP dormant matched
2107 0           $self->_setAttrib('is_dual_cpu', 0);
2108             }
2109 0 0         $$outref =~ /Virtual IP\s+: (.+)/g && $self->_setAttrib('oob_virt_ip', $1);
2110 0 0 0       if ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'is_apls' || $attrib->{attribute} eq 'is_voss' ||
      0        
      0        
      0        
      0        
2111             $attrib->{attribute} eq 'apls_box_type' || $attrib->{attribute} eq 'brand_name') { # Needs to match the same listed on beginning of if block above
2112 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2113 0           return $self->poll_return(1);
2114             }
2115             else { # If an attribute that just needed 'model', fall through to appropriate section below
2116 0           $attrib->{debugMsg} = 0;
2117             }
2118             };
2119 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2120 0 0 0       if ($self->{$Package}{ATTRIB}{'is_nncli'} && $self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-8[36]\d\d/) { # 8300/8600 NNCLI case
2121 0 0         if ($attrib->{stage} < 4) { # 4th stage
2122 0 0         unless ($attrib->{debugMsg}) {
2123 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show interfaces fastEthernet name\n");
2124 0           $attrib->{debugMsg} = 1;
2125             }
2126 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show interfaces fastEthernet name');
2127 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2128 0           $self->_setSlotPortAttrib($outref);
2129 0           $attrib->{stage}++; # Move to next stage
2130 0           $attrib->{debugMsg} = 0;
2131             }
2132 0 0         unless ($attrib->{debugMsg}) {
2133 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show interfaces gigabitEthernet name\n");
2134 0           $attrib->{debugMsg} = 1;
2135             }
2136 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show interfaces gigabitEthernet name');
2137 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2138 0           $self->_setSlotPortAttrib($outref);
2139 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2140 0           return $self->poll_return(1);
2141             }
2142             else { # All other cases: 8300/8600/8800 PPCLI, 8800 NNCLI, VSP
2143 0 0         unless ($attrib->{debugMsg}) {
2144 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show port info\n");
2145 0           $attrib->{debugMsg} = 1;
2146             }
2147 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show ports info name', 'show interfaces gigabitEthernet high-secure');
2148 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2149 0           $self->_setSlotPortAttrib($outref);
2150 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2151 0           return $self->poll_return(1);
2152             }
2153             };
2154 0 0         $attrib->{attribute} =~ /^(?:is_)?oob_/ && do {
2155 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) { # VSP based PassportERS (VOSS)
2156 0 0         unless ($attrib->{debugMsg}) {
2157 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show ip interface vrf MgmtRouter\n");
2158 0           $attrib->{debugMsg} = 1;
2159             }
2160 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show ip interface vrf MgmtRouter');
2161 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2162 0           my ($ip1, $ip2, $ipv);
2163 0 0         $ip1 = $1 if $$outref =~ /Portmgmt\s+ ([\d\.]+)/g;
2164 0 0         $ip1 = $1 if $$outref =~ /Port1\/1\s+ ([\d\.]+)/g;
2165 0 0         $ipv = $1 if $$outref =~ /MgmtVirtIp\s+ ([\d\.]+)/g;
2166 0 0         $ip2 = $1 if $$outref =~ /Port2\/1\s+ ([\d\.]+)/g;
2167 0 0         $ip2 = $1 if $$outref =~ /Portmgmt2\s+ ([\d\.]+)/g;
2168 0 0         if ($self->{$Package}{ATTRIB}{'cpu_slot'} == 1) { # Could be any VSP: 9k, 8k, 4k
2169 0           $self->_setAttrib('oob_ip', $ip1);
2170 0           $self->_setAttrib('oob_standby_ip', $ip2);
2171 0           $self->_setAttrib('oob_virt_ip', $ipv);
2172             }
2173             else { # cpu slot = 2 only on VSP9000
2174 0           $self->_setAttrib('oob_ip', $ip2);
2175 0           $self->_setAttrib('oob_standby_ip', $ip1);
2176 0           $self->_setAttrib('oob_virt_ip', $ipv);
2177             }
2178             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2179             ( (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ||
2180 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_virt_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_virt_ip'}) ) ?
2181             1 : 0 );
2182 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2183 0           return $self->poll_return(1);
2184             }
2185             else { # ERS based PassportERS
2186 0 0         if ($attrib->{stage} < 4) { # 4th stage
2187 0 0         unless ($attrib->{debugMsg}) {
2188 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys info / show sys-info\n");
2189 0           $attrib->{debugMsg} = 1;
2190             }
2191 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show sys info', 'show sys-info');
2192 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2193             # No need to set Model, Sysname and BaseMAC as we only get here if Model is set
2194 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
      0        
2195             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2196             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2197             ) {
2198 0           $self->_setAttrib('is_dual_cpu', 1);
2199             }
2200             else {
2201 0           $self->_setAttrib('is_dual_cpu', 0);
2202             }
2203 0 0         if ($$outref =~ /Virtual IP\s+: (.+)/g) {
2204 0           $self->_setAttrib('oob_virt_ip', $1);
2205             }
2206             else { # Not set
2207 0           $self->_setAttrib('oob_virt_ip', undef);
2208             }
2209 0           $attrib->{stage}++; # Move to next stage
2210 0           $attrib->{debugMsg} = 0;
2211             }
2212 0 0         unless ($attrib->{debugMsg}) {
2213 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show bootconfig config / show boot config running-config\n");
2214 0           $attrib->{debugMsg} = 1;
2215             }
2216 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'show bootconfig config', 'show boot config running-config');
2217 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2218 0           my ($ip1, $ip2);
2219 0 0         $ip1 = $1 if $$outref =~ /^net mgmt ip ([\d\.]+)\/[\d\.]+ *(?:cpu-slot [35])?$/m;
2220 0 0         $ip2 = $1 if $$outref =~ /^net mgmt ip ([\d\.]+)\/[\d\.]+ cpu-slot 6$/m;
2221 0 0         if ($self->{$Package}{ATTRIB}{'cpu_slot'} < 5) {
    0          
2222 0           $self->_setAttrib('oob_ip', $ip1);
2223 0           $self->_setAttrib('oob_standby_ip', undef);
2224             }
2225             elsif ($self->{$Package}{ATTRIB}{'cpu_slot'} == 5) {
2226 0           $self->_setAttrib('oob_ip', $ip1);
2227 0 0         $self->_setAttrib('oob_standby_ip', $self->{$Package}{ATTRIB}{'is_dual_cpu'} ? $ip2 : undef);
2228             }
2229             else { # cpu slot = 6
2230 0           $self->_setAttrib('oob_ip', $ip2);
2231 0 0         $self->_setAttrib('oob_standby_ip', $self->{$Package}{ATTRIB}{'is_dual_cpu'} ? $ip1 : undef);
2232             }
2233             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2234             ( (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ||
2235 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_virt_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_virt_ip'}) ) ?
2236             1 : 0 );
2237 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2238 0           return $self->poll_return(1);
2239             }
2240             };
2241             }
2242             else { # On standby CPU
2243 0 0         ($attrib->{attribute} eq 'is_apls') && do { # APLS is never dual_cpu
2244 0           $self->_setAttrib('is_apls', 0);
2245 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2246 0           return $self->poll_return(1);
2247             };
2248 0 0         ($attrib->{attribute} eq 'is_voss') && do {
2249 0 0         unless ($attrib->{debugMsg}) {
2250 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: cd /\n");
2251 0           $attrib->{debugMsg} = 1;
2252             }
2253 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, 'cd /', 'cd /');
2254 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2255 0 0         if ($$outref =~ /Only devices \/intflash/) {
2256 0           $self->_setAttrib('is_voss', 1);
2257             }
2258             else {
2259 0           $self->_setAttrib('is_voss', 0);
2260             }
2261 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2262 0           return $self->poll_return(1);
2263             };
2264             }
2265             }
2266             elsif ($familyType eq $Prm{bstk}) {
2267             ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'switch_mode' ||
2268             $attrib->{attribute} eq 'unit_number' || $attrib->{attribute} eq 'base_unit' || $attrib->{attribute} eq 'stack_size' ||
2269 0 0 0       $attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac') && do {
      0        
      0        
      0        
      0        
      0        
      0        
      0        
2270 0 0         unless ($attrib->{debugMsg}) {
2271 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys-info\n");
2272 0           $attrib->{debugMsg} = 1;
2273             }
2274 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show sys-info');
2275 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2276 0 0         if ($$outref =~ /Operation Mode:\s+(Switch)/g) {
    0          
2277 0           $self->_setAttrib('switch_mode', $1);
2278 0           $self->_setAttrib('unit_number', undef);
2279 0           $self->_setAttrib('stack_size', undef);
2280 0           $self->_setAttrib('base_unit', undef);
2281             }
2282             elsif ($$outref =~ /Operation Mode:\s+(Stack), Unit # (\d)/g) {
2283 0           $self->_setAttrib('switch_mode', $1);
2284 0           $self->_setAttrib('unit_number', $2);
2285 0           $$outref =~ /Size Of Stack: (\d)/gc; # Use /gc modifier to maintain position at every match
2286 0           $self->_setAttrib('stack_size', $1);
2287 0           $$outref =~ /Base Unit: (\d)/gc; # With /gc modifiers, fileds have to be matched in the right order
2288 0           $self->_setAttrib('base_unit', $1);
2289             }
2290 0 0         $$outref =~ /MAC Address:\s+(.+)/gc && $self->_setBaseMacAttrib($1);
2291 0 0         $$outref =~ /sysDescr:\s+(.+?)(?:\n|\s{4})/gc && # Match up to end of line, or 4 or more spaces (old baystacks append FW/SW version here)
2292             $self->_setModelAttrib($1);
2293 0 0         $$outref =~ /FW:([\d\.]+)\s+SW:v([\d\.]+)/gc && do {
2294 0           $self->_setAttrib('fw_version', $1);
2295 0           $self->_setAttrib('sw_version', $2);
2296             };
2297 0 0         $$outref =~ /sysName: +(\S+)/gc && $self->_setAttrib('sysname', $1); # \S avoids match when field is blank
2298 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2299 0           return $self->poll_return(1);
2300             };
2301 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2302 0 0         unless ($attrib->{debugMsg}) {
2303 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show interfaces\n");
2304 0           $attrib->{debugMsg} = 1;
2305             }
2306 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show interfaces');
2307 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2308 0           $self->_setSlotPortAttrib($outref);
2309 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2310 0           return $self->poll_return(1);
2311             };
2312 0 0         $attrib->{attribute} eq 'stp_mode' && do {
2313 0 0         unless ($attrib->{debugMsg}) {
2314 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show spanning-tree mode\n");
2315 0           $attrib->{debugMsg} = 1;
2316             }
2317 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show spanning-tree mode');
2318 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2319 0 0         if ($$outref =~ /Current STP Operation Mode: (STPG|MSTP|RSTP)/) {
2320 0           $self->_setAttrib('stp_mode', lc($1));
2321             }
2322             else { # Older stackables will not know the command and only support stpg
2323 0           $self->_setAttrib('stp_mode', 'stpg');
2324             }
2325 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2326 0           return $self->poll_return(1);
2327             };
2328 0 0         $attrib->{attribute} eq 'mgmt_vlan' && do {
2329 0 0         unless ($attrib->{debugMsg}) {
2330 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show vlan mgmt\n");
2331 0           $attrib->{debugMsg} = 1;
2332             }
2333 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show vlan mgmt');
2334 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2335 0 0         $$outref =~ /Management VLAN: (\d+)/ && $self->_setAttrib('mgmt_vlan', $1);
2336 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2337 0           return $self->poll_return(1);
2338             };
2339 0 0 0       ($attrib->{attribute} eq 'mgmt_ip' || $attrib->{attribute} eq 'oob_ip' || $attrib->{attribute} eq 'is_oob_connected') && do {
      0        
2340 0 0         unless ($attrib->{debugMsg}) {
2341 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show ip\n");
2342 0           $attrib->{debugMsg} = 1;
2343             }
2344 0           my ($ok, $outref) = $self->cmdPrivExec($pkgsub, undef, 'show ip');
2345 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2346 0 0         $$outref =~ /(?:Switch|Stack) IP Address:\s+[\d\.]+\s+([\d\.]+)\s+[\d\.]+/g && $self->_setAttrib('mgmt_ip', $1);
2347 0 0         if ($$outref =~ /Mgmt (?:Switch|Stack) IP Address:\s+[\d\.]+\s+([\d\.]+)\s/g) {
2348 0           $self->_setAttrib('oob_ip', $1);
2349             }
2350             else { # No OOB port on this device
2351 0           $self->_setAttrib('oob_ip', undef);
2352             }
2353             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2354 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ?
2355             1 : 0 );
2356 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2357 0           return $self->poll_return(1);
2358             };
2359 0 0         $attrib->{attribute} eq 'baudrate' && do {
2360 0 0         unless ($attrib->{debugMsg}) {
2361 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show terminal\n");
2362 0           $attrib->{debugMsg} = 1;
2363             }
2364 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show terminal'); # Don't need to be in privExec for this
2365 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2366 0 0         if ($$outref =~ /Terminal speed: (\d+)/) {
2367 0           $self->_setAttrib('baudrate', $1);
2368             }
2369 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2370 0           return $self->poll_return(1);
2371             };
2372 0 0         $attrib->{attribute} eq 'max_baud' && do {
2373 0 0         unless ($attrib->{debugMsg}) {
2374 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: terminal speed ?\n");
2375 0           $attrib->{debugMsg} = 1;
2376             }
2377 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, "terminal speed ?$CTRL_C"); # Don't need to be in privExec for this
2378 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2379 0           my $baudRate;
2380 0           while ($$outref =~ /^ (\d+)\s*$/mg) {
2381 0 0 0       $baudRate = $1 if !defined $baudRate || $1 > $baudRate;
2382             }
2383 0           $self->_setAttrib('max_baud', $baudRate);
2384 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2385 0           return $self->poll_return(1);
2386             };
2387             }
2388             elsif ($familyType eq $Prm{sr}) {
2389 0 0         $attrib->{attribute} eq 'model' && do {
2390 0 0         unless ($attrib->{debugMsg}) {
2391 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show chassis\n");
2392 0           $attrib->{debugMsg} = 1;
2393             }
2394 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show chassis');
2395 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2396 0 0         $$outref =~ /Chassis Model: (.+)/ && $self->_setModelAttrib($1);
2397 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2398 0           return $self->poll_return(1);
2399             };
2400 0 0 0       ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version') && do {
2401 0 0         unless ($attrib->{debugMsg}) {
2402 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show version\n");
2403 0           $attrib->{debugMsg} = 1;
2404             }
2405 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show version');
2406 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2407 0 0         $$outref =~ /Runtime: (.+)/g && $self->_setAttrib('sw_version', $1);
2408 0 0         $$outref =~ /Boot: (.+?) / && $self->_setAttrib('fw_version', $1);
2409 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2410 0           return $self->poll_return(1);
2411             };
2412 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2413 0 0         if ($attrib->{stage} < 4) { # 4th stage
2414 0 0         unless ($attrib->{debugMsg}) {
2415 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show interface ethernets\n");
2416 0           $attrib->{debugMsg} = 1;
2417             }
2418 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show interface ethernets');
2419 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2420 0           $self->_setSlotPortAttrib($outref);
2421 0           $attrib->{stage}++; # Move to next stage
2422 0           $attrib->{debugMsg} = 0;
2423             }
2424 0 0         unless ($attrib->{debugMsg}) {
2425 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show module configuration all\n");
2426 0           $attrib->{debugMsg} = 1;
2427             }
2428 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show module configuration all');
2429 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2430 0           $self->_setSlotPortAttrib($outref);
2431 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2432 0           return $self->poll_return(1);
2433             };
2434 0 0         $attrib->{attribute} eq 'sysname' && do {
2435 0 0         unless ($attrib->{debugMsg}) {
2436 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show hostname\n");
2437 0           $attrib->{debugMsg} = 1;
2438             }
2439 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show hostname');
2440 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2441 0 0         $$outref =~ /HostName: (.+)/g && $self->_setAttrib('sysname', $1);
2442 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2443 0           return $self->poll_return(1);
2444             };
2445 0 0         $attrib->{attribute} eq 'base_mac' && do {
2446 0 0         unless ($attrib->{debugMsg}) {
2447 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show system configuration\n");
2448 0           $attrib->{debugMsg} = 1;
2449             }
2450 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show system configuration', 1);
2451 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2452 0 0         $$outref =~ /Mac Address\s+0x(.+)/g && $self->_setBaseMacAttrib($1);
2453 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2454 0           return $self->poll_return(1);
2455             };
2456             }
2457             elsif ($familyType eq $Prm{trpz}) {
2458 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac') && do {
      0        
2459 0 0         unless ($attrib->{debugMsg}) {
2460 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show system\n");
2461 0           $attrib->{debugMsg} = 1;
2462             }
2463 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show system');
2464 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2465 0 0         $$outref =~ /Product Name:\s+(.+)/g && $self->_setModelAttrib($1);
2466 0 0         $$outref =~ /System Name:\s+(.+)/g && $self->_setAttrib('sysname', $1);
2467 0 0         $$outref =~ /System MAC:\s+(.+)/g && $self->_setBaseMacAttrib($1);
2468 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2469 0           return $self->poll_return(1);
2470             };
2471 0 0 0       ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version') && do {
2472 0 0         unless ($attrib->{debugMsg}) {
2473 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show version\n");
2474 0           $attrib->{debugMsg} = 1;
2475             }
2476 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show version');
2477 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2478 0 0         $$outref =~ /Version: (.+?) REL/g && $self->_setAttrib('sw_version', $1);
2479 0 0         $$outref =~ /BootLoader:\s+(.+)/ && $self->_setAttrib('fw_version', $1);
2480 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2481 0           return $self->poll_return(1);
2482             };
2483 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2484 0 0         unless ($attrib->{debugMsg}) {
2485 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show port status\n");
2486 0           $attrib->{debugMsg} = 1;
2487             }
2488 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show port status');
2489 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2490 0           $self->_setSlotPortAttrib($outref);
2491 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2492 0           return $self->poll_return(1);
2493             };
2494             }
2495             elsif ($familyType eq $Prm{xlr}) {
2496 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version')&& do {
      0        
2497 0 0         unless ($attrib->{debugMsg}) {
2498 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show config\n");
2499 0           $attrib->{debugMsg} = 1;
2500             }
2501 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show config', 1);
2502 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2503 0 0         $$outref =~ /# box type\s+: (.+)/g && $self->_setModelAttrib($1);
2504 0 0         $$outref =~ /# boot monitor version\s+: v?(.+)/g && $self->_setAttrib('fw_version', $1);
2505 0 0         $$outref =~ /# software version\s+: v?(.+)/g && $self->_setAttrib('sw_version', $1);
2506 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2507 0           return $self->poll_return(1);
2508             };
2509 0 0 0       ($attrib->{attribute} eq 'is_dual_cpu' || $attrib->{attribute} eq 'sysname') && do {
2510 0 0         unless ($attrib->{debugMsg}) {
2511 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show sys info\n");
2512 0           $attrib->{debugMsg} = 1;
2513             }
2514 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show sys info', 3);
2515 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2516 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && $self->_setModelAttrib($1);
2517 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2518 0 0         if ($$outref =~ /CPU.+ dormant /) {
2519 0           $self->_setAttrib('is_dual_cpu', 1);
2520             }
2521             else {
2522 0           $self->_setAttrib('is_dual_cpu', 0);
2523             }
2524 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2525 0           return $self->poll_return(1);
2526             };
2527 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2528 0 0         unless ($attrib->{debugMsg}) {
2529 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show ports info arp\n");
2530 0           $attrib->{debugMsg} = 1;
2531             }
2532 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show ports info arp');
2533 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2534 0           $self->_setSlotPortAttrib($outref);
2535 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2536 0           return $self->poll_return(1);
2537             };
2538             }
2539             elsif ($familyType eq $Prm{xirrus}) {
2540             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'base_mac' ||
2541 0 0 0       $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version')&& do {
      0        
      0        
2542 0 0         unless ($attrib->{debugMsg}) {
2543 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show system-info\n");
2544 0           $attrib->{debugMsg} = 1;
2545             }
2546 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show system-info');
2547 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2548 0 0         $$outref =~ /Model: (.+?),/g && $self->_setModelAttrib($1);
2549 0 0         $$outref =~ /IAPs\s+(.+?)-/g && $self->_setBaseMacAttrib($1);
2550 0 0         $$outref =~ /Boot Loader\s+(.+?) \(.+?\), Build: (.+)/g && $self->_setAttrib('fw_version', "$1-$2");
2551 0 0         $$outref =~ /System Software\s+(.+?) \(.+?\), Build: (.+)/g && $self->_setAttrib('sw_version', "$1-$2");
2552 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2553 0           return $self->poll_return(1);
2554             };
2555 0 0         ($attrib->{attribute} eq 'sysname') && do {
2556 0 0         unless ($attrib->{debugMsg}) {
2557 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show contact-info\n");
2558 0           $attrib->{debugMsg} = 1;
2559             }
2560 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show contact-info');
2561 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2562 0 0         $$outref =~ /Access Point Hostname\s*(.+)/g && $self->_setAttrib('sysname', $1);
2563 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2564 0           return $self->poll_return(1);
2565             };
2566 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2567 0 0         unless ($attrib->{debugMsg}) {
2568 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: show ethernet\n");
2569 0           $attrib->{debugMsg} = 1;
2570             }
2571 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show ethernet');
2572 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2573 0           $self->_setSlotPortAttrib($outref);
2574 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2575 0           return $self->poll_return(1);
2576             };
2577             }
2578 0           return $self->poll_return(1); # Undefined output_result for unrecognized attributes
2579             }
2580              
2581              
2582             sub poll_change_baudrate { # Method to handle change_baudrate for poll methods (used for both blocking & non-blocking modes)
2583 0     0 1   my $self = shift;
2584 0           my $pkgsub = shift;
2585 0           my $pollsub = "${Package}::change_baudrate";
2586              
2587 0 0         unless ($self->{POLLING}) { # Sanity check
2588 0           my (undef, $fileName, $lineNumber) = caller;
2589 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
2590             }
2591              
2592 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
2593 0           my @validArgs = ('baudrate', 'timeout', 'errmode', 'forcebaud');
2594 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
2595 0 0 0       if (@_ && !%args) { # Legacy syntax
2596 0           ($args{baudrate}, $args{timeout}, $args{errmode}) = @_;
2597             }
2598             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
2599             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
2600             # Set method argument keys
2601             baudrate => $args{baudrate},
2602             parity => undef,
2603             databits => undef,
2604             stopbits => undef,
2605             handshake => undef,
2606             forcebaud => $args{forcebaud},
2607             local_side_only => 0, # For that functionality, just call Control::CLI's poll_change_baudrate
2608             # Declare method storage keys which will be used
2609             stage => 0,
2610             userExec => undef,
2611             privExec => undef,
2612             maxMode => $args{baudrate} eq 'max' ? 1:0,
2613             # Declare keys to be set if method called from another polled method
2614             errmode => $args{errmode},
2615 0 0         };
2616             # Cache poll structure keys which this method will use
2617 0           $self->poll_struct_cache($pollsub, $args{timeout});
2618             }
2619 0           my $changeBaud = $self->{POLL}{$pollsub};
2620 0 0         local $self->{errmode} = $changeBaud->{errmode} if defined $changeBaud->{errmode};
2621 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
2622              
2623 0 0         if ($changeBaud->{local_side_only}) { # Same functionality as Control::CLI::change_baudrate()
2624             my $ok = $self->SUPER::poll_change_baudrate($pkgsub,
2625             BaudRate => $changeBaud->{baudrate},
2626             Parity => $changeBaud->{parity},
2627             DataBits => $changeBaud->{databits},
2628             StopBits => $changeBaud->{stopbits},
2629             Handshake => $changeBaud->{handshake},
2630             ForceBaud => $changeBaud->{forcebaud},
2631 0           );
2632 0           return $self->poll_return($ok); # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode, or completed
2633             }
2634              
2635 0 0         if ($changeBaud->{stage} < 1) { # 1st stage
2636 0 0         unless ($self->connection_type eq 'SERIAL') {
2637 0           return $self->poll_return($self->error("$pkgsub: Cannot change baudrate on Telnet/SSH"));
2638             }
2639 0 0         unless (defined $self->baudrate) { # If no active connection come out
2640 0           return $self->poll_return($self->error("$pkgsub: No serial connection established yet"));
2641             }
2642 0 0         unless (defined $changeBaud->{baudrate}) {
2643 0           return $self->poll_return($self->error("$pkgsub: No baudrate specified!"));
2644             }
2645 0 0         unless ($familyType) {
2646 0           return $self->poll_return($self->error("$pkgsub: Family type of remote device is not detected"));
2647             }
2648 0           $changeBaud->{stage}++; # Move to 2nd stage
2649             }
2650              
2651 0 0         if ($changeBaud->{stage} < 2) { # 2nd stage
2652 0 0         unless (defined $self->{$Package}{ATTRIB}{'baudrate'}) { # Make sure this attribute is set
2653 0           my $ok = $self->poll_attribute($pkgsub, 'baudrate');
2654 0 0         return $self->poll_return($ok) unless $ok;
2655             }
2656 0 0         unless (defined $self->{$Package}{ATTRIB}{'baudrate'}) {
2657 0 0         return $self->poll_return($self->error("$pkgsub: Baudrate cannot be changed on device")) unless $changeBaud->{maxMode};
2658 0           $self->debugMsg(4,"ChangeBaudrate: baudrate attrib undefined - maxMode return success\n");
2659 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
2660 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
2661             }
2662 0 0         unless (defined $self->{$Package}{ATTRIB}{'max_baud'}) { # Make sure this attribute is set
2663 0           my $ok = $self->poll_attribute($pkgsub, 'max_baud');
2664 0 0         return $self->poll_return($ok) unless $ok;
2665             }
2666 0 0 0       if ($changeBaud->{maxMode} && !defined $self->{$Package}{ATTRIB}{'max_baud'}) {
2667 0           $self->debugMsg(4,"ChangeBaudrate: max_baud attrib undefined - maxMode return success\n");
2668 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
2669 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
2670             }
2671 0 0         $changeBaud->{baudrate} = $self->{$Package}{ATTRIB}{'max_baud'} if $changeBaud->{maxMode};
2672              
2673 0 0         if ($changeBaud->{baudrate} == $self->baudrate) { # Desired baudrate is already set
2674 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
2675 0           return $self->poll_return(1);
2676             }
2677              
2678             # Now, depending on family type of connected device, ensure we change the baud rate on the device first
2679 0 0         if ($familyType eq $Prm{generic}) {
    0          
    0          
2680 0           return $self->poll_return($self->error("$pkgsub: Unable to complete on $Prm{generic} family_type device"));
2681             }
2682             elsif ($familyType eq $Prm{bstk}) {
2683 0 0 0       unless ($changeBaud->{baudrate} == 9600 || $changeBaud->{baudrate} == 19200 || $changeBaud->{baudrate} == 38400) {
      0        
2684 0           return $self->poll_return($self->error("$pkgsub: Supported baud rates for $Prm{bstk} = 9600, 19200, 38400"));
2685             }
2686             }
2687             elsif ($familyType eq $Prm{pers}) {
2688 0 0 0       unless ($changeBaud->{baudrate} == 9600 || $changeBaud->{baudrate} == 19200 || $changeBaud->{baudrate} == 38400 ||
      0        
      0        
      0        
2689             $changeBaud->{baudrate} == 57600 || $changeBaud->{baudrate} == 115200) {
2690 0           return $self->poll_return($self->error("$pkgsub: Supported baud rates for $Prm{pers} = 9600, 19200, 38400, 57600, 115200"));
2691             }
2692             }
2693             else { # Other Avaya family types not supported
2694 0 0         return $self->poll_return($self->error("$pkgsub: Only supported on $Prm{pers} and $Prm{bstk} family_type")) unless $changeBaud->{maxMode};
2695 0           $self->debugMsg(4,"ChangeBaudrate: Not $Prm{pers} or $Prm{bstk} family_type - maxMode return success\n");
2696 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
2697 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
2698             }
2699 0           $changeBaud->{stage}++; # Move to 3rd stage
2700             }
2701              
2702 0 0         if ($changeBaud->{stage} < 3) { # 3rd stage
2703 0 0         if ($familyType eq $Prm{pers}) {
2704 0 0         unless (defined $self->{$Package}{ATTRIB}{'model'}) { # Make sure this attribute is set
2705 0           my $ok = $self->poll_attribute($pkgsub, 'model');
2706 0 0         return $self->poll_return($ok) unless $ok;
2707             }
2708 0 0         if ($changeBaud->{userExec} = $self->last_prompt =~ />\s?$/) {
2709 0           my $ok = $self->poll_enable($pkgsub);
2710 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2711             }
2712             }
2713 0           $changeBaud->{stage}++; # Move to 4th stage
2714             }
2715              
2716 0 0         if ($changeBaud->{stage} < 4) { # 4th stage
2717 0 0 0       if ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) {
2718 0 0         if ($changeBaud->{privExec} = $self->last_prompt !~ /\(config/) {
2719 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'config term');
2720 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2721 0 0         return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device")) unless $$resref;
2722             }
2723             }
2724 0           $changeBaud->{stage}++; # Move to 5th stage
2725             }
2726              
2727 0 0         if ($changeBaud->{stage} < 5) { # 5th stage
2728 0 0         if ($familyType eq $Prm{bstk}) {
    0          
2729 0 0         $self->print(line => "terminal speed $changeBaud->{baudrate}", errmode => 'return')
2730             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
2731             }
2732             elsif ($familyType eq $Prm{pers}) {
2733 0 0         if ($self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-(?:83|16)\d\d/) { # 8300 & 1600
2734 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
2735 0 0         $self->print(line => "boot config sio baud $changeBaud->{baudrate}", errmode => 'return')
2736             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
2737             }
2738             else {
2739 0 0         $self->print(line => "config bootconfig sio baud $changeBaud->{baudrate}", errmode => 'return')
2740             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
2741             }
2742             }
2743             else { # All other PassportERS devices
2744 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
2745 0 0         $self->print(line => "boot config sio console baud $changeBaud->{baudrate}", errmode => 'return')
2746             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
2747             }
2748             else {
2749 0 0         $self->print(line => "config bootconfig sio console baud $changeBaud->{baudrate}", errmode => 'return')
2750             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
2751             }
2752             }
2753             }
2754 0           $self->debugMsg(4,"ChangeBaudrate: set device to ", \$changeBaud->{baudrate}, "\n");
2755 0           $changeBaud->{stage}++; # Move to 6th stage
2756             }
2757              
2758 0 0         if ($changeBaud->{stage} < 6) { # 6th stage
2759 0           my $ok = $self->poll_readwait($pkgsub, 0);
2760 0 0         return $self->poll_return($ok) unless $ok; # Come out if error, or if nothing to read in non-blocking mode
2761 0 0 0       if (length $self->{POLL}{read_buffer} && $self->{POLL}{read_buffer} =~ /$self->{$Package}{prompt_qr}/) {
2762             # This is a failure, as it would imply that we can see a prompt back, even though we changed the baudrate on the device
2763 0 0         return $self->poll_return($self->error("$pkgsub: Baudrate change had no effect on device; still at $self->baudrate baud")) unless $changeBaud->{maxMode};
2764 0           $self->debugMsg(4,"ChangeBaudrate: Baudrate change had no effect on device - maxMode return success\n");
2765 0           $self->{POLL}{output_result} = $self->baudrate;
2766 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
2767             }
2768 0 0         if (defined $self->{$Package}{ORIGBAUDRATE}) { # Clear note following restore
2769 0 0         $self->{$Package}{ORIGBAUDRATE} = undef if $self->{$Package}{ORIGBAUDRATE} == $changeBaud->{baudrate};
2770             }
2771             else { # 1st time this method is run, make a note of original baudrate (needed in DESTROY)
2772 0           $self->{$Package}{ORIGBAUDRATE} = $self->baudrate;
2773             }
2774 0           $changeBaud->{stage}++; # Move to 7th stage
2775             }
2776              
2777 0 0         if ($changeBaud->{stage} < 7) { # 7th stage
2778             my $ok = $self->SUPER::poll_change_baudrate($pkgsub,
2779             BaudRate => $changeBaud->{baudrate},
2780             ForceBaud => $changeBaud->{forcebaud},
2781 0           );
2782 0 0         return $self->poll_return($ok) unless $ok; # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode
2783 0           $self->debugMsg(4,"ChangeBaudrate: changed local serial port to ", \$changeBaud->{baudrate}, "\n");
2784 0           $self->_setAttrib('baudrate', $changeBaud->{baudrate}); # Adjust the attribute as we are sure we changed it now
2785 0           $changeBaud->{stage}++; # Move to 8th stage
2786             }
2787              
2788 0 0         if ($changeBaud->{stage} < 8) { # 8th stage
2789 0           my $ok = $self->poll_cmd($pkgsub, ''); # Send carriage return + ensure we get valid prompt back
2790 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2791 0           $changeBaud->{stage}++; # Move to 9th stage
2792             }
2793              
2794 0 0         if ($changeBaud->{stage} < 9) { # 9th stage
2795 0 0 0       if ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) {
2796 0 0         if ($changeBaud->{privExec}) {
2797 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'exit');
2798 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2799 0 0         return $self->poll_return($self->error("$pkgsub: Error while changing baud rate")) unless $$resref;
2800             }
2801             }
2802 0           $changeBaud->{stage}++; # Move to 10th stage
2803             }
2804              
2805 0 0         if ($changeBaud->{stage} < 10) { # 10th stage
2806 0 0 0       if ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) {
2807 0 0         if ($changeBaud->{userExec}) {
2808 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'disable');
2809 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2810 0 0         return $self->poll_return($self->error("$pkgsub: Error while changing baud rate")) unless $$resref;
2811             }
2812             }
2813             }
2814 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
2815 0           return $self->poll_return(1);
2816             }
2817              
2818              
2819             sub poll_enable { # Method to handle enable for poll methods (used for both blocking & non-blocking modes)
2820 0     0 1   my $self = shift;
2821 0           my $pkgsub = shift;
2822 0           my $pollsub = "${Package}::enable";
2823              
2824 0 0         unless ($self->{POLLING}) { # Sanity check
2825 0           my (undef, $fileName, $lineNumber) = caller;
2826 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
2827             }
2828              
2829 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
2830 0           my @validArgs = ('password', 'prompt_credentials', 'timeout', 'errmode');
2831 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
2832 0 0 0       if (@_ && !%args) { # Legacy syntax
2833 0           ($args{password}, $args{prompt_credentials}, $args{timeout}, $args{errmode}) = @_;
2834             }
2835             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
2836             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
2837             # Set method argument keys
2838             enable_password => defined $args{password} ? $args{password} : $self->{$Package}{ENABLEPWD},
2839             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
2840             # Declare method storage keys which will be used
2841             stage => 0,
2842             login_attempted => undef,
2843             login_failed => undef,
2844             # Declare keys to be set if method called from another polled method
2845             errmode => $args{errmode},
2846 0 0         };
    0          
2847             # Cache poll structure keys which this method will use
2848 0           $self->poll_struct_cache($pollsub, $args{timeout});
2849             }
2850 0           my $enable = $self->{POLL}{$pollsub};
2851 0 0         local $self->{errmode} = $enable->{errmode} if defined $enable->{errmode};
2852 0 0         return $self->poll_return($self->error("$pkgsub: No connection to enable")) if $self->eof;
2853 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
2854 0           my $prompt = $self->{$Package}{prompt_qr};
2855 0           my $passwordPrompt = $self->{password_prompt_qr};
2856 0           my $enablePwd;
2857              
2858 0 0         if ($enable->{stage} < 1) { # 1st stage
2859 0           $enable->{stage}++; # Ensure we don't come back here in non-blocking mode
2860 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) unless $familyType;
2861 0 0         return $self->poll_return(1) unless $self->{$Package}{ATTRIB}{'is_nncli'}; # Come out if not in NNCLI mode
2862 0 0         return $self->poll_return(1) unless $self->last_prompt =~ />\s?$/; # Come out if not in UserExec mode
2863             # Flush any unread data which might be pending
2864 0           $self->read(blocking => 0);
2865             # Send enable command
2866 0 0         $self->print(line => 'enable', errmode => 'return')
2867             or return $self->poll_return($self->error("$pkgsub: Unable to send CLI command: enable // ".$self->errmsg));
2868             }
2869              
2870             # Main loop
2871             do {
2872 0           my $ok = $self->poll_read($pkgsub, 'Failed after enable command');
2873 0 0         return $self->poll_return($ok) unless $ok;
2874              
2875 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer};
2876 0 0         $enable->{login_failed}++ if $self->{POLL}{local_buffer} =~ /error: Access denied/;
2877 0 0         if ($self->{POLL}{local_buffer} =~ /$passwordPrompt/) { # Handle password prompt
2878 0           $enable->{login_attempted}++;
2879 0 0         if (defined $enable->{enable_password}) { # An enable password is supplied
2880 0 0         if ($enable->{login_attempted} == 1) { # First try; use supplied
2881 0           $enablePwd = $enable->{enable_password};
2882 0           $self->debugMsg(4,"enable() Sending supplied password\n");
2883 0 0         $self->print(line => $enablePwd, errmode => 'return')
2884             or return $self->poll_return($self->error("$pkgsub: Unable to send enable password // ".$self->errmsg));
2885             }
2886             else { # Next tries, enter blanks
2887 0           $enablePwd = '';
2888 0           $self->debugMsg(4,"enable() Sending carriage return instead of supplied password\n");
2889 0 0         $self->print(errmode => 'return')
2890             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
2891             }
2892             }
2893             else { # No password supplied
2894 0 0         if ($enable->{login_attempted} == 1) { # First try; use blank
    0          
2895 0           $enablePwd = '';
2896 0           $self->debugMsg(4,"enable() Sending carriage return for password\n");
2897 0 0         $self->print(errmode => 'return')
2898             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
2899             }
2900             elsif ($enable->{login_attempted} == 2) { # Second try; use cached login password
2901 0   0       $enablePwd = $self->password || '';
2902 0           $self->debugMsg(4,"enable() Sending login password for enable password\n");
2903 0 0         $self->print(line => $enablePwd, errmode => 'return')
2904             or return $self->poll_return($self->error("$pkgsub: Unable to send cached password // ".$self->errmsg));
2905             }
2906             else { # Third try; prompt?
2907 0 0         if ($enable->{prompt_credentials}) {
2908 0           $enablePwd = promptCredential($enable->{prompt_credentials}, 'Hide', 'Enable Password');
2909 0 0         $self->print(line => $enablePwd, errmode => 'return')
2910             or return $self->poll_return($self->error("$pkgsub: Unable to send enable password // ".$self->errmsg));
2911             }
2912             else { # Enter blanks
2913 0           $enablePwd = '';
2914 0           $self->debugMsg(4,"enable() Sending carriage return instead of prompting for password\n");
2915 0 0         $self->print(errmode => 'return')
2916             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
2917             }
2918             }
2919             }
2920 0           $self->{POLL}{local_buffer} = '';
2921             }
2922 0           } until ($self->{POLL}{local_buffer} =~ /($prompt)/);
2923 0           $self->{$Package}{CONFIGCONTEXT} = $2;
2924 0           ($self->{LASTPROMPT} = $1) =~ s/$LastPromptClense//o; # Remove initial carriage return if there
2925 0 0         return $self->poll_return($self->error("$pkgsub: Password required")) if $enable->{login_failed};
2926 0 0         return $self->poll_return($self->error("$pkgsub: Failed to enter PrivExec mode")) if $self->last_prompt =~ />\s?$/; # If still in UserExec mode
2927 0 0         $self->{$Package}{ENABLEPWD} = $enablePwd if defined $enablePwd;
2928 0           return $self->poll_return(1);
2929             }
2930              
2931              
2932             sub poll_device_more_paging { # Method to handle device_more_paging for poll methods (used for both blocking & non-blocking modes)
2933 0     0 1   my $self = shift;
2934 0           my $pkgsub = shift;
2935 0           my $pollsub = "${Package}::device_more_paging";
2936              
2937 0 0         unless ($self->{POLLING}) { # Sanity check
2938 0           my (undef, $fileName, $lineNumber) = caller;
2939 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
2940             }
2941              
2942 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
2943 0           my @validArgs = ('enable', 'timeout', 'errmode');
2944 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
2945 0 0 0       if (@_ && !%args) { # Legacy syntax
2946 0           ($args{enable}, $args{timeout}, $args{errmode}) = @_;
2947             }
2948             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
2949             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
2950             # Set method argument keys
2951             enable => $args{enable},
2952             # Declare method storage keys which will be used
2953             stage => 0,
2954             cmdString => undef,
2955             # Declare keys to be set if method called from another polled method
2956             errmode => $args{errmode},
2957 0           };
2958             # Cache poll structure keys which this method will use
2959 0           $self->poll_struct_cache($pollsub, $args{timeout});
2960             }
2961 0           my $devMorePage = $self->{POLL}{$pollsub};
2962 0 0         local $self->{errmode} = $devMorePage->{errmode} if defined $devMorePage->{errmode};
2963 0 0         return $self->poll_return($self->error("$pkgsub: No connection to set more paging on")) if $self->eof;
2964 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
2965              
2966 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) unless $familyType;
2967 0 0 0       if ($familyType eq $Prm{bstk}) {
    0          
    0          
    0          
    0          
2968 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
2969 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "terminal length $devMorePage->{cmdString}");
2970 0 0         return $self->poll_return($ok) unless $ok;
2971 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
2972             }
2973             elsif ($familyType eq $Prm{pers} || $familyType eq $Prm{xlr}) {
2974 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) { # NNCLI
2975 0 0         if ($devMorePage->{stage} < 1) {
2976 0 0         unless (defined $self->{$Package}{ATTRIB}{'model'}) { # This attribute may not yet be set
2977 0           my $ok = $self->poll_attribute($pkgsub, 'model');
2978 0 0         return $self->poll_return($ok) unless $ok;
2979             }
2980 0 0 0       if (defined $self->{$Package}{ATTRIB}{'model'} && $self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-83\d\d/) { # 8300 NNCLI
2981 0           $devMorePage->{stage} += 2; # Go to section after next
2982             }
2983             else { # NNCLI on 8600 or VSP (or if 'model' is not defined we could be on a Standby CPU of 8600 or VSP or 8300..)
2984 0           $devMorePage->{stage}++; # Go to next section
2985             }
2986             }
2987 0 0         if ($devMorePage->{stage} < 2) { # NNCLI on 8600 or VSP (or if 'model' is not defined we could be on a Standby CPU of 8600 or VSP or 8300..)
2988 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'enable' : 'disable' unless defined $devMorePage->{cmdString};
    0          
2989 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "terminal more $devMorePage->{cmdString}");
2990 0 0         return $self->poll_return($ok) unless $ok;
2991 0 0 0       return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) if !$$resref && defined $self->{$Package}{ATTRIB}{'model'};
2992 0           $devMorePage->{stage}++; # Go to next section (8300) if we failed here and 'model' attrib not defined
2993 0 0         $devMorePage->{stage}++ if $$resref; # Skip next section if we succeded
2994 0           $devMorePage->{cmdString} = undef;
2995             }
2996 0 0         if ($devMorePage->{stage} < 3) { # 8300 NNCLI
2997 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '' : 'no ' unless defined $devMorePage->{cmdString};
    0          
2998 0           my ($ok, undef, $resref) = $self->cmdConfig($pkgsub, '', "$devMorePage->{cmdString}more");
2999 0 0         return $self->poll_return($ok) unless $ok;
3000 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3001             }
3002             }
3003             else { # CLI
3004 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'true' : 'false' unless defined $devMorePage->{cmdString};
    0          
3005 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "config cli more $devMorePage->{cmdString}");
3006 0 0         return $self->poll_return($ok) unless $ok;
3007 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3008             }
3009             }
3010             elsif ($familyType eq $Prm{sr}) {
3011 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
3012 0           my ($ok, undef, $resref) = $self->cmdConfig($pkgsub, '', "terminal length $devMorePage->{cmdString}");
3013 0 0         return $self->poll_return($ok) unless $ok;
3014 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3015             }
3016             elsif ($familyType eq $Prm{trpz}) {
3017 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
3018 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "set length $devMorePage->{cmdString}");
3019 0 0         return $self->poll_return($ok) unless $ok;
3020 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3021             }
3022             elsif ($familyType eq $Prm{xirrus}) {
3023 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'enable' : 'disable' unless defined $devMorePage->{cmdString};
    0          
3024 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "more $devMorePage->{cmdString}");
3025 0 0         return $self->poll_return($ok) unless $ok;
3026 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3027             }
3028             else {
3029 0           return $self->poll_return($self->error("$pkgsub: Cannot configure more paging on family type $familyType"));
3030             }
3031 0           return $self->poll_return(1);
3032             }
3033              
3034              
3035             sub poll_device_peer_cpu { # Method to handle device_peer_cpu for poll methods (used for both blocking & non-blocking modes)
3036 0     0 1   my $self = shift;
3037 0           my $pkgsub = shift;
3038 0           my $pollsub = "${Package}::device_peer_cpu";
3039              
3040 0 0         unless ($self->{POLLING}) { # Sanity check
3041 0           my (undef, $fileName, $lineNumber) = caller;
3042 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
3043             }
3044              
3045 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
3046 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode');
3047 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
3048 0 0 0       if (@_ && !%args) { # Legacy syntax
3049 0           ($args{username}, $args{password}, $args{prompt_credentials}, $args{timeout}, $args{errmode}) = @_;
3050             }
3051             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
3052             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
3053             # Set method argument keys
3054             username => defined $args{username} ? $args{username} : $self->username,
3055             password => defined $args{password} ? $args{password} : $self->password,
3056             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
3057             # Declare method storage keys which will be used
3058             stage => 0,
3059             # Declare keys to be set if method called from another polled method
3060             errmode => $args{errmode},
3061 0 0         };
    0          
    0          
3062             # Cache poll structure keys which this method will use
3063 0           $self->poll_struct_cache($pollsub, $args{timeout});
3064             }
3065 0           my $devPeerCpu = $self->{POLL}{$pollsub};
3066 0 0         local $self->{errmode} = $devPeerCpu->{errmode} if defined $devPeerCpu->{errmode};
3067 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) if $self->eof;
3068 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3069              
3070 0 0         if ($devPeerCpu->{stage} < 1) { # 1st stage
3071 0 0         unless ($familyType) {
3072 0           return $self->poll_return($self->error("$pkgsub: Attribute family_type not set"));
3073             }
3074 0 0         unless ($familyType eq $Prm{pers}) {
3075 0           return $self->poll_return($self->error("$pkgsub: No peer CPU on family_type $familyType"));
3076             }
3077 0 0 0       unless (($devPeerCpu->{username} && $devPeerCpu->{password}) || $devPeerCpu->{prompt_credentials}) {
      0        
3078 0           return $self->poll_return($self->error("$pkgsub: Username & password required"));
3079             }
3080 0           $devPeerCpu->{stage}++; # Move to 2nd stage
3081             }
3082              
3083 0 0         if ($devPeerCpu->{stage} < 2) { # 2nd stage
3084 0           my $ok = $self->poll_enable($pkgsub); # If in nncli mode, need to be in PrivExec
3085 0 0         return $self->poll_return($ok) unless $ok;
3086              
3087 0 0         $self->print(line => 'peer telnet', errmode => 'return')
3088             or return $self->poll_return($self->error("$pkgsub: Unable to send peer telnet command // ".$self->errmsg));
3089 0           $devPeerCpu->{stage}++; # Move to 3rd stage
3090             }
3091              
3092 0 0         if ($devPeerCpu->{stage} < 3) { # 3rd stage
3093 0           my $ok = $self->poll_waitfor($pkgsub, 'Login: $', undef, 'return');
3094 0 0         return $self->poll_return($self->error("$pkgsub: Never got peer login prompt // ".$self->errmsg)) unless defined $ok;
3095 0 0         return $self->poll_return($ok) unless $ok;
3096              
3097 0 0         $devPeerCpu->{username} = promptCredential($devPeerCpu->{prompt_credentials}, 'Clear', 'Username') unless defined $devPeerCpu->{username};
3098 0 0         $self->print(line => $devPeerCpu->{username}, errmode => 'return')
3099             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
3100 0           $devPeerCpu->{stage}++; # Move to 4th stage
3101             }
3102              
3103 0 0         if ($devPeerCpu->{stage} < 4) { # 4th stage
3104 0           my $ok = $self->poll_waitfor($pkgsub, 'Password: $', undef, 'return');
3105 0 0         return $self->poll_return($self->error("$pkgsub: Never got peer password prompt // ".$self->errmsg)) unless defined $ok;
3106 0 0         return $self->poll_return($ok) unless $ok;
3107              
3108 0 0         $devPeerCpu->{password} = promptCredential($devPeerCpu->{prompt_credentials}, 'Hide', 'Password') unless defined $devPeerCpu->{password};
3109 0 0         $self->print(line => $devPeerCpu->{password}, errmode => 'return')
3110             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
3111 0           $devPeerCpu->{stage}++; # Move to last stage
3112             }
3113              
3114             # Use cmd() to expect a new prompt now
3115 0           my $ok = $self->poll_cmd($pkgsub, More_pages => 0, Reset_prompt => 1);
3116 0 0         return $self->poll_return($ok) unless $ok;
3117              
3118 0           $self->{LASTPROMPT} =~ /$InitPrompt{$self->{$Package}{PROMPTTYPE}}/;
3119 0           $self->_setAttrib('cpu_slot', $2);
3120 0 0         $self->_setAttrib('is_master_cpu', $self->{LASTPROMPT} =~ /^@/ ? 0 : 1);
3121 0 0         $self->_setAttrib('is_dual_cpu', 1) if $self->{LASTPROMPT} =~ /^@/;
3122 0           return $self->poll_return(1);
3123             }
3124              
3125              
3126             sub cmdPrivExec { # If nncli send command in PrivExec mode and restore mode on exit; if not nncli just sends command; used for show commands
3127 0     0 1   my ($self, $pkgsub, $cmdcli, $cmdnncli, $morePages) = @_;
3128 0           my $pollsub = "${Package}::cmdPrivExec";
3129 0           my ($ok, $outref, $resref);
3130              
3131 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3132 0           $self->{POLL}{$pollsub} = {
3133             stage => 0,
3134             userExec => undef,
3135             outref => undef,
3136             resref => undef,
3137             };
3138             }
3139 0           my $cmdPrivExec = $self->{POLL}{$pollsub};
3140              
3141 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3142 0 0         if ($cmdPrivExec->{stage} < 1) { # 1st stage
3143 0 0         if ($cmdPrivExec->{userExec} = $self->last_prompt =~ />\s?$/) {
3144 0           $ok = $self->poll_enable($pkgsub);
3145 0 0         return $ok unless $ok;
3146             }
3147 0           $cmdPrivExec->{stage}++; # Move to 2nd stage
3148             }
3149 0 0         if ($cmdPrivExec->{stage} < 2) { # 2nd stage
3150 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, Command => $cmdnncli, More_pages => $morePages);
3151 0 0         return $ok unless $ok;
3152 0           $cmdPrivExec->{outref} = $outref;
3153 0           $cmdPrivExec->{resref} = $resref;
3154 0           $cmdPrivExec->{stage}++; # Move to 3rd stage
3155             }
3156 0 0         if ($cmdPrivExec->{stage} < 3) { # 3rd stage
3157 0 0         if ($cmdPrivExec->{userExec}) {
3158 0           ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'disable');
3159 0 0         return $ok unless $ok;
3160 0 0         return ($ok, undef, $resref) unless $$resref;
3161             }
3162 0           ($outref, $resref) = ($cmdPrivExec->{outref}, $cmdPrivExec->{resref});
3163 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3164 0           return (1, $outref, $resref);
3165             }
3166             }
3167             else {
3168 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, Command => $cmdcli, More_pages => $morePages);
3169 0 0         return $ok unless $ok;
3170 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3171 0           return (1, $outref, $resref);
3172             }
3173             }
3174              
3175              
3176             sub cmdConfig { # If nncli send command in Config mode and restore mode on exit; if not nncli just sends command; used for config commands
3177 0     0 1   my ($self, $pkgsub, $cmdcli, $cmdnncli) = @_;
3178 0           my $pollsub = "${Package}::cmdConfig";
3179 0           my ($ok, $outref, $resref);
3180              
3181 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3182 0           $self->{POLL}{$pollsub} = {
3183             stage => 0,
3184             userExec => undef,
3185             privExec => undef,
3186             outref => undef,
3187             resref => undef,
3188             };
3189             }
3190 0           my $cmdConfig = $self->{POLL}{$pollsub};
3191              
3192 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3193 0 0         if ($cmdConfig->{stage} < 1) { # 1st stage
3194 0 0         if ($cmdConfig->{userExec} = $self->last_prompt =~ />\s?$/) {
3195 0           $ok = $self->poll_enable($pkgsub);
3196 0 0         return $ok unless $ok;
3197             }
3198 0           $cmdConfig->{stage}++; # Move to 2nd stage
3199             }
3200 0 0         if ($cmdConfig->{stage} < 2) { # 2nd stage
3201 0 0         if ($cmdConfig->{privExec} = $self->last_prompt !~ /[\(\/]config/) { # This needs to match '(config[-if])' or SecureRouter '/configure'
3202 0           ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'config term');
3203 0 0         return $ok unless $ok;
3204 0 0         return ($ok, undef, $resref) unless $$resref;
3205             }
3206 0           $cmdConfig->{stage}++; # Move to 3rd stage
3207             }
3208 0 0         if ($cmdConfig->{stage} < 3) { # 3rd stage
3209 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, $cmdnncli);
3210 0 0         return $ok unless $ok;
3211 0           $cmdConfig->{outref} = $outref;
3212 0           $cmdConfig->{resref} = $resref;
3213 0           $cmdConfig->{stage}++; # Move to 4th stage
3214             }
3215 0 0         if ($cmdConfig->{stage} < 4) { # 4th stage
3216 0 0         if ($cmdConfig->{privExec}) {
3217 0           ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'end');
3218 0 0         return $ok unless $ok;
3219 0 0         return ($ok, undef, $resref) unless $$resref;
3220             }
3221 0           $cmdConfig->{stage}++; # Move to 5th stage
3222             }
3223 0 0         if ($cmdConfig->{stage} < 5) { # 5th stage
3224 0 0         if ($cmdConfig->{userExec}) {
3225 0           ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'disable');
3226 0 0         return $ok unless $ok;
3227 0 0         return ($ok, undef, $resref) unless $$resref;
3228             }
3229 0           ($outref, $resref) = ($cmdConfig->{outref}, $cmdConfig->{resref});
3230 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3231 0           return (1, $outref, $resref);
3232             }
3233             }
3234             else {
3235 0 0         $cmdcli = "config $cmdcli" unless $cmdcli =~ /^config /; # Prepend config if not already there
3236 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, $cmdcli);
3237 0 0         return $ok unless $ok;
3238 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3239 0           return (1, $outref, $resref);
3240             }
3241             }
3242              
3243              
3244             sub discoverDevice { # Issues CLI commands to host, to determine what family type it belongs to
3245 0     0 1   my ($self, $pkgsub) = @_;
3246 0           my $pollsub = "${Package}::discoverDevice";
3247              
3248 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3249 0           $self->{POLL}{$pollsub} = {
3250             stage => 0,
3251             };
3252             }
3253 0           my $discDevice = $self->{POLL}{$pollsub};
3254              
3255 0 0         if ($discDevice->{stage} < 1) { # Initial loginstage checking - do only once
3256 0           $discDevice->{stage}++; # Ensure we don't come back here in non-blocking mode
3257 0           $self->debugMsg(4,"\nATTEMPTING EXTENDED DISCOVERY OF HOST DEVICE !\n");
3258              
3259             # Output from commands below is prone to false triggers on the generic prompt;
3260             # so we lock it down to the minimum length required
3261 0           $self->last_prompt =~ /(.*)([\?\$%#>]\s?)$/;
3262 0           $self->prompt(join('', ".{", length($1), ",}$2\$"));
3263             }
3264              
3265             # Prefer commands unique to platform, and with small output (not more paged)
3266              
3267 0 0         if ($discDevice->{stage} < 2) { # Next stage
3268             # BaystackERS detection command
3269 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show ip address');
3270 0 0         return $ok unless $ok;
3271 0           $discDevice->{stage}++; # Move to next stage on next cycle
3272 0 0         if ($$outref =~ /\s+Configured\s+In Use\s+Last BootP/) {
3273 0           $self->_setAttrib('family_type', $Prm{bstk});
3274 0           $self->_setAttrib('is_nncli', 1);
3275 0           $self->{$Package}{PROMPTTYPE} = $Prm{bstk};
3276 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3277 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{bstk}}/;
3278 0           $self->_setDevicePrompts($Prm{bstk}, $1);
3279 0           return (1, $Prm{bstk});
3280             }
3281             }
3282 0 0         if ($discDevice->{stage} < 3) { # Next stage
3283             # PassportERS-nncli detection command
3284 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show basic config');
3285 0 0         return $ok unless $ok;
3286 0           $discDevice->{stage}++; # Move to next stage on next cycle
3287 0 0         if ($$outref =~ /^\s+auto-recover-delay :/m) {
3288 0           $self->_setAttrib('family_type', $Prm{pers});
3289 0           $self->_setAttrib('is_nncli', 1);
3290 0           $self->_setAttrib('is_master_cpu', 1);
3291 0           $self->{$Package}{PROMPTTYPE} ="$Prm{pers}_nncli";
3292 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3293 0           $self->{LASTPROMPT} =~ /$InitPrompt{"$Prm{pers}_nncli"}/;
3294 0           $self->_setDevicePrompts("$Prm{pers}_nncli", $1);
3295 0           return (1, $Prm{pers});
3296             }
3297             }
3298 0 0         if ($discDevice->{stage} < 4) { # Next stage
3299             # PassportERS-cli detection command
3300 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show bootconfig info');
3301 0 0         return $ok unless $ok;
3302 0           $discDevice->{stage}++; # Move to next stage on next cycle
3303 0 0         if ($$outref =~ /^Version:\s+(?i:v|REL)?(.+)/m) {
3304 0           $self->_setAttrib('fw_version', $1);
3305 0           $self->_setAttrib('family_type', $Prm{pers});
3306 0           $self->_setAttrib('is_nncli', 0);
3307 0           $self->_setAttrib('is_master_cpu', 1);
3308 0           $self->{$Package}{PROMPTTYPE} = "$Prm{pers}_cli";
3309 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3310 0           $self->{LASTPROMPT} =~ /$InitPrompt{"$Prm{pers}_cli"}/;
3311 0           $self->_setDevicePrompts("$Prm{pers}_cli", $1);
3312 0           return (1, $Prm{pers});
3313             }
3314             }
3315 0 0         if ($discDevice->{stage} < 5) { # Next stage
3316             # WLAN 9100 detection command
3317 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show contact-info');
3318 0 0         return $ok unless $ok;
3319 0           $discDevice->{stage}++; # Move to next stage on next cycle
3320 0 0         if ($$outref =~ /^Access Point Hostname\s*(.+)$/m) {
3321 0           my $sysname = $1;
3322 0           $self->_setAttrib('family_type', $Prm{xirrus});
3323 0           $self->_setAttrib('is_nncli', 1);
3324 0           $self->_setAttrib('sysname', $sysname);
3325 0           $self->{$Package}{PROMPTTYPE} = $Prm{xirrus};
3326 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3327 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{xirrus}}/;
3328 0           $self->_setDevicePrompts($Prm{xirrus}, $1);
3329 0           return (1, $Prm{xirrus});
3330             }
3331             }
3332 0 0         if ($discDevice->{stage} < 6) { # Next stage
3333             # Secure Router detection command
3334 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show chassis');
3335 0 0         return $ok unless $ok;
3336 0           $discDevice->{stage}++; # Move to next stage on next cycle
3337 0 0         if ($$outref =~ /^Chassis Model: (.+)$/m) {
3338 0           my $model = $1;
3339 0           $self->_setAttrib('family_type', $Prm{sr});
3340 0           $self->_setAttrib('is_nncli', 1);
3341 0           $self->_setModelAttrib($model);
3342 0           $self->{$Package}{PROMPTTYPE} = $Prm{sr};
3343 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3344 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{sr}}/;
3345 0           $self->_setDevicePrompts($Prm{sr}, $1);
3346 0           return (1, $Prm{sr});
3347             }
3348             }
3349 0 0         if ($discDevice->{stage} < 7) { # Next stage
3350             # WLAN 2300 detection command
3351 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show system');
3352 0 0         return $ok unless $ok;
3353 0           $discDevice->{stage}++; # Move to next stage on next cycle
3354 0 0         if ($$outref =~ /Product Name:\s+(.+)/g) {
3355 0           my $model = $1;
3356 0           $self->_setAttrib('family_type', $Prm{trpz});
3357 0           $self->_setAttrib('is_nncli', 1);
3358 0           $self->_setModelAttrib($model);
3359 0 0         $$outref =~ /System Name:\s+(.+)/g && $self->_setAttrib('sysname', $1);
3360 0 0         $$outref =~ /System MAC:\s+(.+)/g && $self->_setBaseMacAttrib($1);
3361 0           $self->{$Package}{PROMPTTYPE} = $Prm{trpz};
3362 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3363 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{trpz}}/;
3364 0           $self->_setDevicePrompts($Prm{trpz}, $1);
3365 0           return (1, $Prm{trpz});
3366             }
3367             }
3368 0 0         if ($discDevice->{stage} < 8) { # Next stage
3369             # Accelar detection command
3370 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show sys perf');
3371 0 0         return $ok unless $ok;
3372 0           $discDevice->{stage}++; # Move to next stage on next cycle
3373 0 0         if ($$outref =~ /^\s+NVRamSize:/m) {
3374 0           $self->_setAttrib('family_type', $Prm{xlr});
3375 0           $self->_setAttrib('is_nncli', 0);
3376 0           $self->_setAttrib('is_master_cpu', 1);
3377 0           $self->{$Package}{PROMPTTYPE} = $Prm{xlr};
3378 0           $self->debugMsg(4,"Prompt type = $self->{$Package}{PROMPTTYPE}\n\n");
3379 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{xlr}}/;
3380 0           $self->_setDevicePrompts($Prm{xlr}, $1);
3381 0           return (1, $Prm{xlr});
3382             }
3383             }
3384              
3385             # We give up; set as generic device
3386 0           $self->_setAttrib('family_type', $Prm{generic});
3387 0           $self->_setAttrib('is_nncli', 0);
3388 0           $self->_setDevicePrompts($Prm{generic});
3389 0           return (1, $Prm{generic});
3390             }
3391              
3392              
3393             sub debugMsg { # Print a debug message
3394 0     0 1   my $self = shift;
3395 0 0         if (shift() & $self->{debug}) {
3396 0           my $string1 = shift();
3397 0   0       my $stringRef = shift() || \"";#" Ultraedit hack!
3398 0   0       my $string2 = shift() || "";
3399 0 0         if ($self->{$Package}{DEBUGLOGFH}) {
3400 0           print {$self->{$Package}{DEBUGLOGFH}} $string1, $$stringRef, $string2;
  0            
3401             }
3402             else {
3403 0           print $string1, $$stringRef, $string2;
3404             }
3405             }
3406 0           return;
3407             }
3408              
3409              
3410             ########################################## Internal Private Methods ##########################################
3411              
3412             sub _setDevicePrompts { # Steps to set the actual device prompt & more prompt
3413 0     0     my ($self, $keyType, $actualPrompt) = @_;
3414 0           my $setPrompt;
3415              
3416 0           $setPrompt = $Prompt{$keyType};
3417 0 0         if ($actualPrompt) { # Generic prompt will skip this
3418             # If Perl's metacharacters are used in the switch prompt, backslash them not to mess up prompt regex
3419 0           $actualPrompt =~ s/([\{\}\[\]\(\)\^\$\.\|\*\+\?\\])/\\$1/g;
3420 0           $setPrompt =~ s/SWITCHNAME/$actualPrompt/;
3421             }
3422 0           $self->prompt($setPrompt);
3423 0           $self->more_prompt($MorePrompt{$keyType}, $MorePromptDelay{$keyType});
3424 0           return;
3425             }
3426              
3427              
3428             sub _setSlotPortAttrib { # Set the Slot & Port attributes
3429 0     0     my ($self, $outref) = @_;
3430 0           my (@slots, @ports, $currentSlot);
3431             # Get current attribute if partly stored
3432 0 0         @slots = @{$self->{$Package}{ATTRIB}{'slots'}} if $self->{$Package}{ATTRIBFLAG}{'slots'};
  0            
3433 0 0         @ports = @{$self->{$Package}{ATTRIB}{'ports'}} if $self->{$Package}{ATTRIBFLAG}{'ports'};
  0            
3434 0           while ($$outref =~ /^(?:\s*|interface\s+ethernet|gig)?(?:(\d{1,2})\/)?(\d{1,2}(?:\/\d{1,2})?)/mg) {
3435 0 0 0       if (defined $1 && (!defined $currentSlot || $1 != $currentSlot)) { # New slot
      0        
3436 0           $currentSlot = $1;
3437 0 0         push(@slots, $currentSlot) unless grep {$_ eq $currentSlot} @slots;
  0            
3438             }
3439 0 0         if (defined $currentSlot) {
3440 0 0         push(@{$ports[$currentSlot]}, $2) unless grep {$_ eq $2} @{$ports[$currentSlot]};
  0            
  0            
  0            
3441             }
3442             else {
3443 0 0         push(@ports, $2) unless grep {$_ eq $2} @ports;
  0            
3444             }
3445             }
3446 0           @slots = sort {$a <=> $b} @slots; # Slots might need re-arranging on older swithes with fastEther & gigEther ports
  0            
3447 0           $self->_setAttrib('slots', \@slots);
3448 0           $self->_setAttrib('ports', \@ports);
3449 0           return;
3450             }
3451              
3452              
3453             sub _setModelAttrib { # Set & re-format the Model attribute
3454 0     0     my ($self, $model) = @_;
3455              
3456 0           $model =~ s/\s+$//; # Remove trailing spaces
3457 0           $model =~ s/^\s+//; # Remove leading spaces
3458              
3459 0 0         if ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{bstk}) {
    0          
    0          
    0          
    0          
3460             # Try and reformat the model number into something like ERS-5510
3461 0           $model =~ s/Ethernet Routing Switch /ERS-/;
3462 0           $model =~ s/Ethernet Switch /ES-/;
3463 0           $model =~ s/Business Policy Switch /BPS-/;
3464 0           $model =~ s/Wireless LAN Controller WC/WC-/;
3465 0           $model =~ s/Virtual Services Platform /VSP-/;
3466 0           $model =~ s/(-\d{3,})([A-Z])/$1-$2/;
3467             }
3468             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{sr}) {
3469             # Try and reformat the model number into something like SR-4134
3470 0           $model =~ s/SR(\d+)/SR-$1/; # From show chassis
3471 0           $model =~ s/Secure Router /SR-/; # From banner
3472             }
3473             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{trpz}) {
3474             # Try and reformat the model number into something like WSS-2380
3475 0           $model = 'WSS-' . $model;
3476             }
3477             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{xirrus}) {
3478             # Try and reformat the model number into something like WAP-9132
3479 0           $model =~ s/(\D+)(\d+)/$1-$2/; # From show chassis
3480             }
3481             elsif ($self->{$Package}{ATTRIB}{'is_apls'}) {
3482             # Try and reformat from DSG6248CFP to DSG-6248-CFP
3483 0           $model =~ s/^([A-Z]{3})(\d{3,})/$1-$2/;
3484 0           $model =~ s/(-\d{3,})([A-Z])/$1-$2/;
3485             }
3486 0           $self->_setAttrib('model', $model);
3487              
3488             # VOSS is a PassportERS with a VSP model name
3489 0 0         if ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{pers}) {
3490 0 0 0       if ($self->{$Package}{ATTRIB}{'is_apls'} || $model =~ /^VSP/) { # Requires is_apls to always be set before is_voss
3491 0           $self->_setAttrib('is_voss', 1);
3492             }
3493             else {
3494 0           $self->_setAttrib('is_voss', 0);
3495             }
3496             }
3497 0           return;
3498             }
3499              
3500              
3501             sub _setBoxTypeAttrib { # Set & re-format the APLS BoxType attribute
3502 0     0     my ($self, $boxType) = @_;
3503              
3504 0           $boxType =~ s/\s+$//; # Remove trailing spaces
3505 0           $boxType =~ s/^\s+//; # Remove leading spaces
3506              
3507 0           $boxType =~ s/^([A-Z]{3})(\d{3,})/$1-$2/;
3508 0           $boxType =~ s/(-\d{3,})([A-Z])/$1-$2/;
3509 0           $self->_setAttrib('apls_box_type', $boxType);
3510 0           return;
3511             }
3512              
3513              
3514             sub _setBaseMacAttrib { # Set & re-format the Base_Mac attribute
3515 0     0     my ($self, $mac) = @_;
3516              
3517 0           $mac =~ s/\s+$//; # Remove trailing spaces
3518 0           $mac =~ s/^\s+//; # Remove leading spaces
3519              
3520             # Reformat the MAC from xx:xx:xx:xx:xx:xx to xx-xx-xx-xx-xx-xx
3521 0           $mac =~ s/:/-/g;
3522              
3523             # Reformat the MAC from xxxxxxxxxxxx to xx-xx-xx-xx-xx-xx
3524 0           $mac =~ s/([\da-f]{2})([\da-f]{2})([\da-f]{2})([\da-f]{2})([\da-f]{2})([\da-f]{2})/$1-$2-$3-$4-$5-$6/;
3525              
3526 0           $self->_setAttrib('base_mac', $mac);
3527 0           return;
3528             }
3529              
3530              
3531             sub _setAttrib { # Set attribute
3532 0     0     my ($self, $attrib, $value) = @_;
3533 0 0 0       if ($attrib eq 'is_nncli' || $attrib eq 'is_acli') {
3534 0           $self->{$Package}{ATTRIB}{'is_nncli'} = $value;
3535 0           $self->{$Package}{ATTRIBFLAG}{'is_nncli'} = 1;
3536 0           $self->{$Package}{ATTRIB}{'is_acli'} = $value;
3537 0           $self->{$Package}{ATTRIBFLAG}{'is_acli'} = 1;
3538             }
3539             else {
3540 0           $self->{$Package}{ATTRIB}{$attrib} = $value;
3541 0           $self->{$Package}{ATTRIBFLAG}{$attrib} = 1;
3542             }
3543 0 0         if (defined $value) {
3544 0           $self->debugMsg(4,"Attribute - $attrib => $value\n");
3545             }
3546             else {
3547 0           $self->debugMsg(4,"Attribute - $attrib => undef\n");
3548             }
3549 0 0         if ($attrib eq 'family_type') {
3550 0 0         if (defined $Attribute{$value}) {
3551 0           $self->{$Package}{ATTRIB}{'all'} = [@{$Attribute{Global}}, @{$Attribute{$value}}];
  0            
  0            
3552 0           $self->debugMsg(4,"Attribute - all = Global + $value attributes\n");
3553             }
3554             else {
3555 0           $self->{$Package}{ATTRIB}{'all'} = $Attribute{Global};
3556 0           $self->debugMsg(4,"Attribute - all = Global only\n");
3557             }
3558 0           $self->{$Package}{ATTRIBFLAG}{'all'} = 1;
3559             }
3560 0           return;
3561             }
3562              
3563              
3564             sub _determineOutcome { # Determine if an error message was returned by host
3565 0     0     my ($self, $outref, $lastPromptEchoedCmd) = @_;
3566 0           my $familyType;
3567              
3568 0 0         return unless $familyType = $self->{$Package}{ATTRIB}{'family_type'};
3569 0 0         return if $familyType eq $Prm{generic};
3570 0 0         if ($$outref =~ /$ErrorPatterns{$familyType}/m) {
3571 0           (my $errmsg = $1) =~ s/\x07//g; # Suppress bell chars if any
3572 0           $self->debugMsg(4,"\ncmd() Detected error message from host:\n", \$errmsg, "\n");
3573 0           $self->{$Package}{last_cmd_errmsg} = $lastPromptEchoedCmd . $errmsg;
3574 0           return $self->{$Package}{last_cmd_success} = 0;
3575             }
3576             else {
3577 0           return $self->{$Package}{last_cmd_success} = 1;
3578             }
3579             }
3580              
3581              
3582             sub _restoreDeviceBaudrate { # Check done in disconnect and DESTROY to restore device baudrate before quiting
3583 0     0     my $self = shift;
3584 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3585             # If change_bauderate() was called and serial connection still up...
3586 0 0 0       if (defined $self->baudrate && defined (my $origBaud = $self->{$Package}{ORIGBAUDRATE}) ) {
3587             # ...try and restore original baudrate on device before quiting
3588 0 0         if ($familyType eq $Prm{bstk}) {
    0          
3589 0           $self->errmode('return');
3590 0           $self->put($CTRL_C);
3591 0           $self->print("terminal speed $origBaud");
3592             }
3593             elsif ($familyType eq $Prm{pers}) {
3594 0           $self->errmode('return');
3595 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3596 0           $self->printlist('enable', 'config term', "boot config sio console baud $origBaud");
3597             }
3598             else {
3599 0           $self->print("config bootconfig sio console baud $origBaud");
3600             }
3601             }
3602             }
3603 0           return 1;
3604             }
3605              
3606              
3607             sub _printDot {
3608 0     0     local $| = 1; # Flush STDOUT buffer
3609 0           print '.';
3610 0           return;
3611             }
3612              
3613              
3614             1;
3615             __END__;