File Coverage

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


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