File Coverage

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


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