File Coverage

blib/lib/Control/CLI/Extreme.pm
Criterion Covered Total %
statement 31 2351 1.3
branch 12 2228 0.5
condition 1 728 0.1
subroutine 6 67 8.9
pod 49 49 100.0
total 99 5423 1.8


line stmt bran cond sub pod time code
1             package Control::CLI::Extreme;
2              
3 1     1   78413 use strict;
  1         2  
  1         30  
4 1     1   5 use warnings;
  1         2  
  1         26  
5 1     1   5 use Exporter qw( import );
  1         2  
  1         34  
6 1     1   6 use Carp;
  1         1  
  1         51  
7 1     1   1271 use Control::CLI qw( :all );
  1         80999  
  1         41474  
8              
9             my $Package = __PACKAGE__;
10             our $VERSION = '1.09';
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 $Space = " ";
25             my $CTRL_C = "\cC";
26             my $CTRL_U = "\cU";
27             my $CTRL_X = "\cX";
28             my $CTRL_Y = "\cY";
29             my $CTRL_Z = "\cZ";
30              
31             my %LoginPatterns = ( # Patterns to check for during device login (Telnet/Serial) and initial connection to CLI
32             bell => "\x07",
33             banner => 'Enter Ctrl-Y to begin',
34             menu => 'Use arrow keys to highlight option, press or to select option',
35             submenu => 'Press Ctrl-R to return to previous menu. Press Ctrl-C to return to Main Menu',
36             username => 'Enter Username: ',
37             password => "Enter Password: \e[?", # Should match only on the initial password prompt and not subsequent ones where * are printed
38             lastlogin => 'Failed retries since last login:',
39             localfail => 'Incorrect',
40             localfail_xos => 'Login incorrect',
41             radiusfail => 'Access Denied from RADIUS',
42             radiustimeout1 => 'no response from RADIUS servers',
43             radiustimeout2 => 'No reply from RADIUS server',
44             srbanner => "\((?:Secure Router|VSP4K)",
45             xlrbanner => "\x0d************************************\n",
46             ersbanner => "\x0d* Ethernet Routing Switch",
47             passportbanner => "\x0d* Passport 8",
48             pp1600banner => "\x0d* Passport 16", # The 6 ensures this does not trigger on old Accelar 1x00
49             vspbanner => "All Rights Reserved.\n\x0dVirtual Services Platform",
50             fabengbanner => "Extreme Networks Fabric Engine",
51             consoleLogMsg1 => "connected via console port", #On serial port: GlobalRouter SW INFO user rwa connected via console port
52             consoleLogMsg2 => "Blocked unauthorized ACLI access",
53             more1 => '----More (q=Quit, space/return=Continue)----',
54             more2 => '--More--',
55             wlan9100banner => 'Avaya Wi-Fi Access Point',
56             xos => 'ExtremeXOS',
57             switchEngine => 'Extreme Networks Switch Engine',
58             isw => 'Product: ISW',
59             slx => 'Welcome to the Extreme SLX-OS Software'
60             );
61             my %Prm = ( # Hash containing list of named parameters returned by attributes
62             bstk => 'BaystackERS',
63             pers => 'PassportERS',
64             xlr => 'Accelar',
65             sr => 'SecureRouter',
66             trpz => 'WLAN2300',
67             xirrus => 'WLAN9100',
68             xos => 'ExtremeXOS',
69             isw => 'ISW',
70             s200 => 'Series200',
71             wing => 'Wing',
72             slx => 'SLX',
73             hive => 'HiveOS',
74             ipanema => 'Ipanema',
75             generic => 'generic',
76             );
77              
78             my %Attribute = (
79             Global => [
80             'family_type',
81             'is_nncli',
82             'is_acli',
83             'model',
84             'sw_version',
85             'fw_version',
86             'slots',
87             'ports',
88             'sysname',
89             'base_mac',
90             'baudrate',
91             'max_baud',
92             ],
93              
94             $Prm{pers} => [
95             'is_voss',
96             'is_fabric_engine',
97             'is_apls',
98             'apls_box_type',
99             'brand_name',
100             'is_master_cpu',
101             'is_dual_cpu',
102             'cpu_slot',
103             'is_ha',
104             'stp_mode',
105             'oob_ip',
106             'oob_virt_ip',
107             'oob_standby_ip',
108             'is_oob_connected',
109             ],
110              
111             $Prm{bstk} => [
112             'unit_number',
113             'base_unit',
114             'switch_mode',
115             'stack_size',
116             'stp_mode',
117             'mgmt_vlan',
118             'mgmt_ip',
119             'oob_ip',
120             'is_oob_connected',
121             ],
122              
123             $Prm{xos} => [
124             'is_xos',
125             'is_switch_engine',
126             'unit_number',
127             'master_unit',
128             'switch_mode',
129             'stack_size',
130             'stp_mode',
131             'oob_ip',
132             'is_oob_connected',
133             ],
134              
135             $Prm{isw} => [
136             'is_isw',
137             ],
138              
139             $Prm{wing} => [
140             'is_wing',
141             ],
142              
143             $Prm{s200} => [
144             'unit_number',
145             'manager_unit',
146             'switch_mode',
147             'stack_size',
148             'stp_mode',
149             'oob_ip',
150             'is_oob_connected',
151             ],
152              
153             $Prm{slx} => [
154             'is_slx',
155             'is_slx_r',
156             'is_slx_s',
157             'is_slx_x',
158             'switch_type',
159             'is_active_mm',
160             'is_dual_mm',
161             'mm_number',
162             'is_ha',
163             'stp_mode',
164             'oob_ip',
165             'oob_virt_ip',
166             'oob_standby_ip',
167             'is_oob_connected',
168             ],
169              
170             $Prm{hive} => [
171             'is_hiveos',
172             ],
173              
174             $Prm{ipanema} => [
175             'is_sdwan',
176             ],
177              
178             $Prm{xlr} => [
179             'is_master_cpu',
180             'is_dual_cpu',
181             ],
182             );
183              
184             my @InitPromptOrder = ("$Prm{pers}_cli", "$Prm{pers}_nncli", $Prm{xos}, $Prm{ipanema}, 'generic');
185             my $GenericPromptRegex = '[\?\$%#>](?:\e\[00?m)?\s?$';
186             my %InitPrompt = ( # Initial prompt pattern expected at login
187             # Capturing brackets: $1 = switchName, $2 = login_cpu_slot, $3 = configContext;
188             $Prm{bstk} => '\x0d?([^\n\x0d\x0a]{1,50}?)()(?:\((.+?)\))?(?:<.+>)?[>#]$',
189             "$Prm{pers}_cli" => '\x0d?([^\n\x0d\x0a]+):([1356])((?:\/[\w\d\.-]+)*)[>#] $',
190             "$Prm{pers}_nncli" => '\x0d?([^\n\x0d\x0a]+):([12356])(?:\((.+?)\))?[>#]$',
191             $Prm{xlr} => '\x0d?([^\n\x0d\x0a]+?)()((?:\/[\w\d-]+)*)[>#] $',
192             $Prm{sr} => '\x0d? *\x0d([^\n\x0d\x0a]+?)()((?:\/[\w\d\s-]+(?: \(\d+\/\d+\))?)*)# $',
193             $Prm{trpz} => '([^\n\x0d\x0a]+)[>#] $',
194             $Prm{xirrus} => '(?:\x10\x00)?([^\n\x0d\x0a]+?)()(?:\((.+?)\))?# $',
195             $Prm{xos} => '(?:! )?(?:\* )?(?:\([^\n\x0d\x0a\)]+\) )?([^\n\x0d\x0a]+)\.\d+ [>#] $',
196             $Prm{isw} => '([^\n\x0d\x0a]{1,50}?)()(?:\((.+?)\))?[>#] $',
197             $Prm{s200} => '\(([^\n\x0d\x0a\)]+)\) ()(?:\((.+?)\))?[>#]$',
198             $Prm{wing} => '([^\n\x0d\x0a\)]+?)()(?:\((.+?)\))?\*?[>#]$',
199             $Prm{slx} => '([^\n\x0d\x0a\)]+)()(?:\((.+?)\))?# $',
200             $Prm{hive} => '([^\n\x0d\x0a\)]+)()#$',
201             $Prm{ipanema} => '\[([^\n\x0d\x0a\\[\])]+?)(?:\.rt\d)?\]()\$ $',
202             $Prm{generic} => '[^\n\x0d\x0a]*' . $GenericPromptRegex,
203             );
204              
205             my %Prompt = ( # Prompt pattern templates; SWITCHNAME gets replaced with actual switch prompt during login
206             $Prm{bstk} => 'SWITCHNAME(?:\((.+?)\))?(?:<.+>)?[>#]$',
207             "$Prm{pers}_cli" => 'SWITCHNAME:[1356]((?:\/[\w\d\.-]+)*)[>#] $',
208             "$Prm{pers}_nncli" => 'SWITCHNAME:[12356](?:\((.+?)\))?[>#]$',
209             $Prm{xlr} => 'SWITCHNAME((?:\/[\w\d-]+)*)[>#] $',
210             $Prm{sr} => '\x0d? *\x0dSWITCHNAME((?:\/[\w\d\s-]+(?: \(\d+\/\d+\))?)*)# $',
211             $Prm{trpz} => 'SWITCHNAME[>#] $',
212             $Prm{xirrus} => '(?:\x10\x00)?SWITCHNAME(?:\((.+?)\))?# $',
213             $Prm{xos} => '(?:! )?(?:\* )?(?:\([^\n\x0d\x0a\)]+\) )?SWITCHNAME\.\d+ [>#] $',
214             $Prm{isw} => 'SWITCHNAME(?:\((.+?)\))?[>#] $',
215             $Prm{s200} => '\(SWITCHNAME\) (?:\((.+?)\))?[>#]$',
216             $Prm{wing} => 'SWITCHNAME(?:\((.+?)\))?\*?[>#]$',
217             $Prm{slx} => 'SWITCHNAME(?:\((.+?)\))?# $',
218             $Prm{hive} => 'SWITCHNAME#$',
219             $Prm{ipanema} => '(?:\[SWITCHNAME(?:\.rt\d)?\]\$|bash-[\d\.]+\$|SWITCHNAME\.?(?:rt\d)?:[~\/\w]+#) $',
220             $Prm{generic} => '[^\n\x0d\x0a]*' . $GenericPromptRegex,
221             );
222              
223             my @PromptConfigContext = ( # Used to extract config_context in _setLastPromptAndConfigContext(); these patterns need to condense the above
224             '\((.+?)\)\*?[>#]\s?$', # NNCLI CLIs
225             '((?:\/[\w\d\.-]+)*)[>#]\s?$', # PassportERS in PPCLI mode & Accelar
226             );
227              
228             my $LastPromptClense = '^(?:\x0d? *\x0d|\x10\x00)'; # When capturing lastprompt, SecureRouter and Xirrus sometimes precede the prompt with these characters
229              
230             my %MorePrompt = ( # Regular expression character like ()[]. need to be backslashed
231             $Prm{bstk} => '----More \(q=Quit, space/return=Continue\)----',
232             "$Prm{pers}_cli" => '\n\x0d?--More-- \(q = quit\) ',
233             "$Prm{pers}_nncli" => '\n\x0d?--More-- ?\(q = quit\) ?|--More--', # More prompt on command syntax does not have the spaces..
234             $Prm{xlr} => '--More-- \(q = quit\) ',
235             $Prm{sr} => 'Press any key to continue \(q : quit\) :\x00|Press any key to continue \(q : quit \| enter : next line\) :\x00',
236             $Prm{trpz} => 'press any key to continue, q to quit\.',
237             $Prm{xirrus} => '--MORE--\x10?',
238             $Prm{xos} => '\e\[7mPress to continue or to quit:(?:\e\[m)?',
239             $Prm{isw} => '-- more --, next page: Space, continue: g, quit: \^C',
240             $Prm{s200} => '--More-- or \(q\)uit',
241             $Prm{wing} => '--More-- ',
242             $Prm{slx} => '(?:\e\[7m)?(?:--More--|\(END\))(?:\e\[27m)?',
243             $Prm{hive} => ' --More-- ',
244             $Prm{ipanema} => '', # N/A on Linux..
245             $Prm{generic} => '----More \(q=Quit, space/return=Continue\)----'
246             . '|--More-- \(q = quit\) '
247             . '|Press any key to continue \(q : quit\) :\x00'
248             . '|press any key to continue, q to quit\.'
249             . '|--MORE--'
250             . '|\e\[7mPress to continue or to quit:(?:\e\[m)?'
251             . '|-- more --, next page: Space, continue: g, quit: \^C'
252             . '|--More-- or \(q\)uit'
253             . '|(?:\e\[7m)?(?:--More--|\(END\))(?:\e\[27m)?'
254             );
255             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
256             "$Prm{pers}_nncli" => '--More--',
257             $Prm{generic} => '--More--',
258             );
259              
260             our %MoreSkipWithin = ( # Only on family type switches where possible, set to the character used to skip subsequent more prompts
261             $Prm{isw} => 'g',
262             $Prm{wing} => 'r',
263             );
264              
265             my %ExitPrivExec = ( # Override (instead of usual 'disable') to use to exit PrivExec mode and return to UserExec
266             $Prm{s200} => $CTRL_Z,
267             );
268              
269             my %RefreshCommands = ( # Some commands on some devices endlessly refresh the output requested; not desireable when scripting CLI; these paterns determine how to exit such commands
270             "$Prm{pers}_cli" => {
271             pattern => '^Monitor Interval: \d+sec \| Monitor Duration: \d+sec',
272             send => "\n",
273             },
274             "$Prm{pers}_nncli" => {
275             pattern => '^Monitor Interval: \d+sec \| Monitor Duration: \d+sec',
276             send => "\n",
277             },
278             $Prm{xos} => {
279             pattern => '^ U->page up D->page down ESC->exit',
280             send => "\e",
281             },
282             );
283              
284             our %ErrorPatterns = ( # Patterns which indicated the last command sent generated a syntax error on the host device (if regex does not match full error message, it must end with .+)
285             $Prm{bstk} => '^('
286             . '\s+\^\n.+'
287             . '|% Invalid ' # "% Invalid input detected at '^' marker." OR "% Invalid QoS UBP [classifier] entry name"
288             . '|% Cannot modify settings'
289             . '|% Bad (?:port|unit|format)' # ife 33/1; ife 1/333; ife 1/1-2/2
290             . '|% MLT \d+ does not exist or it is not enabled'
291             . '|% No such VLAN'
292             . '|% Bad VLAN list format\.'
293             . '|% View name does not exist' # snmp-server user admin read-view root write-view root notify-view root
294             . '|% Partial configuration of \'.+?\' already exists\.'# same as above
295             . '|% View already exists, you must first delete it\.' # snmp-server view root 1
296             . '|% User \w+ does not exist' # no snmp-server user admindes
297             . '|% User \'.+?\' already exists' # snmp-server user admin md5 passwdvbn read-view root write-view root notify-view root
298             . '|% Password length must be in range:' # username add rwa role-name RW password // rwa // rwa (with password security)
299             . '|% Bad format, use forms:.+' # vlan members add 71 1/6-1/7 (1/6-7 is correct)
300             . ')',
301             $Prm{pers} => '^('
302             . '\x07?\s+\^\n.+'
303             . '|% Invalid input detected at \'\^\' marker\.'
304             . '|.+? not found in path .+'
305             . '|(?:parameter|object) .+? is out of range'
306             . '| ?\x07?Error ?: .+'
307             . '|Unable to .+'
308             . '|% Not allowed on secondary cpu\.'
309             . '|% Incomplete command\.'
310             . '|% Vrf "[^"]+" does not exist'
311             . '| ERROR: copy failed \(code:0x[\da-fA-F]+\)'
312             . '|Save config to file [^ ]+ failed\.'
313             . '|% Vlan \d+ does not exist'
314             . '|Command not allowed MSTP RSTP mode\.' # On MSTP switch : vlan create 101 type port 1
315             . '|% Permission denied\.' # TACACS commands not allowed
316             . '|% Only (?:gigabit|fast) ethernet ports allowed' # config interface gig on fastEth port or vice-versa
317             . '|There are \d+ releases already on system. Please remove 1 to proceed'
318             . '|can\'t \w+ ".+?" 0x\d+' # delete /flash/.ssh -y : can't remove "/flash/.ssh" 0x300042
319             . '|".+?" is ambiguous in path /.+' # AccDist3:5#% do
320             . '|Password change aborted\.' # Creating snmpv3 usm user with enh-secure-mode and password does not meet complexity requirements
321             . '|Invalid password. Authentication failed' # username teamnoc level ro // invalid-old-pwd // ...
322             . '|Passwords do not match. Password change aborted' # username teamnoc level ro // valid-old-pwd // newpwd // diffpwd
323             . '|Error: Prefix List ".+?" not found' # no ip prefix-list ""
324             . '|Invalid ipv4 address\.' # filter acl ace action 11 6 permit redirect-next-hop 2000:100::201 (on a non-ipv6 acl)
325             . '|\x07?error in getting .+' # mlt 1 member 1/1 (where mlt does not exist)
326             . '|\x07?Error In \w+ ?:' # ip name-server primary (where is already set)
327             . ')',
328             $Prm{xlr} => '^('
329             . '.+? not found'
330             . '|(?:parameter|object) .+? is out of range'
331             . ')',
332             $Prm{sr} => '^('
333             . '\s+\^\n.+'
334             . '|Error : Command .+? does not exist'
335             . '|Config is locked by some other user'
336             . ')',
337             $Prm{trpz} => '^('
338             . '\s+\^\n.+'
339             . '|Unrecognized command:.+'
340             . '|Unrecognized command in this mode:.+'
341             . ')',
342             $Prm{xirrus} => '^('
343             . '\s+\^\n.+'
344             . ')',
345             $Prm{xos} => '^('
346             . '\x07?\s+\^\n.+'
347             . '|Error: .*(?:already|not).+'
348             . ')',
349             $Prm{isw} => '^('
350             . '\s+\^\n.+'
351             . '|% Incomplete command.'
352             . '|% Invalid .+' # ISW3(config)#% interface vlan 101; ISW3(config-if-vlan)#% ip igmp snooping : % Invalid IGMP VLAN 101!
353             . ')',
354             $Prm{s200} => '^('
355             . '\s+\^\n.+'
356             . '|Command not found / Incomplete command.'
357             . '|Failed to create.+' # (Extreme 220) (Vlan)#vlan 6666
358             . ')',
359             $Prm{wing} => '^('
360             . '\s+\^\n.+'
361             . '|%% Error:.+'
362             . ')',
363             $Prm{slx} => '^('
364             . '-+\^\n.+'
365             . '|(?:%% )?Error ?: .+'
366             . '|Error\(s\):\n.+'
367             . ')',
368             $Prm{hive} => '^('
369             . '\s+\^--'
370             . ')',
371             $Prm{ipanema} => '('
372             . '^\? Unknown command'
373             . '|: No such file or directory'
374             . '|: incorrect password'
375             . ')',
376             );
377             our $CmdConfirmPrompt = '[\(\[] *(?:[yY](?:es)? *(?:[\\\/]|or) *[nN]o?|[nN]o? *(?:[\\\/]|or) *[yY](?:es)?|y - .+?, n - .+?, - .+?) *[\)\]](?: *[?:] *| )$'; # Y/N prompt
378             our $CmdInitiatedPrompt = '[?:=]\h*(?:\(.+?\)\h*)?$'; # Prompt for additional user info
379             our $WakeConsole = "\n"; # Sequence to send when connecting to console to wake device
380              
381             my $LoginReadAttempts = 10; # Number of read attempts for readwait() method used in login()
382             my $CmdPromptReadAttempts = 8; # Number of read attempts for readwait() method used in poll_cmd()
383             my $ReadwaitTimer = 100; # Timer to use when calling readwait()
384             my $CmdTimeoutRatio = 0.1; # In cmd() if read times out, a 2nd read is attempted with timeout * this ratio
385             my $NonRecognizedLogin = 0; # In login() determines whether a non-recognized login output sequence makes method return using error mode action
386             my $GenericLogin = 0; # In login(), if set, disables extended discovery
387              
388             my %Default = ( # Hash of default object settings which can be modified on a per object basis
389             morePaging => 0, # For --more-- prompt, number of pages accepted before sending q to quit
390             # 0 = accept all pages; 1 = send q after 1st page, i.e. only 1 page; etc
391             progressDots => 0, # After how many bytes received, an activity dot is printed; 0 = disabled
392             return_result => 0, # Whether cmd methods return true/false result or output of command
393             cmd_confirm_prompt => $CmdConfirmPrompt,
394             cmd_initiated_prompt => $CmdInitiatedPrompt,
395             cmd_feed_timeout => 10, # Command requests for data, we have none, after X times we give up
396             wake_console => $WakeConsole,
397             ors => "\r", # Override of Control::CLI's Output Record Separator used by print() & cmd()
398             );
399              
400             our @ConstructorArgs = ( @Control::CLI::ConstructorArgs, 'return_result', 'more_paging', 'debug_file',
401             'cmd_confirm_prompt', 'cmd_initiated_prompt', 'cmd_feed_timeout', 'console', 'wake_console',
402             );
403              
404             # Debug levels can be set using the debug() method or via debug argument to new() constructor
405             # Debug levels defined:
406             # 0 : No debugging
407             # bit 1 : Control::CLI - Debugging activated for polling methods + readwait() + Win32/Device::SerialPort constructor $quiet flag reset
408             # bit 2 : Control::CLI - Debugging is activated on underlying Net::SSH2 and Win32::SerialPort / Device::SerialPort
409             # bit 4 : Control::CLI::Extreme - Basic debugging
410             # bit 8 : Control::CLI::Extreme - Extended debugging of login() & cmd() methods
411              
412              
413             ############################################# Constructors/Destructors #######################################
414              
415             sub new {
416 1     1 1 1589 my $pkgsub = "${Package}::new";
417 1         3 my $invocant = shift;
418 1   33     9 my $class = ref($invocant) || $invocant;
419 1         3 my (%args, %cliArgs);
420 1         3 my $debugLevel = $Default{debug};
421 1 50       4 if (@_ == 1) { # Method invoked with just the connection type argument
422 0         0 $cliArgs{use} = shift;
423             }
424             else {
425 1         7 %args = parseMethodArgs($pkgsub, \@_, \@ConstructorArgs);
426 1         104 my @suppressArgs = ('prompt', 'return_result', 'more_paging', 'cmd_confirm_prompt',
427             'cmd_initiated_prompt', 'cmd_feed_timeout', 'console', 'wake_console', 'debug_file');
428 1         4 %cliArgs = suppressMethodArgs(\@_, \@suppressArgs);
429             }
430 1 50       57 my $self = $class->SUPER::new(%cliArgs) or return;
431             $self->{$Package} = {
432             # Lower Case ones can be set by user; Upper case ones are set internaly in the class
433             morePaging => $Default{morePaging},
434             progressDots => $Default{progressDots},
435             prompt => undef,
436             prompt_qr => undef,
437             morePrompt => undef,
438             morePrompt_qr => undef,
439             morePromptDelay_qr => undef,
440             last_cmd_success => undef,
441             last_cmd_errmsg => undef,
442             return_result => $Default{return_result},
443             cmd_confirm_prompt => $Default{cmd_confirm_prompt},
444             cmd_confirm_prompt_qr => qr/$Default{cmd_confirm_prompt}/,
445             cmd_initiated_prompt => $Default{cmd_initiated_prompt},
446             cmd_initiated_prompt_qr => qr/$Default{cmd_initiated_prompt}/,
447             cmd_feed_timeout => $Default{cmd_feed_timeout},
448             console => undef,
449             wake_console => $Default{wake_console},
450 1         804 noRefreshCmdPattern => undef,
451             noRefreshCmdSend => undef,
452             PROMPTTYPE => undef,
453             ENABLEPWD => undef,
454             ORIGBAUDRATE => undef,
455             ATTRIB => undef,
456             ATTRIBFLAG => undef,
457             CONFIGCONTEXT => '',
458             DEBUGLOGFH => undef,
459             };
460 1 50       6 unless (defined $args{output_record_separator}) { # If not already set in constructor...
461 1         10 $self->output_record_separator($Default{ors}); # ...we override Control::CLI's default with our own default
462             }
463 1         17 foreach my $arg (keys %args) { # Accepted arguments on constructor
464 2 50       20 if ($arg eq 'prompt') { $self->prompt($args{$arg}) }
  0 50       0  
    50          
    50          
    50          
    50          
    50          
    50          
    50          
465 0         0 elsif ($arg eq 'return_result') { $self->return_result($args{$arg}) }
466 0         0 elsif ($arg eq 'more_paging') { $self->more_paging($args{$arg}) }
467 0         0 elsif ($arg eq 'cmd_confirm_prompt') { $self->cmd_confirm_prompt($args{$arg}) }
468 0         0 elsif ($arg eq 'cmd_initiated_prompt') { $self->cmd_initiated_prompt($args{$arg}) }
469 0         0 elsif ($arg eq 'cmd_feed_timeout') { $self->cmd_feed_timeout($args{$arg}) }
470 0         0 elsif ($arg eq 'console') { $self->console($args{$arg}) }
471 0         0 elsif ($arg eq 'wake_console') { $self->wake_console($args{$arg}) }
472 0         0 elsif ($arg eq 'debug_file') { $self->debug_file($args{$arg}) }
473             }
474 1         5 return $self;
475             }
476              
477              
478             # sub DESTROY {} # We don't need to override Control::CLI's destroy method
479              
480              
481             ############################################### Object methods ###############################################
482              
483             sub connect { # All the steps necessary to connect to a CLI session on an Extreme Networking device
484 0     0 1   my $pkgsub = "${Package}::connect";
485 0           my $self = shift;
486 0           my %args;
487 0 0         if (@_ == 1) { # Method invoked in the shorthand form
488 0           $args{host} = shift;
489 0 0         if ($args{host} =~ /^(.+?)\s+(\d+)$/) {
490 0           ($args{host}, $args{port}) = ($1, $2);
491             }
492             }
493             else {
494 0           my @validArgs = ('host', 'port', 'username', 'password', 'publickey', 'privatekey', 'passphrase',
495             'prompt_credentials', 'baudrate', 'parity', 'databits', 'stopbits', 'handshake',
496             'errmode', 'connection_timeout', 'timeout', 'read_attempts', 'wake_console',
497             'return_reference', 'blocking', 'data_with_error', 'terminal_type', 'window_size',
498             'callback', 'forcebaud', 'atomic_connect', 'non_recognized_login', 'generic_login');
499 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
500             }
501              
502             # Initialize the base POLL structure
503             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
504             $pkgsub,
505             __PACKAGE__->can('connect_poll'),
506             defined $args{blocking} ? $args{blocking} : $self->{blocking},
507             defined $args{connection_timeout} ? $args{connection_timeout} : $self->{connection_timeout},
508             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
509             1,
510             wantarray,
511             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
512             undef, # n/a
513 0 0         );
    0          
    0          
    0          
514             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
515             # Set method argument keys
516             host => $args{host},
517             port => $args{port},
518             username => $args{username},
519             password => $args{password},
520             publickey => $args{publickey},
521             privatekey => $args{privatekey},
522             passphrase => $args{passphrase},
523             baudrate => $args{baudrate},
524             parity => $args{parity},
525             databits => $args{databits},
526             stopbits => $args{stopbits},
527             handshake => $args{handshake},
528             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
529             terminal_type => $args{terminal_type},
530             window_size => $args{window_size},
531             callback => $args{callback},
532             forcebaud => $args{forcebaud},
533             atomic_connect => $args{atomic_connect},
534             login_timeout => defined $args{timeout} ? $args{timeout} : $self->{timeout},
535             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
536             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
537             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
538             non_recognized_login => defined $args{non_recognized_login} ? $args{non_recognized_login} : $NonRecognizedLogin,
539             generic_login => defined $args{generic_login} ? $args{generic_login} : $GenericLogin,
540             # Declare method storage keys which will be used
541 0 0         stage => $self->{LOGINSTAGE} ? 1 : 0,
    0          
    0          
    0          
    0          
    0          
    0          
    0          
542             };
543 0 0 0       if (!$self->{LOGINSTAGE} && $self->{TYPE} ne 'SERIAL' && useIPv6 && defined $args{blocking} && !$args{blocking}) {
      0        
      0        
      0        
544 0           carp "$pkgsub: IO::Socket::IP is required for non-blocking connect";
545             }
546 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
547 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
548 0           return __PACKAGE__->can('poll_connect')->($self, $pkgsub); # Do not call a sub-classed version
549             }
550            
551              
552             sub connect_poll { # Poll status of connection (non-blocking mode)
553 0     0 1   my $pkgsub = "${Package}::connect_poll";
554 0           my $self = shift;
555 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
556              
557 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('connect_poll')) {
558 0           return $self->error("$pkgsub: Method connect() needs to be called first with blocking false");
559             }
560 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
561 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
562 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
563              
564             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
565 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
566              
567             # We get here only if we are not complete: $self->{POLL}{complete} == 0
568 0           return __PACKAGE__->can('poll_connect')->($self, $pkgsub); # Do not call a sub-classed version
569             }
570              
571              
572             sub disconnect { # Perform check on restoring buadrate on device before doing Control::CLI's disconnect
573 0     0 1   my $self = shift;
574 0 0         $self->_restoreDeviceBaudrate if $self->connection_type eq 'SERIAL';
575 0           return $self->SUPER::disconnect(@_);
576             }
577              
578              
579             sub login { # Handles steps necessary to get to CLI session, including menu, banner and Telnet/Serial login
580 0     0 1   my $pkgsub = "${Package}::login";
581 0           my $self =shift;
582 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'return_reference',
583             'read_attempts', 'wake_console', 'blocking', 'data_with_error', 'non_recognized_login', 'generic_login');
584 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
585              
586             # Initialize the base POLL structure
587             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
588             $pkgsub,
589             __PACKAGE__->can('login_poll'),
590             defined $args{blocking} ? $args{blocking} : $self->{blocking},
591             defined $args{timeout} ? $args{timeout} : $self->{timeout},
592             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
593             1,
594             wantarray,
595             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
596             undef, # n/a
597 0 0         );
    0          
    0          
    0          
598             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
599             # Set method argument keys
600             username => $args{username},
601             password => $args{password},
602             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
603             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
604             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
605             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
606             non_recognized_login => defined $args{non_recognized_login} ? $args{non_recognized_login} : $NonRecognizedLogin,
607 0 0         generic_login => defined $args{generic_login} ? $args{generic_login} : $GenericLogin,
    0          
    0          
    0          
    0          
    0          
608             # Declare method storage keys which will be used
609             stage => 0,
610             login_attempted => undef,
611             password_sent => undef,
612             login_error => '',
613             family_type => undef,
614             cpu_slot => undef,
615             detectionFromPrompt => undef,
616             };
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 0           return __PACKAGE__->can('poll_login')->($self, $pkgsub); # Do not call a sub-classed version
620             }
621              
622              
623             sub login_poll { # Poll status of login (non-blocking mode)
624 0     0 1   my $pkgsub = "${Package}::login_poll";
625 0           my $self = shift;
626 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
627              
628 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('login_poll')) {
629 0           return $self->error("$pkgsub: Method login() needs to be called first with blocking false");
630             }
631 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
632 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
633 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
634              
635             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
636 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
637              
638             # We get here only if we are not complete: $self->{POLL}{complete} == 0
639 0           return __PACKAGE__->can('poll_login')->($self, $pkgsub); # Do not call a sub-classed version
640             }
641              
642              
643             sub cmd { # Sends a CLI command to host and returns result or output data
644 0     0 1   my $pkgsub = "${Package}::cmd";
645 0           my $self = shift;
646 0           my %args;
647 0 0         if (@_ == 1) { # Method invoked with just the command argument
648 0           $args{command} = shift;
649             }
650             else {
651 0           my @validArgs = ('command', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_confirm_prompt', 'more_pages',
652             'timeout', 'errmode', 'return_reference', 'return_result', 'progress_dots', 'blocking', 'poll_syntax');
653 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
654             }
655 0 0         $args{command} = '' unless defined $args{command};
656              
657             # Initialize the base POLL structure
658             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
659             $pkgsub,
660             __PACKAGE__->can('cmd_poll'),
661             defined $args{blocking} ? $args{blocking} : $self->{blocking},
662             defined $args{timeout} ? $args{timeout} : $self->{timeout},
663             defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
664             (defined $args{return_result} ? $args{return_result} : $self->{$Package}{return_result}) ? 2 : 1,
665             undef, # This is set below
666             defined $args{return_reference} ? $args{return_reference} : $self->{return_reference},
667             undef, # n/a
668 0 0         );
    0          
    0          
    0          
    0          
    0          
669             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
670             # Set method argument keys
671             command => $args{command},
672             prompt => defined $args{prompt} ? $args{prompt} : $self->{$Package}{prompt_qr},
673             more_prompt => defined $args{more_prompt} ? $args{more_prompt} : $self->{$Package}{morePrompt_qr},
674             more_prompt_delay => defined $args{more_prompt} ? undef : $self->{$Package}{morePromptDelay_qr},
675             more_pages => defined $args{more_pages} ? $args{more_pages} : $self->{$Package}{morePaging},
676             reset_prompt => $args{reset_prompt} && defined $self->{$Package}{PROMPTTYPE},
677             yn_prompt => defined $args{cmd_confirm_prompt} ? $args{cmd_confirm_prompt} : $self->{$Package}{cmd_confirm_prompt_qr},
678             cmd_prompt => undef,
679             feed_data => undef,
680             progress_dots => defined $args{progress_dots} ? $args{progress_dots} : $self->{$Package}{progressDots},
681             # Declare method storage keys which will be used
682             stage => 0,
683             lastLine => '',
684             outputNewline => '',
685             progress => undef,
686             alreadyCmdTimeout => 0,
687             ynPromptCount => undef,
688             cmdPromptCount => undef,
689             cmdEchoRemoved => 0,
690             lastPromptEchoedCmd => undef,
691             cache_timeout => $self->{POLL}{timeout},
692 0 0 0       noRefreshCmdDone => undef,
    0          
    0          
    0          
    0          
    0          
693             };
694 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
695 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
696 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
697              
698 0           my ($ok, $output) = $self->poll_cmd($pkgsub);
699             # We have a different syntax for scalar output in blocking and non-blocking modes
700 0 0         if ($args{poll_syntax}) { # New syntax
701 0 0         return wantarray ? ($ok, $output) : $ok;
702             }
703             else { # Old syntax
704 0 0         return wantarray ? ($ok, $output) : $output;
705             }
706             }
707              
708              
709             sub cmd_prompted { # Sends a CLI command to host, feed additional data and return any output
710 0     0 1   my $pkgsub = "${Package}::cmd_prompted";
711 0           my $pollsub = "${Package}::cmd";
712 0           my $self = shift;
713 0           my ($cmd, @feedData, $errmode, $reset_prompt, $pollSyntax);
714 0           my $morePages = $self->{$Package}{morePaging};
715 0           my $progressDots = $self->{$Package}{progressDots};
716 0           my $timeout = $self->{timeout};
717 0           my $blocking = $self->{blocking};
718 0           my $returnRef = $self->{return_reference};
719 0           my $returnRes = $self->{$Package}{return_result};
720 0           my $prompt = $self->{$Package}{prompt_qr};
721 0           my $morePrompt = $self->{$Package}{morePrompt_qr};
722 0           my $morePromptDelay = $self->{$Package}{morePromptDelay_qr};
723 0           my $cmdPrompt = $self->{$Package}{cmd_initiated_prompt_qr};
724 0 0 0       if (lc($_[0]) ne 'command' && lc($_[0]) ne 'poll_syntax') { # No command or poll_syntax argument, assume list form
725 0           $cmd = shift;
726 0           @feedData = @_;
727             }
728             else { # Method invoked with multiple arguments form
729 0           my @validArgs = ('command', 'feed', 'feed_list', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_initiated_prompt', 'more_pages',
730             'timeout', 'errmode', 'return_reference', 'return_result', 'progress_dots', 'blocking', 'poll_syntax');
731 0           my @args = parseMethodArgs($pkgsub, \@_, \@validArgs);
732 0           for (my $i = 0; $i < $#args; $i += 2) {
733 0 0         $cmd = $args[$i + 1] if $args[$i] eq 'command';
734 0 0         push @feedData, $args[$i + 1] if $args[$i] eq 'feed';
735 0 0 0       push @feedData, @{$args[$i + 1]} if $args[$i] eq 'feed_list' && ref($args[$i + 1]) eq "ARRAY";
  0            
736 0 0         $prompt = $args[$i + 1] if $args[$i] eq 'prompt';
737 0 0         $morePages = $args[$i + 1] if $args[$i] eq 'more_pages';
738 0 0         $timeout = $args[$i + 1] if $args[$i] eq 'timeout';
739 0 0         $blocking = $args[$i + 1] if $args[$i] eq 'blocking';
740 0 0         $returnRef = $args[$i + 1] if $args[$i] eq 'return_reference';
741 0 0         $returnRes = $args[$i + 1] if $args[$i] eq 'return_result';
742 0 0         $reset_prompt = $args[$i + 1] if $args[$i] eq 'reset_prompt';
743 0 0         ($morePrompt, $morePromptDelay) = ($args[$i + 1], undef) if $args[$i] eq 'more_prompt';
744 0 0         $progressDots = $args[$i + 1] if $args[$i] eq 'progress_dots';
745 0 0         $cmdPrompt = $args[$i + 1] if $args[$i] eq 'cmd_initiated_prompt';
746 0 0         $errmode = parse_errmode($pkgsub, $args[$i + 1]) if $args[$i] eq 'errmode';
747 0 0         $pollSyntax = $args[$i + 1] if $args[$i] eq 'poll_syntax';
748             }
749             }
750 0 0         $cmd = '' unless defined $cmd;
751              
752             # Initialize the base POLL structure
753 0 0 0       $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
754             $pollsub,
755             __PACKAGE__->can('cmd_poll'),
756             $blocking,
757             $timeout,
758             $errmode,
759             $returnRes ? 2 : 1,
760             $blocking || wantarray, # Always true in blocking mode; if wantarray otherwise
761             $returnRef,
762             undef, # n/a
763             );
764             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
765             # Set method argument keys
766             command => $cmd,
767             prompt => $prompt,
768             more_prompt => $morePrompt,
769             more_prompt_delay => $morePromptDelay,
770             more_pages => $morePages,
771             reset_prompt => $reset_prompt && defined $self->{$Package}{PROMPTTYPE},
772             yn_prompt => undef,
773             cmd_prompt => $cmdPrompt,
774             feed_data => \@feedData,
775             progress_dots => $progressDots,
776             # Declare method storage keys which will be used
777             stage => 0,
778             lastLine => '',
779             outputNewline => '',
780             progress => undef,
781             alreadyCmdTimeout => 0,
782             ynPromptCount => undef,
783             cmdPromptCount => undef,
784             cmdEchoRemoved => 0,
785             lastPromptEchoedCmd => undef,
786             cache_timeout => $self->{POLL}{timeout},
787 0   0       noRefreshCmdDone => undef,
788             };
789 0   0       $self->{POLL}{output_requested} = !$pollSyntax || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
790 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
791 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
792              
793 0           my ($ok, $output) = __PACKAGE__->can('poll_cmd')->($self, $pkgsub); # Do not call a sub-classed version
794             # We have a different syntax for scalar output in blocking and non-blocking modes
795 0 0         if ($pollSyntax) { # New syntax
796 0 0         return wantarray ? ($ok, $output) : $ok;
797             }
798             else { # Old syntax
799 0 0         return wantarray ? ($ok, $output) : $output;
800             }
801             }
802              
803              
804             sub cmd_poll { # Poll status of cmd (non-blocking mode)
805 0     0 1   my $pkgsub = "${Package}::cmd_poll";
806 0           my $self = shift;
807 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
808              
809 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('cmd_poll')) {
810 0           return $self->error("$pkgsub: Method cmd() needs to be called first with blocking false");
811             }
812 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
813 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
814 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
815              
816             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
817 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
818              
819             # We get here only if we are not complete: $self->{POLL}{complete} == 0
820 0           return __PACKAGE__->can('poll_cmd')->($self, $pkgsub); # Do not call a sub-classed version
821             }
822              
823              
824             sub attribute { # Read attributes for host device
825 0     0 1   my $pkgsub = "${Package}::attribute";
826 0           my $self = shift;
827 0           my %args;
828 0 0         if (@_ == 1) { # Method invoked with just the command argument
829 0           $args{attribute} = shift;
830             }
831             else {
832 0           my @validArgs = ('attribute', 'reload', 'blocking', 'timeout', 'errmode', 'poll_syntax');
833 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
834             }
835              
836             # Initialize the base POLL structure
837             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
838             $pkgsub,
839             __PACKAGE__->can('attribute_poll'),
840             defined $args{blocking} ? $args{blocking} : $self->{blocking},
841             defined $args{timeout} ? $args{timeout} : $self->{timeout},
842 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
843             2,
844             undef, # This is set below
845             0,
846             undef, # n/a
847             );
848             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
849             # Set method argument keys
850             attribute => $args{attribute},
851             reload => $args{reload},
852             # Declare method storage keys which will be used
853 0           stage => 0,
854             debugMsg => 0,
855             };
856 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
857 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
858 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
859              
860 0           my ($ok, $attribute) = __PACKAGE__->can('poll_attribute')->($self, $pkgsub); # Do not call a sub-classed version
861             # We have a different syntax for scalar output in blocking and non-blocking modes
862 0 0         if ($args{poll_syntax}) { # New syntax
863 0 0         return wantarray ? ($ok, $attribute) : $ok;
864             }
865             else { # Old syntax
866 0 0         return wantarray ? ($ok, $attribute) : $attribute;
867             }
868             }
869              
870              
871             sub attribute_poll { # Poll status of attribute (non-blocking mode)
872 0     0 1   my $pkgsub = "${Package}::attribute_poll";
873 0           my $self = shift;
874 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
875              
876 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('attribute_poll')) {
877 0           return $self->error("$pkgsub: Method attribute() needs to be called first with blocking false");
878             }
879 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
880 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
881 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
882              
883             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
884 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
885              
886             # We get here only if we are not complete: $self->{POLL}{complete} == 0
887 0           return __PACKAGE__->can('poll_attribute')->($self, $pkgsub); # Do not call a sub-classed version
888             }
889              
890              
891             sub change_baudrate { # Change baud rate on device and on current connection, if serial
892 0     0 1   my $pkgsub = "${Package}::change_baudrate";
893 0           my $self = shift;
894 0           my (%args);
895 0 0         if (@_ == 1) { # Method invoked with just the command argument
896 0           $args{baudrate} = shift;
897             }
898             else {
899 0           my @validArgs = ('baudrate', 'parity', 'databits', 'stopbits', 'handshake', 'forcebaud',
900             'timeout', 'errmode', 'local_side_only', 'blocking', 'poll_syntax');
901 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
902             }
903              
904             # Initialize the base POLL structure
905             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
906             $pkgsub,
907             __PACKAGE__->can('change_baudrate_poll'),
908             defined $args{blocking} ? $args{blocking} : $self->{blocking},
909             defined $args{timeout} ? $args{timeout} : $self->{timeout},
910 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
911             2,
912             undef, # This is set below
913             0,
914             undef, # n/a
915             );
916             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
917             # Set method argument keys
918             baudrate => $args{baudrate},
919             parity => $args{parity},
920             databits => $args{databits},
921             stopbits => $args{stopbits},
922             handshake => $args{handshake},
923             forcebaud => $args{forcebaud},
924             local_side_only => $args{local_side_only},
925             # Declare method storage keys which will be used
926             stage => 0,
927             userExec => undef,
928             privExec => undef,
929 0 0         maxMode => $args{baudrate} eq 'max' ? 1:0,
930             };
931 0   0       $self->{POLL}{output_requested} = !$args{poll_syntax} || wantarray; # Always true in legacy syntax and in poll_syntax if wantarray
932 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
933 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
934              
935 0           my ($ok, $baudrate) = __PACKAGE__->can('poll_change_baudrate')->($self, $pkgsub); # Do not call a sub-classed version
936             # We have a different syntax for scalar output in blocking and non-blocking modes
937 0 0         if ($args{poll_syntax}) { # New syntax
938 0 0         return wantarray ? ($ok, $baudrate) : $ok;
939             }
940             else { # Old syntax
941 0 0         return wantarray ? ($ok, $baudrate) : $baudrate;
942             }
943             }
944              
945              
946             sub change_baudrate_poll { # Poll status of change_baudrate (non-blocking mode)
947 0     0 1   my $pkgsub = "${Package}::change_baudrate_poll";
948 0           my $self = shift;
949 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
950              
951 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('change_baudrate_poll')) {
952 0           return $self->error("$pkgsub: Method change_baudrate() needs to be called first with blocking false");
953             }
954 0           $self->{POLL}{output_requested} = wantarray; # This might change at every call
955 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
956 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
957              
958             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
959 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
960              
961             # We get here only if we are not complete: $self->{POLL}{complete} == 0
962 0           return __PACKAGE__->can('poll_change_baudrate')->($self, $pkgsub); # Do not call a sub-classed version
963             }
964              
965              
966             sub enable { # Enter PrivExec mode (handle enable password for WLAN2300)
967 0     0 1   my $pkgsub = "${Package}::enable";
968 0           my $self = shift;
969 0           my %args;
970 0 0         if (@_ == 1) { # Method invoked with just the command argument
971 0           $args{password} = shift;
972             }
973             else {
974 0           my @validArgs = ('password', 'prompt_credentials', 'timeout', 'errmode', 'blocking');
975 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
976             }
977              
978             # Initialize the base POLL structure
979             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
980             $pkgsub,
981             __PACKAGE__->can('enable_poll'),
982             defined $args{blocking} ? $args{blocking} : $self->{blocking},
983             defined $args{timeout} ? $args{timeout} : $self->{timeout},
984 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
985             0, # no output
986             0, # no output
987             undef, # n/a
988             undef, # n/a
989             );
990             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
991             # Set method argument keys
992             enable_password => defined $args{password} ? $args{password} : $self->{$Package}{ENABLEPWD},
993             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
994             # Declare method storage keys which will be used
995 0 0         stage => 0,
    0          
996             login_attempted => undef,
997             password_sent => undef,
998             login_failed => undef,
999             };
1000 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1001 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1002 0           return __PACKAGE__->can('poll_enable')->($self, $pkgsub); # Do not call a sub-classed version
1003             }
1004              
1005              
1006             sub enable_poll { # Poll status of enable (non-blocking mode)
1007 0     0 1   my $pkgsub = "${Package}::enable_poll";
1008 0           my $self = shift;
1009 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
1010              
1011 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('enable_poll')) {
1012 0           return $self->error("$pkgsub: Method enable() needs to be called first with blocking false");
1013             }
1014 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1015 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1016              
1017             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
1018 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
1019              
1020             # We get here only if we are not complete: $self->{POLL}{complete} == 0
1021 0           return __PACKAGE__->can('poll_enable')->($self, $pkgsub); # Do not call a sub-classed version
1022             }
1023              
1024              
1025             sub device_more_paging { # Enable/Disable more paging on host device
1026 0     0 1   my $pkgsub = "${Package}::device_more_paging";
1027 0           my $self = shift;
1028 0           my (%args, $familyType);
1029 0 0         if (@_ == 1) { # Method invoked with just the command argument
1030 0           $args{enable} = shift;
1031             }
1032             else {
1033 0           my @validArgs = ('enable', 'timeout', 'errmode', 'blocking');
1034 0           %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
1035             }
1036              
1037             # Initialize the base POLL structure
1038             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
1039             $pkgsub,
1040             __PACKAGE__->can('device_more_paging_poll'),
1041             defined $args{blocking} ? $args{blocking} : $self->{blocking},
1042             defined $args{timeout} ? $args{timeout} : $self->{timeout},
1043 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
1044             0, # no output
1045             0, # no output
1046             undef, # n/a
1047             undef, # n/a
1048             );
1049             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
1050             # Set method argument keys
1051             enable => $args{enable},
1052             # Declare method storage keys which will be used
1053 0           stage => 0,
1054             cmdString => undef,
1055             };
1056 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1057 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1058 0           return __PACKAGE__->can('poll_device_more_paging')->($self, $pkgsub); # Do not call a sub-classed version
1059             }
1060              
1061              
1062             sub device_more_paging_poll { # Poll status of device_more_paging (non-blocking mode)
1063 0     0 1   my $pkgsub = "${Package}::device_more_paging_poll";
1064 0           my $self = shift;
1065 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
1066              
1067 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('device_more_paging_poll')) {
1068 0           return $self->error("$pkgsub: Method device_more_paging() needs to be called first with blocking false");
1069             }
1070 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1071 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1072              
1073             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
1074 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
1075              
1076             # We get here only if we are not complete: $self->{POLL}{complete} == 0
1077 0           return __PACKAGE__->can('poll_device_more_paging')->($self, $pkgsub); # Do not call a sub-classed version
1078             }
1079              
1080              
1081             sub device_peer_cpu { # Connect to peer CPU on ERS8x00 / VSP9000
1082 0     0 1   my $pkgsub = "${Package}::device_peer_cpu";
1083 0           my $self = shift;
1084 0           my $familyType;
1085 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'blocking');
1086 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs);
1087              
1088             # Initialize the base POLL structure
1089             $self->poll_struct( # $methodName, $codeRef, $blocking, $timeout, $errmode, $outputType, $outputRequested, $returnReference, $returnList
1090             $pkgsub,
1091             __PACKAGE__->can('device_peer_cpu_poll'),
1092             defined $args{blocking} ? $args{blocking} : $self->{blocking},
1093             defined $args{timeout} ? $args{timeout} : $self->{timeout},
1094 0 0         defined $args{errmode} ? parse_errmode($pkgsub, $args{errmode}) : undef,
    0          
    0          
1095             0, # no output
1096             0, # no output
1097             undef, # n/a
1098             undef, # n/a
1099             );
1100             $self->{POLL}{$pkgsub} = { # Populate structure with method arguments/storage
1101             # Set method argument keys
1102             username => defined $args{username} ? $args{username} : $self->username,
1103             password => defined $args{password} ? $args{password} : $self->password,
1104             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
1105             # Declare method storage keys which will be used
1106 0 0         stage => 0,
    0          
    0          
1107             };
1108 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1109 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1110 0           return __PACKAGE__->can('poll_device_peer_cpu')->($self, $pkgsub); # Do not call a sub-classed version
1111             }
1112              
1113              
1114             sub device_peer_cpu_poll { # Poll status of device_peer_cpu (non-blocking mode)
1115 0     0 1   my $pkgsub = "${Package}::device_peer_cpu_poll";
1116 0           my $self = shift;
1117 0 0         carp "$pkgsub: No arguments expected" if @_; # No arguments expected
1118              
1119 0 0 0       unless (defined $self->{POLL} && $self->{POLL}{coderef} == __PACKAGE__->can('device_peer_cpu_poll')) {
1120 0           return $self->error("$pkgsub: Method device_peer_cpu() needs to be called first with blocking false");
1121             }
1122 0           local $self->{POLLING} = 1; # True until we come out of this polling-capable method
1123 0 0         local $self->{errmode} = $self->{POLL}{errmode} if defined $self->{POLL}{errmode};
1124              
1125             # If already completed (1) or we got an error (undef) from previous call (errmsg is already set) then we go no further
1126 0 0 0       return $self->poll_return($self->{POLL}{complete}) unless defined $self->{POLL}{complete} && $self->{POLL}{complete} == 0;
1127              
1128             # We get here only if we are not complete: $self->{POLL}{complete} == 0
1129 0           return __PACKAGE__->can('poll_device_peer_cpu')->($self, $pkgsub); # Do not call a sub-classed version
1130             }
1131              
1132              
1133             sub debug_file { # Set debug output file
1134 0     0 1   my ($self, $fh) = @_;
1135 0           my $pkgsub = "${Package}::debug_file";
1136              
1137 0 0         unless (defined $fh) { # No input = return current filehandle
1138 0           return $self->{$Package}{DEBUGLOGFH};
1139             }
1140 0 0 0       unless (ref $fh or length $fh) { # Empty input = stop logging
1141 0           $self->{$Package}{DEBUGLOGFH} = undef;
1142 0           return;
1143             }
1144 0 0 0       if (!ref($fh) && !defined(fileno $fh)) { # Open a new filehandle if input is a filename
1145 0           my $logfile = $fh;
1146 0           $fh = IO::Handle->new;
1147 0 0         open($fh, '>', "$logfile") or return $self->error("$pkgsub: Unable to open output log file: $!");
1148             }
1149 0           $fh->autoflush();
1150 0           $self->{$Package}{DEBUGLOGFH} = $fh;
1151 0           return $fh;
1152             }
1153              
1154              
1155             #################################### Methods to set/read Object variables ####################################
1156              
1157             sub flush_credentials { # Clear the stored username, password, passphrases, and enable password, if any
1158 0     0 1   my $self = shift;
1159 0           $self->SUPER::flush_credentials;
1160 0           $self->{$Package}{ENABLEPWD} = undef;
1161 0           return 1;
1162             }
1163              
1164              
1165             sub prompt { # Read/Set object prompt
1166 0     0 1   my ($self, $newSetting) = @_;
1167 0           my $currentSetting = $self->{$Package}{prompt};
1168 0 0         if (defined $newSetting) {
1169 0           $self->debugMsg(4, "\nPrompt Regex set to:\n$newSetting\n");
1170 0           $self->{$Package}{prompt} = $newSetting;
1171 0           $self->{$Package}{prompt_qr} = qr/$newSetting/;
1172             }
1173 0           return $currentSetting;
1174             }
1175              
1176              
1177             sub more_prompt { # Read/Set object more prompt
1178 0     0 1   my ($self, $newSetting, $delayPrompt) = @_;
1179 0           my $currentSetting = $self->{$Package}{morePrompt};
1180 0 0         if (defined $newSetting) {
1181 0           $self->debugMsg(4, "More Prompt Regex set to:\n$newSetting\n");
1182 0           $self->{$Package}{morePrompt} = $newSetting;
1183 0 0         $self->{$Package}{morePrompt_qr} = $newSetting ? qr/$newSetting/ : undef;
1184 0 0         $self->{$Package}{morePromptDelay_qr} = $delayPrompt ? qr/$delayPrompt/ : undef;
1185             }
1186 0           return $currentSetting;
1187             }
1188              
1189              
1190             sub more_paging { # Set the number of pages to read in the resence of --more-- prompts from host
1191 0     0 1   my ($self, $newSetting) = @_;
1192 0           my $currentSetting = $self->{$Package}{morePaging};
1193 0 0         $self->{$Package}{morePaging} = $newSetting if defined $newSetting;
1194 0           return $currentSetting;
1195             }
1196              
1197              
1198             sub progress_dots { # Enable/disable activity dots
1199 0     0 1   my ($self, $newSetting) = @_;
1200 0           my $currentSetting = $self->{$Package}{progressDots};
1201 0 0         $self->{$Package}{progressDots} = $newSetting if defined $newSetting;
1202 0           return $currentSetting;
1203             }
1204              
1205              
1206             sub return_result { # Set/read return_result mode
1207 0     0 1   my ($self, $newSetting) = @_;
1208 0           my $currentSetting = $self->{$Package}{return_result};
1209 0 0         $self->{$Package}{return_result} = $newSetting if defined $newSetting;
1210 0           return $currentSetting;
1211             }
1212              
1213              
1214             sub cmd_confirm_prompt { # Read/Set object cmd_confirm_prompt prompt
1215 0     0 1   my ($self, $newSetting) = @_;
1216 0           my $currentSetting = $self->{$Package}{cmd_confirm_prompt};
1217 0 0         if (defined $newSetting) {
1218 0           $self->{$Package}{cmd_confirm_prompt} = $newSetting;
1219 0           $self->{$Package}{cmd_confirm_prompt_qr} = qr/$newSetting/;
1220             }
1221 0           return $currentSetting;
1222             }
1223              
1224              
1225             sub cmd_initiated_prompt { # Read/Set object cmd_initiated_prompt prompt
1226 0     0 1   my ($self, $newSetting) = @_;
1227 0           my $currentSetting = $self->{$Package}{cmd_initiated_prompt};
1228 0 0         if (defined $newSetting) {
1229 0           $self->{$Package}{cmd_initiated_prompt} = $newSetting;
1230 0           $self->{$Package}{cmd_initiated_prompt_qr} = qr/$newSetting/;
1231             }
1232 0           return $currentSetting;
1233             }
1234              
1235              
1236             sub cmd_feed_timeout { # Read/Set object value of cmd_feed_timeout
1237 0     0 1   my ($self, $newSetting) = @_;
1238 0           my $currentSetting = $self->{$Package}{cmd_feed_timeout};
1239 0 0         $self->{$Package}{cmd_feed_timeout} = $newSetting if defined $newSetting;
1240 0           return $currentSetting;
1241             }
1242              
1243              
1244             sub console { # Read/Set value of console
1245 0     0 1   my ($self, $newSetting) = @_;
1246 0           my $currentSetting = $self->{$Package}{console};
1247 0 0         $self->{$Package}{console} = $newSetting if defined $newSetting;
1248 0           return $currentSetting;
1249             }
1250              
1251              
1252             sub wake_console { # Read/Set object value of wake_console
1253 0     0 1   my ($self, $newSetting) = @_;
1254 0           my $currentSetting = $self->{$Package}{wake_console};
1255 0 0         $self->{$Package}{wake_console} = $newSetting if defined $newSetting;
1256 0           return $currentSetting;
1257             }
1258              
1259              
1260             sub last_cmd_success { # Return the result of the last command sent via cmd methods
1261 0     0 1   my ($self, $newSetting) = @_;
1262 0           my $currentSetting = $self->{$Package}{last_cmd_success};
1263 0 0         $self->{$Package}{last_cmd_success} = $newSetting if defined $newSetting;
1264 0           return $currentSetting;
1265             }
1266              
1267              
1268             sub last_cmd_errmsg { # Set/read the last generated error message from host
1269 0     0 1   my ($self, $newSetting) = @_;
1270 0           my $currentSetting = $self->{$Package}{last_cmd_errmsg};
1271 0 0         $self->{$Package}{last_cmd_errmsg} = $newSetting if defined $newSetting;
1272 0           return $currentSetting;
1273             }
1274              
1275              
1276             sub no_refresh_cmd { # Read/Set object no_refresh_cmd pattern
1277 0     0 1   my ($self, $newSetting, $sendChar) = @_;
1278 0           my $currentSetting = $self->{$Package}{noRefreshCmdPattern};
1279 0 0 0       if (defined $newSetting && (defined $sendChar || !$newSetting)) {
      0        
1280 0           $self->debugMsg(4, "NoRefreshCmdPattern Regex set to:\n$newSetting\n");
1281 0 0         $self->{$Package}{noRefreshCmdPattern} = $newSetting ? $newSetting : undef;
1282 0 0         $self->{$Package}{noRefreshCmdSend} = "\n" eq $sendChar ? $self->{ors} : $sendChar;
1283             }
1284 0           return $currentSetting;
1285             }
1286              
1287              
1288             ################################# Methods to read read-only Object variables #################################
1289              
1290             sub config_context { # Return the configuration context contained in the last prompt
1291 0     0 1   my $self = shift;
1292 0           return $self->{$Package}{CONFIGCONTEXT};
1293             }
1294              
1295              
1296             sub enable_password { # Read the enable password (WLAN2300)
1297 0     0 1   my $self = shift;
1298 0           return $self->{$Package}{ENABLEPWD};
1299             }
1300              
1301              
1302             #################################### Private poll methods for sub classes ####################################
1303              
1304             sub poll_connect { # Internal method to connect to host and perform login (used for both blocking & non-blocking modes)
1305 0     0 1   my $self = shift;
1306 0           my $pkgsub = shift;
1307 0           my $pollsub = "${Package}::connect";
1308              
1309 0 0         unless ($self->{POLLING}) { # Sanity check
1310 0           my (undef, $fileName, $lineNumber) = caller;
1311 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1312             }
1313              
1314 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1315 0           my @validArgs = ('host', 'port', 'username', 'password', 'publickey', 'privatekey', 'passphrase',
1316             'prompt_credentials', 'baudrate', 'parity', 'databits', 'stopbits', 'handshake',
1317             'errmode', 'connection_timeout', 'login_timeout', 'read_attempts', 'wake_console',
1318             'data_with_error', 'terminal_type', 'window_size', 'callback', 'forcebaud',
1319             'atomic_connect', 'non_recognized_login', 'generic_login');
1320 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1321 0 0 0       if (@_ && !%args) { # Legacy syntax
1322             ($args{host}, $args{port}, $args{username}, $args{password}, $args{publickey}, $args{privatekey},
1323             $args{passphrase}, $args{baudrate}, $args{parity}, $args{databits}, $args{stopbits},
1324             $args{handshake}, $args{prompt_credentials}, $args{read_attempts}, $args{wake_console},
1325 0           $args{connection_timeout}, $args{login_timeout}, $args{errmode}) = @_;
1326             }
1327             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1328             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1329             # Set method argument keys
1330             host => $args{host},
1331             port => $args{port},
1332             username => defined $args{username} ? $args{username} : $self->{USERNAME},
1333             password => defined $args{password} ? $args{password} : $self->{PASSWORD},
1334             publickey => $args{publickey},
1335             privatekey => $args{privatekey},
1336             passphrase => defined $args{passphrase} ? $args{passphrase} : $self->{PASSPHRASE},
1337             baudrate => $args{baudrate},
1338             parity => $args{parity},
1339             databits => $args{databits},
1340             stopbits => $args{stopbits},
1341             handshake => $args{handshake},
1342             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
1343             terminal_type => $args{terminal_type},
1344             window_size => $args{window_size},
1345             callback => $args{callback},
1346             forcebaud => $args{forcebaud},
1347             atomic_connect => $args{atomic_connect},
1348             login_timeout => defined $args{login_timeout} ? $args{login_timeout} : $self->{timeout},
1349             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
1350             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
1351             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
1352             non_recognized_login => defined $args{non_recognized_login} ? $args{non_recognized_login} : $NonRecognizedLogin,
1353             generic_login => defined $args{generic_login} ? $args{generic_login} : $GenericLogin,
1354             # Declare method storage keys which will be used
1355             stage => $self->{LOGINSTAGE} ? 1 : 0,
1356             # Declare keys to be set if method called from another polled method
1357             errmode => $args{errmode},
1358 0 0         };
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1359             # Cache poll structure keys which this method will use
1360 0           $self->poll_struct_cache($pollsub, $args{connection_timeout});
1361             }
1362 0           my $connect = $self->{POLL}{$pollsub};
1363 0 0         local $self->{errmode} = $connect->{errmode} if defined $connect->{errmode};
1364              
1365 0 0         if ($connect->{stage} < 1) { # Connect stage
1366             my $ok = $self->SUPER::poll_connect($pkgsub,
1367             Host => $connect->{host},
1368             Port => $connect->{port},
1369             Username => $connect->{username},
1370             Password => $connect->{password},
1371             PublicKey => $connect->{publickey},
1372             PrivateKey => $connect->{privatekey},
1373             Passphrase => $connect->{passphrase},
1374             BaudRate => $connect->{baudrate},
1375             ForceBaud => $connect->{forcebaud},
1376             Parity => $connect->{parity},
1377             DataBits => $connect->{databits},
1378             StopBits => $connect->{stopbits},
1379             Handshake => $connect->{handshake},
1380             Prompt_credentials => $connect->{prompt_credentials},
1381             Terminal_type => $connect->{terminal_type},
1382             Window_size => $connect->{window_size},
1383             Callback => $connect->{callback},
1384             Atomic_connect => $connect->{atomic_connect},
1385 0           );
1386 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
1387             # Unless console already set, set it now; will determine whether or not wake_console is sent upon login
1388 0 0 0       $self->console( $self->connection_type eq 'SERIAL' ||
1389             ($self->connection_type eq 'TELNET' && $self->port != 23) ||
1390             ($self->connection_type eq 'SSH' && $self->port != 22) ) unless defined $self->console;
1391 0           $connect->{stage}++; # Ensure we don't come back here in non-blocking mode
1392             }
1393              
1394             # Login stage
1395             my ($ok, $outRef) = $self->poll_login($pkgsub,
1396             Username => $connect->{username},
1397             Password => $connect->{password},
1398             Read_attempts => $connect->{read_attempts},
1399             Wake_console => $connect->{wake_console},
1400             Data_with_error => $connect->{data_with_error},
1401             Non_recognized_login => $connect->{non_recognized_login},
1402             Generic_login => $connect->{generic_login},
1403             Prompt_credentials => $connect->{prompt_credentials},
1404             Timeout => $connect->{login_timeout},
1405 0           );
1406 0 0         $self->{POLL}{output_buffer} = $$outRef if $ok;
1407 0           return $self->poll_return($ok);
1408             }
1409              
1410              
1411             sub poll_login { # Method to handle login for poll methods (used for both blocking & non-blocking modes)
1412 0     0 1   my $self = shift;
1413 0           my $pkgsub = shift;
1414 0           my $pollsub = "${Package}::login";
1415              
1416 0 0         unless ($self->{POLLING}) { # Sanity check
1417 0           my (undef, $fileName, $lineNumber) = caller;
1418 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1419             }
1420              
1421 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1422 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode', 'read_attempts', 'wake_console',
1423             'data_with_error', 'non_recognized_login', 'generic_login');
1424 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1425 0 0 0       if (@_ && !%args) { # Legacy syntax
1426             ($args{username}, $args{password}, $args{read_attempts}, $args{wake_console},
1427 0           $args{prompt_credentials}, $args{data_with_error}, $args{timeout}, $args{errmode}) = @_;
1428             }
1429             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1430             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1431             # Set method argument keys
1432             username => defined $args{username} ? $args{username} : $self->{USERNAME},
1433             password => defined $args{password} ? $args{password} : $self->{PASSWORD},
1434             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
1435             read_attempts => defined $args{read_attempts} ? $args{read_attempts} : $LoginReadAttempts,
1436             data_with_error => defined $args{data_with_error} ? $args{data_with_error} : $self->{data_with_error},
1437             wake_console => defined $args{wake_console} ? $args{wake_console} : $self->{$Package}{wake_console},
1438             non_recognized_login => defined $args{non_recognized_login} ? $args{non_recognized_login} : $NonRecognizedLogin,
1439             generic_login => defined $args{generic_login} ? $args{generic_login} : $GenericLogin,
1440             # Declare method storage keys which will be used
1441             stage => 0,
1442             login_attempted => undef,
1443             password_sent => undef,
1444             login_error => '',
1445             family_type => undef,
1446             cpu_slot => undef,
1447             detectionFromPrompt => undef,
1448             # Declare keys to be set if method called from another polled method
1449             errmode => $args{errmode},
1450 0 0         };
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1451             # Cache poll structure keys which this method will use
1452 0           $self->poll_struct_cache($pollsub, $args{timeout});
1453             }
1454 0           my $login = $self->{POLL}{$pollsub};
1455 0 0         local $self->{errmode} = $login->{errmode} if defined $login->{errmode};
1456 0 0         return $self->poll_return($self->error("$pkgsub: No connection to login to")) if $self->eof;
1457              
1458 0           my $usernamePrompt = $self->username_prompt;
1459 0           my $passwordPrompt = $self->password_prompt;
1460              
1461 0 0         if ($login->{stage} < 1) { # Initial loginstage & setup - do only once
1462 0           $login->{stage}++; # Ensure we don't come back here in non-blocking mode
1463 0 0         if ($self->{LOGINSTAGE}) {
1464 0           $login->{family_type} = $self->{$Package}{ATTRIB}{'family_type'}; # Might be already set from previous login attempt
1465             }
1466             else { # Flush all attributes, as we assume we are connecting to a new device
1467 0           $self->{$Package}{ATTRIB} = undef;
1468 0           $self->{$Package}{ATTRIBFLAG} = undef;
1469             }
1470             # Handle resuming previous login attempt
1471 0 0 0       if ($self->{LOGINSTAGE} eq 'username' && $login->{username}) { # Resume login from where it was left
    0 0        
    0 0        
1472 0 0         $self->print(line => $login->{username}, errmode => 'return')
1473             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
1474 0           $self->{LOGINSTAGE} = '';
1475 0           $login->{login_attempted} = 1;
1476             }
1477             elsif ($self->{LOGINSTAGE} eq 'password' && $login->{password}) { # Resume login from where it was left
1478 0 0         $self->print(line => $login->{password}, errmode => 'return')
1479             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
1480 0           $self->{LOGINSTAGE} = '';
1481 0           $login->{login_attempted} = 1;
1482             }
1483             elsif ($self->console && $login->{wake_console}) {
1484 0           $self->debugMsg(8,"\nlogin() Sending wake_console sequence >$login->{wake_console}<\n");
1485 0 0         $self->put(string => $login->{wake_console}, errmode => 'return') # Bring connection into life
1486             or return $self->poll_return($self->error("$pkgsub: Unable to send bring alive character sequence // ".$self->errmsg));
1487             }
1488             }
1489 0 0         if ($login->{stage} < 2) { # Main login loop
1490 0           my ($pattern, $patdepth, $deepest);
1491 0           my ($promptType, $capturedPrompt, $switchName, $cliType, $configContext);
1492 0           LOGINLOOP: while (1) {
1493             # Wait until we have read in all available data
1494 0           my $ok = $self->poll_readwait($pkgsub, 1, $login->{read_attempts}, $ReadwaitTimer, $login->{login_error}.'Failed reading login prompt', $login->{data_with_error});
1495 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
1496              
1497 0           $self->debugMsg(8,"\nlogin() Connection input to process:\n>", \$self->{POLL}{read_buffer}, "<\n");
1498 0           $self->{POLL}{output_buffer} .= $self->{POLL}{read_buffer}; # This buffer preserves all the output, in case it is requested
1499 0 0         $self->{POLL}{local_buffer} = $self->{POLL}{read_buffer} =~ /\n/ ? '' : stripLastLine(\$self->{POLL}{local_buffer}); # Flush or keep lastline
1500 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer}; # If read was single line, this buffer appends it to lastline from previous read
1501              
1502             # Pattern matching; try and detect patterns, and record their depth in the input stream
1503 0           $pattern = '';
1504 0           $deepest = -1;
1505 0           foreach my $key (keys %LoginPatterns) {
1506 0 0         if (($patdepth = rindex($self->{POLL}{read_buffer}, $LoginPatterns{$key})) >= 0) { # We have a match
1507 0           $self->debugMsg(8,"\nlogin() Matched pattern $key @ depth $patdepth\n");
1508 0 0         unless ($login->{family_type}) { # Only if family type not already detected
1509             # If a banner is seen, try and extract attributes from it also
1510 0 0 0       if ($key eq 'banner' || $key eq 'menu' || $key eq 'submenu') {
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
1511 0           $login->{family_type} = $Prm{bstk};
1512 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1513 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1);
1514 0 0         if ($key eq 'banner') {
1515 0 0         $self->{POLL}{read_buffer} =~ /\*\*\* ((?:[^\*\n]+?) (?:Switch|Controller|Platform) (?:WC)?\d+.*?)\s+/ &&
1516             $self->_setModelAttrib($1);
1517 0 0         $self->{POLL}{read_buffer} =~ /FW:([\d\.]+)\s+SW:v([\d\.]+)/ && do {
1518 0           $self->_setAttrib('fw_version', $1);
1519 0           $self->_setAttrib('sw_version', $2);
1520             };
1521             }
1522             }
1523             elsif ($key eq 'srbanner') {
1524 0           $login->{family_type} = $Prm{sr};
1525 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1526 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1);
1527 0 0         $self->{POLL}{read_buffer} =~ /\((Secure Router \d+)\)/ && $self->_setModelAttrib($1);
1528 0 0         $self->{POLL}{read_buffer} =~ /Version: (.+)/ && $self->_setAttrib('sw_version', $1);
1529             }
1530             elsif ($key eq 'xlrbanner') {
1531 0           $login->{family_type} = $Prm{xlr};
1532 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1533 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 0);
1534 0 0         $self->{POLL}{read_buffer} =~ /\* Software Release (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
1535             }
1536             elsif ($key eq 'ersbanner' || $key eq 'passportbanner' || $key eq 'pp1600banner') {
1537 0           $login->{family_type} = $Prm{pers};
1538 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1539 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 0);
1540 0 0         $self->{POLL}{read_buffer} =~ /\* Software Release (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
1541             }
1542             elsif ($key eq 'vspbanner' || $key eq 'fabengbanner') {
1543 0           $login->{family_type} = $Prm{pers};
1544 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1545 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1);
1546 0 0         $self->{POLL}{read_buffer} =~ /(?:Software(?: Release)?|Fabric Engine) Build (.+?) / && $self->_setAttrib('sw_version', $1);
1547             }
1548             elsif ($key eq 'wlan9100banner') {
1549 0           $login->{family_type} = $Prm{xirrus};
1550 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1551 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1);
1552 0 0         $self->{POLL}{read_buffer} =~ /AvayaOS Version (.+?) / && $self->_setAttrib('sw_version', $1);
1553             }
1554             elsif ($key eq 'xos') {
1555 0           $login->{family_type} = $Prm{xos};
1556 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1557 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 0, is_xos => 1, is_switch_engine => 0);
1558             }
1559             elsif ($key eq 'switchEngine') {
1560 0           $login->{family_type} = $Prm{xos};
1561 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1562 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 0, is_xos => 1, is_switch_engine => 1);
1563             }
1564             elsif ($key eq 'isw') {
1565 0           $login->{family_type} = $Prm{isw};
1566 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1567 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1, is_isw => 1, baudrate => 115200);
1568             }
1569             elsif ($key eq 'slx') {
1570 0           $login->{family_type} = $Prm{slx};
1571 0           $self->debugMsg(8,"login() Detected family_type = $login->{family_type}\n");
1572 0           $self->_setFamilyTypeAttrib($login->{family_type}, is_nncli => 1, is_slx => 1);
1573             }
1574             }
1575 0 0         if ($patdepth > $deepest) { # We have a deeper match, we keep it
1576 0           ($pattern, $deepest) = ($key, $patdepth);
1577             }
1578             }
1579             }
1580 0 0         $self->debugMsg(8,"\nlogin() Retaining pattern: $pattern\n") if $deepest > -1;
1581              
1582             # Try and match CLI prompts now; this is the only exit point of the loop
1583 0 0         if ($login->{family_type}) { # A family type was already detected from banner
1584 0 0         if ($login->{family_type} eq $Prm{pers}) {
1585 0           foreach my $type ('cli', 'nncli') {
1586 0           $promptType = "$login->{family_type}_$type";
1587 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$promptType})/) {
1588 0           ($capturedPrompt, $switchName, $login->{cpu_slot}, $configContext) = ($1, $2, $3, $4);
1589 0           $cliType = $type;
1590 0           last;
1591             }
1592             }
1593             }
1594             else {
1595 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$login->{family_type}})/) {
1596 0           ($capturedPrompt, $switchName, $configContext) = ($1, $2, $4);
1597 0           $promptType = $login->{family_type};
1598             }
1599             }
1600             }
1601             else { # A family type has not been detected yet; try and detect from received prompt
1602 0           foreach my $key (@InitPromptOrder) {
1603 0 0         if ($self->{POLL}{local_buffer} =~ /($InitPrompt{$key})/) {
1604 0           ($capturedPrompt, $switchName, $login->{cpu_slot}, $configContext) = ($1, $2, $3, $4);
1605 0           $promptType = $key;
1606 0           ($login->{family_type} = $key) =~ s/_(\w+)$//;
1607 0           $cliType = $1;
1608 0           $login->{detectionFromPrompt} = 1;
1609 0           last;
1610             }
1611             }
1612             }
1613 0 0         if ($capturedPrompt) { # We have a prompt, we can exit loop
1614 0           $self->debugMsg(8,"\nlogin() Got CLI prompt for family type $login->{family_type} !\n");
1615 0           $capturedPrompt =~ s/^\x0d//; # Remove initial carriage return if there
1616 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
1617 0 0         if ($login->{family_type} eq $Prm{slx}) { # SLX with vt100 spoils the 1st prompt, correct it here
1618 0           $capturedPrompt =~ s/^\e\[\?7h//;
1619 0           $switchName =~ s/^\e\[\?7h//;
1620             }
1621 0           $self->_setDevicePrompts($promptType, $switchName);
1622 0           $self->_setLastPromptAndConfigContext($capturedPrompt, $configContext);
1623 0 0         $self->_setAttrib('cpu_slot', $login->{cpu_slot}) if $login->{family_type} eq $Prm{pers};
1624 0 0         if ($login->{detectionFromPrompt}) {
1625 0 0 0       if ($login->{family_type} eq $Prm{bstk} || (defined $cliType && $cliType eq 'nncli')) {
      0        
1626 0           $self->_setAttrib('is_nncli', 1);
1627             }
1628             else {
1629 0           $self->_setAttrib('is_nncli', 0);
1630             }
1631             }
1632 0           last LOGINLOOP;
1633             }
1634              
1635             # Now try and match other prompts expected to be seen at the very end of received input stream
1636 0 0         if ($self->{POLL}{read_buffer} =~ /$usernamePrompt/) { # Handle Modular login prompt
    0          
1637 0           $self->debugMsg(8,"\nlogin() Matched Login prompt\n\n");
1638 0           $pattern = 'username';
1639             }
1640             elsif ($self->{POLL}{read_buffer} =~ /$passwordPrompt/) { # Handle Modular password prompt
1641 0           $self->debugMsg(8,"\nlogin() Matched Password prompt\n\n");
1642 0           $pattern = 'password';
1643             }
1644              
1645             # Now handle any pattern matches we had above
1646 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          
1647 0           $self->debugMsg(8,"\nlogin() Processing Stackable Banner\n\n");
1648 0 0         $self->put(string => $CTRL_Y, errmode => 'return')
1649             or return $self->poll_return($self->error("$pkgsub: Unable to send CTRL-Y sequence // ".$self->errmsg));
1650 0           next;
1651             }
1652             elsif ($pattern eq 'menu') { # We got the menu, send a 'c' and get into CLI
1653 0           $self->debugMsg(8,"\nlogin() Processing Stackable Menu\n\n");
1654 0 0         $self->put(string => 'c', errmode => 'return')
1655             or return $self->poll_return($self->error("$pkgsub: Unable to select 'Command Line Interface...' // ".$self->errmsg));
1656 0           next;
1657             }
1658             elsif ($pattern eq 'submenu') { # We are in a sub-menu page, send a 'CTRL_C' to get to main menu page
1659 0           $self->debugMsg(8,"\nlogin() Processing Stackable Sub-Menu page\n\n");
1660 0 0         $self->put(string => $CTRL_C, errmode => 'return')
1661             or return $self->poll_return($self->error("$pkgsub: Unable to go back to main menu page // ".$self->errmsg));
1662 0           next;
1663             }
1664             elsif ($pattern =~ /^more\d$/) { # We are connecting on the console port, and we are in the midst of more-paged output
1665 0           $self->debugMsg(8,"\nlogin() Quitting residual more-paged output for serial port access\n");
1666 0 0         $self->put(string => 'q', errmode => 'return')
1667             or return $self->poll_return($self->error("$pkgsub: Unable to quit more-paged output found after serial connect // ".$self->errmsg));
1668 0           next;
1669             }
1670             elsif ($pattern =~ /^consoleLogMsg\d$/) { # We are connecting on the console port, and this log message is spoiling our 1st prompt
1671 0           $self->debugMsg(8,"\nlogin() Sending extra carriage return after password for serial port access\n");
1672             # On Modular VSPs Console port, immediately after login you get log message :SW INFO user rwa connected via console port
1673             # As this message is appended to the very 1st prompt, we are not able to lock on that initial prompt
1674             # So we feed an extra carriage return so that we can lock on a fresh new prompt
1675 0 0         $self->print(errmode => 'return')
1676             or return $self->poll_return($self->error("$pkgsub: Unable to get new prompt after console log message // ".$self->errmsg));
1677 0           next;
1678             }
1679             elsif ($pattern eq 'lastlogin') { # Last login splash screen; skip it with RETURN key
1680             # This screen appears on ERS4800 release 5.8
1681 0           $self->debugMsg(8,"\nlogin() Processing Last Login screen\n\n");
1682 0 0         $self->print(errmode => 'return')
1683             or return $self->poll_return($self->error("$pkgsub: Unable to send Carriage Return // ".$self->errmsg));
1684 0           next;
1685             }
1686             elsif ($pattern eq 'username') { # Handle login prompt
1687 0           $self->debugMsg(8,"\nlogin() Processing Login/Username prompt\n\n");
1688 0 0         if ($login->{login_attempted}) {
1689 0           $self->{LOGINSTAGE} = 'username';
1690 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1691             }
1692 0 0         unless ($login->{username}) {
1693 0 0         if ($self->{TYPE} eq 'SSH') { # If an SSH connection, we already have the username
1694 0           $login->{username} = $self->{USERNAME};
1695             }
1696             else {
1697 0 0         unless ($login->{prompt_credentials}) {
1698 0           $self->{LOGINSTAGE} = 'username';
1699 0           return $self->poll_return($self->error("$pkgsub: Username required"));
1700             }
1701 0           $login->{username} = promptCredential($login->{prompt_credentials}, 'Clear', 'Username');
1702             }
1703             }
1704 0 0         $self->print(line => $login->{username}, errmode => 'return')
1705             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
1706 0           $self->{LOGINSTAGE} = '';
1707 0           $login->{login_attempted} = 1;
1708 0           next;
1709             }
1710             elsif ($pattern eq 'password') { # Handle password prompt
1711 0           $self->debugMsg(8,"\nlogin() Processing Password prompt\n\n");
1712 0 0         if ($login->{password_sent}) {
1713 0           $self->{LOGINSTAGE} = 'password';
1714 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1715             }
1716 0 0         unless (defined $login->{password}) {
1717 0 0         unless ($login->{prompt_credentials}) {
1718 0           $self->{LOGINSTAGE} = 'password';
1719 0           return $self->poll_return($self->error("$pkgsub: Password required"));
1720             }
1721 0           $login->{password} = promptCredential($login->{prompt_credentials}, 'Hide', 'Password');
1722             }
1723 0 0         $self->print(line => $login->{password}, errmode => 'return')
1724             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
1725 0           $self->{LOGINSTAGE} = '';
1726 0           $login->{password_sent} = 1;
1727 0           next;
1728             }
1729             elsif ($pattern =~ /^localfail/) { # Login failure
1730 0           return $self->poll_return($self->error("$pkgsub: Incorrect Username or Password"));
1731             }
1732             elsif ($pattern eq 'radiusfail') { # Radius Login failure
1733 0           return $self->poll_return($self->error("$pkgsub: Switch got access denied from RADIUS"));
1734             }
1735             elsif ($pattern =~ /^radiustimeout\d$/) { # Radius timeout
1736 0           $login->{login_error} = "Switch got no response from RADIUS servers\n";
1737 0           next; # In this case don't error, as radius falback might still get us in
1738             }
1739 0 0 0       if (!$login->{family_type} && $login->{non_recognized_login}) { # If we have some complete output, which does not match any of the above, we can come out if we asked to
1740 0           return $self->poll_return($self->error("$pkgsub: Non recognized login output"));
1741             }
1742             } # LOGINLOOP
1743 0 0 0       if (!$login->{generic_login} && ($login->{family_type} eq $Prm{generic} || ($login->{detectionFromPrompt} && $self->{LASTPROMPT} !~ /^@/)) ) { # Can't tell, need extended discovery
      0        
1744 0           $login->{stage}++; # Move to next section in non-blocking mode
1745             }
1746             else {
1747 0           $login->{stage} += 2; # Move to section after next
1748             }
1749 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1750             }
1751 0 0         if ($login->{stage} < 3) { # Extended discovery
1752 0           my ($ok, $familyType) = $self->discoverDevice($pkgsub);
1753 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
1754 0           $login->{family_type} = $familyType;
1755 0 0 0       if ($login->{family_type} eq $Prm{generic} && ($self->{errmode} eq 'croak' || $self->{errmode} eq 'die')) {
      0        
1756 0           carp "\n$pkgsub Warning! Device type not detected; using $Prm{generic}\n";
1757             }
1758 0           $login->{stage} += 2; # Move to final section
1759             }
1760 0 0         if ($login->{stage} < 4) { # Generic_login OR family type was detected, not just from the prompt (though we do rely on prompt alone for Standby CPUs on PassportERS)
1761 0 0 0       if ($login->{family_type} eq $Prm{pers} || $login->{family_type} eq $Prm{xlr}) {
1762 0 0         $self->_setAttrib('is_master_cpu', $self->{LASTPROMPT} =~ /^@/ ? 0 : 1);
1763 0 0         $self->_setAttrib('is_dual_cpu', 1) if $self->{LASTPROMPT} =~ /^@/;
1764             }
1765 0 0         $self->_setFamilyTypeAttrib($login->{family_type}) if $login->{detectionFromPrompt};
1766             }
1767              
1768             # Store credentials if these were used
1769 0 0         ($self->{USERNAME}, $self->{PASSWORD}) = ($login->{username}, $login->{password}) if $login->{login_attempted};
1770 0           return $self->poll_return(1);
1771             }
1772              
1773              
1774             sub poll_cmd { # Method to handle cmd for poll methods (used for both blocking & non-blocking modes)
1775 0     0 1   my $self = shift;
1776 0           my $pkgsub = shift;
1777 0           my $pollsub = "${Package}::cmd";
1778              
1779 0 0         unless ($self->{POLLING}) { # Sanity check
1780 0           my (undef, $fileName, $lineNumber) = caller;
1781 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
1782             }
1783              
1784 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
1785 0           my @validArgs = ('command', 'feed_list', 'prompt', 'reset_prompt', 'more_prompt', 'cmd_confirm_prompt',
1786             'cmd_initiated_prompt', 'more_pages', 'timeout', 'errmode', 'progress_dots');
1787 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
1788 0 0 0       if (@_ && !%args) { # Legacy syntax
1789             ($args{command}, $args{more_pages}, $args{prompt}, $args{reset_prompt}, $args{timeout}, $args{errmode},
1790 0           $args{feed_list}, $args{cmd_confirm_prompt}, $args{cmd_initiated_prompt}) = @_;
1791             }
1792 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
1793             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
1794             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
1795             # Set method argument keys
1796             command => $args{command},
1797             prompt => defined $args{prompt} ? $args{prompt} : $self->{$Package}{prompt_qr},
1798             more_prompt => defined $args{more_prompt} ? $args{more_prompt} : $self->{$Package}{morePrompt_qr},
1799             more_prompt_delay => defined $args{more_prompt} ? undef : $self->{$Package}{morePromptDelay_qr},
1800             more_pages => defined $args{more_pages} ? $args{more_pages} : $self->{$Package}{morePaging},
1801             reset_prompt => $args{reset_prompt} && defined $self->{$Package}{PROMPTTYPE},
1802             yn_prompt => defined $args{cmd_confirm_prompt} ? $args{cmd_confirm_prompt} : $self->{$Package}{cmd_confirm_prompt_qr},
1803             cmd_prompt => defined $args{cmd_initiated_prompt} ? $args{cmd_initiated_prompt} : $self->{$Package}{cmd_initiated_prompt_qr},
1804             feed_data => $args{feed_list},
1805             progress_dots => defined $args{progress_dots} ? $args{progress_dots} : $self->{$Package}{progressDots},
1806             # Declare method storage keys which will be used
1807             stage => 0,
1808             lastLine => '',
1809             outputNewline => '',
1810             progress => undef,
1811             alreadyCmdTimeout => 0,
1812             ynPromptCount => undef,
1813             cmdPromptCount => undef,
1814             cmdEchoRemoved => 0,
1815             lastPromptEchoedCmd => undef,
1816             cache_timeout => defined $args{timeout} ? $args{timeout} : $self->{POLL}{timeout},
1817             noRefreshCmdDone => undef,
1818             # Declare keys to be set if method called from another polled method
1819             errmode => $args{errmode},
1820 0 0 0       };
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1821             # Cache poll structure keys which this method will use
1822 0           $self->poll_struct_cache($pollsub, $args{timeout});
1823             }
1824              
1825 0           my $cmd = $self->{POLL}{$pollsub};
1826 0 0         local $self->{errmode} = $cmd->{errmode} if defined $cmd->{errmode};
1827 0 0         return $self->poll_return($self->error("$pkgsub: No connection to send cmd to")) if $self->eof;
1828 0 0         $cmd->{prompt} = $InitPrompt{$self->{$Package}{PROMPTTYPE}} if $cmd->{reset_prompt};
1829 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
1830 0           my $newLineLastLine = 0;
1831              
1832 0 0         if ($cmd->{stage} < 1) { # Send command - do only once
1833 0           $cmd->{stage}++; # Ensure we don't come back here in non-blocking mode
1834 0 0         if (defined $cmd->{command}) {
1835 0           my $command = $cmd->{command};
1836             # In NNCLI mode, if command ends with ?, append CTRL-X otherwise partial command will appear after next prompt
1837 0 0 0       if ($command =~ /\?\s*$/ && $self->{$Package}{ATTRIB}{'is_nncli'}) {
1838 0 0         if ($familyType eq $Prm{sr}) { $command .= $CTRL_U }
  0            
1839 0           else { $command .= $CTRL_X }
1840             }
1841             # Flush any unread data which might be pending
1842 0           $self->read(blocking => 0);
1843             # Send the command
1844 0           $self->debugMsg(8,"\ncmd() Sending command:>", \$command, "<\n");
1845 0 0         $self->print(line => $command, errmode => 'return')
1846             or return $self->poll_return($self->error("$pkgsub: Unable to send CLI command: $command // ".$self->errmsg));
1847             }
1848             }
1849 0           CMDLOOP: while (1) {
1850             # READ in data
1851 0 0         if ($cmd->{stage} == 1) { # Normal data read
    0          
1852 0           my $ok = $self->poll_read($pkgsub); # We always come back even in case of error
1853 0 0 0       return $self->poll_return($ok) if defined $ok && $ok == 0; # Come out only in case of non-blocking not ready
1854 0 0         unless (defined $ok) { # We catch timeout event here
1855 0 0 0       if ($cmd->{alreadyCmdTimeout} || !length $familyType || $familyType eq $Prm{generic}) {
      0        
1856 0           return $self->poll_return($self->error("$pkgsub: Failed after sending command // ".$self->errmsg));
1857             }
1858 0           $self->debugMsg(4, "\ncmd() Initial cmd timeout; attempting reset_prompt\n");
1859 0 0         $self->print(errmode => 'return') # Send a carriage return and we have a 2nd try at catching prompt
1860             or return $self->poll_return($self->error("$pkgsub: Unable to send Carriage Return // ".$self->errmsg));
1861 0           $self->{POLL}{timeout} = $cmd->{cache_timeout} * $CmdTimeoutRatio; # Re-arm timeout
1862 0           $cmd->{prompt} = $InitPrompt{$self->{$Package}{PROMPTTYPE}};
1863 0           $cmd->{alreadyCmdTimeout} = 1; # Ensures that at next timeout we generate the error mode action (so cannot loop)
1864 0           $cmd->{reset_prompt} = 1;
1865 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1866 0           next CMDLOOP;
1867             }
1868             }
1869             elsif ($cmd->{stage} == 2) { # cmd_prompt / Wait if more data coming
1870 0           my $ok = $self->poll_readwait($pkgsub, 0, $CmdPromptReadAttempts, $ReadwaitTimer, "Cmd_prompt; unable to check for more data");
1871 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
1872 0           $cmd->{stage} = 1; # Whatever the outcome below, we will revert to normal data reads
1873 0 0         unless (length $self->{POLL}{read_buffer}) { # No more data => no false trigger
1874 0           $self->debugMsg(8,"\ncmd() Detected CMD embedded prompt\n");
1875 0           my $feed;
1876 0 0         if ($feed = shift(@{$cmd->{feed_data}})) {
  0            
1877 0           $self->debugMsg(8,"cmd() - Have data to feed:>", \$feed, "<\n");
1878             }
1879             else {
1880 0 0         if (++$cmd->{cmdPromptCount} > $self->{$Package}{cmd_feed_timeout}) {
1881 0           return $self->poll_return($self->error("$pkgsub: Command embedded prompt timeout"));
1882             }
1883 0           $feed = '';
1884 0           $self->debugMsg(8,"cmd() - No data to feed!\n");
1885             }
1886 0 0         $self->print(line => $feed, errmode => 'return')
1887             or return $self->poll_return($self->error("$pkgsub: Unable to feed data at cmd prompt // ".$self->errmsg));
1888              
1889 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
1890 0           next CMDLOOP;
1891             }
1892             }
1893             else { # Stage = 3 ; more prompt delay / 1 non-blocking read to ascertain if partial more prompt can be processed or not
1894 0           my $ok = $self->poll_readwait($pkgsub, 0, 1, $ReadwaitTimer, "Delayed More prompt; unable to check for more data");
1895 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
1896 0           $cmd->{stage} = 1; # We immediately revert to normal data reads
1897             }
1898              
1899             # Process data
1900 0 0         if ($cmd->{progress_dots}) { # Print dots for progress
1901 0 0         _printDot() unless defined $cmd->{progress};
1902 0 0         if ( ( $cmd->{progress} += length($self->{POLL}{read_buffer}) ) > $cmd->{progress_dots}) {
1903 0           _printDot();
1904 0           $cmd->{progress} -= $cmd->{progress_dots};
1905             }
1906             }
1907              
1908 0 0         unless ($cmd->{cmdEchoRemoved}) { # If the echoed cmd was not yet removed
1909 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer}; # Append to local_buffer
1910 0 0         if ($self->{POLL}{local_buffer} =~ s/(^.*\n)//) { # if we can remove it now
1911 0           $self->debugMsg(8,"\ncmd() Stripped echoed command\n");
1912 0           $cmd->{lastPromptEchoedCmd} = $self->{LASTPROMPT} . $1;
1913 0 0         $cmd->{lastPromptEchoedCmd} =~ s/\x10?\x00//g if $familyType eq $Prm{xirrus}; # WLAN9100 in telnet
1914 0           $cmd->{cmdEchoRemoved} = 1;
1915 0           $self->{POLL}{read_buffer} = $self->{POLL}{local_buffer}; # Re-prime read_buffer so that we fall through below with what's left
1916 0           $self->{POLL}{local_buffer} = ''; # Empty local_buffer so that we fall through below
1917 0 0         next CMDLOOP unless length $self->{POLL}{read_buffer}; # If we have remaining data, fall through, else do next cycle
1918             }
1919             else { # if we can't then no point processing patterns below
1920 0           next CMDLOOP; # Do next read
1921             }
1922             }
1923             # If we get here, it means that the echo-ed cmd has been removed
1924             # read_buffer will either hold remaining output after removing echoed cmd
1925             # or it will hold the most recent data read
1926              
1927 0           my $output = $cmd->{lastLine}.$self->{POLL}{read_buffer}; # New output appended to previous lastLine
1928 0           $self->{POLL}{output_buffer} .= $cmd->{outputNewline}; # Re-add newLine if had been withheld
1929 0 0         $self->debugMsg(8,"\ncmd() Output for new cycle:\n>", \$output, "<\n") if length $output;
1930              
1931             # We check for refresh patterns here, as the patterns may not be in the lastline
1932 0 0 0       if ($self->{$Package}{noRefreshCmdPattern} && !$cmd->{noRefreshCmdDone} && $output =~ /$self->{$Package}{noRefreshCmdPattern}/m) { # We have a refreshed command
      0        
1933 0           $self->debugMsg(8,"\ncmd() Refreshed command output detected; feeding noRefreshCmdSend\n");
1934 0 0         $self->put(string => $self->{$Package}{noRefreshCmdSend}, errmode => 'return')
1935             or return $self->poll_return($self->error("$pkgsub: Unable to cancel refreshed command // ".$self->errmsg));
1936 0           $cmd->{noRefreshCmdDone} = 1; # Make sure we do this only once
1937             }
1938              
1939 0 0         if (length $output) { # Clean up patterns
1940 0           $output =~ s/^(?:\x08 \x08)+//; # Remove backspace chars following a more prompt, if any
1941 0           $output =~ s/^\x08+ +\x08+//; # Remove backspace chars following a more prompt, if any (Wing and SLX)
1942 0 0         $output =~ s/\x08+ +\x08+// if $familyType eq $Prm{hive}; # On HiveOS, these are not necessarily at the beginning of the line
1943 0 0         $output =~ s/^\x0d *\x0d// if $familyType eq $Prm{s200}; # Remove Secure Router CR+spaces+0+CR sequence following more prompt
1944 0 0         $output =~ s/^\x0d *\x00\x0d// if $familyType eq $Prm{sr}; # Remove Secure Router CR+spaces+0+CR sequence following more prompt
1945 0 0         $output =~ s/^(?:\e\[D \e\[D)+// if $familyType eq $Prm{isw}; # Remove ISW escape sequences following more prompt
1946 0 0         if ($familyType eq $Prm{slx}) {
1947 0           $output =~ s/(?:(?:\e\[[58]D|\x0d)?\e\[K|(?:\e\[\dD|\x08)*\x0d {8}(?:\e\[\dD|\x08)*\x0d?|\x08{8} {8}\x08{8})//; # Remove SLX escape sequence following more prompt or final END prompt (Telnet/ssh | Console)
1948 0           $output =~ s/ ?\e\[\d+;\d+H//g; # Remove SLX escape sequences on Console output of some command
1949 0           $output =~ s/\e\[m\x0f(?:\e\[7m)?//g; # SLX9850 on serial port, spits these all the time..
1950             }
1951 0 0         $output =~ s/^(?:\e\[60;D|(?:\e\[m)?\x0d)\e\[K// if $familyType eq $Prm{xos}; # Remove ExtremeXOS escape sequence following more prompt
1952 0 0 0       $output =~ s/\e\[2J\e\[H// if $cmd->{noRefreshCmdDone} && $familyType eq $Prm{pers}; # Recover from ExtremeXOS refreshed command
1953 0 0 0       $output =~ s/\x0d\e\[23A\e\[J/\n/ if $cmd->{noRefreshCmdDone} && $familyType eq $Prm{xos}; # Recover from ExtremeXOS refreshed command
1954 0 0 0       $output =~ s/$cmd->{more_prompt}(?:\e\[D \e\[D)+//g if $familyType eq $Prm{isw} && $cmd->{more_prompt}; # Remove double --more-- prompt which is not used
1955 0 0         if ($familyType eq $Prm{xirrus}) {
1956 0           $output =~ s/\x10?\x00//g; # Remove weird chars that WLAN9100 peppers output with, with telnet only,
1957             # ...in some case even not at beginning of line..
1958 0           $output =~ s/^ ?\x0d *\x0d//; # Remove WLAN9100 CR+spaces+CR sequence following more prompt
1959             # .. with telnet, the WLAN9100 echoes back the space which was sent to page
1960             }
1961             # Note, order of these matches is important
1962 0           $output =~ s/^\x0d+//mg; # Remove spurious CarriageReturns at beginning of line, a BPS/470 special
1963 0           $output =~ s/\x0d+$//mg; # Remove spurious CarriageReturns at end of each line, 5500, 4500...
1964             }
1965 0           $cmd->{lastLine} = stripLastLine(\$output); # We strip a new lastLine from it
1966              
1967             # Here we either hold data in $output or in $cmd->{lastLine} or both
1968 0 0         $self->debugMsg(8,"\ncmd() Output to keep:\n>", \$output, "<\n") if length $output;
1969 0 0         $self->debugMsg(8,"\ncmd() Lastline stripped:\n>", \$cmd->{lastLine}, "<\n") if length $cmd->{lastLine};
1970              
1971 0 0         if (length $output) { # Append output now
1972 0           $self->{POLL}{local_buffer} .= $output; # Append to local_buffer
1973 0           $self->{POLL}{output_buffer} .= $output; # Append to output_buffer as well
1974             }
1975            
1976             # Since some more prompt pattern matches can include an initial \n newline which needs removing, we need lastLine to hold that \n
1977 0 0 0       if (length $cmd->{lastLine} && $self->{POLL}{local_buffer} =~ s/\n\n$/\n/) { # If output had x2 trailing newlines, strip last ...
1978 0           $cmd->{lastLine} = "\n" . $cmd->{lastLine}; # ... and pre-pend it to lastLine
1979 0           $cmd->{outputNewline} = chop $self->{POLL}{output_buffer}; # And chop & store \n from output_buffer
1980 0           $newLineLastLine = 1; # and remember it
1981 0           $self->debugMsg(8,"\ncmd() Lastline adjusted:\n>", \$cmd->{lastLine}, "<\n");
1982             }
1983             else {
1984 0           $cmd->{outputNewline} = ''; # Clear it
1985 0           $newLineLastLine = 0; # Clear it
1986             }
1987              
1988 0 0         next CMDLOOP unless length $cmd->{lastLine};
1989              
1990 0 0         if ($cmd->{lastLine} =~ s/($cmd->{prompt})//) {
1991 0           my ($cap1, $cap2, $cap3) = ($1, $2, $3); # We need to store these in temporary variables
1992 0 0         $self->_setDevicePrompts(undef, $cap2) if $cmd->{reset_prompt};
1993 0 0         $self->_setLastPromptAndConfigContext($cap1, $cmd->{reset_prompt} ? $cap3 : $cap2);
1994 0 0 0       unless ($newLineLastLine && !length $cmd->{lastLine}) { # Only if we did not gobble the \n ...
1995 0           $self->{POLL}{output_buffer} .= $cmd->{outputNewline}; # ... re-add to output its final \n
1996             }
1997 0           $self->debugMsg(8,"\ncmd() prompt detected; cmd complete!\n");
1998 0           last CMDLOOP;
1999             }
2000 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        
2001 0           $self->debugMsg(8,"\ncmd() more prompt delay pattern detected; forcing 1 cycle readwait\n");
2002 0           $cmd->{stage} = 3; # Force a 1 cycle readwait at next cycle
2003 0           $cmd->{morePromptDelayed} = 1; # Make sure we don't come back here at next cycle
2004 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
2005 0           next CMDLOOP;
2006             }
2007 0 0 0       if ($cmd->{more_prompt} && $cmd->{lastLine} =~ s/(?:$cmd->{more_prompt})$//) { # We have a more prompt
2008 0           $cmd->{morePromptDelayed} = 0; # Reset this flag
2009 0 0         if ($cmd->{lastLine} =~ s/^\n//) { # If we did not gobble the \n remove it and re-add it (residual lastLine can still be rolled over)
2010 0           $self->{POLL}{local_buffer} .= "\n";
2011 0 0         $self->{POLL}{output_buffer} .= $cmd->{outputNewline} if $newLineLastLine;
2012             }
2013 0 0         $cmd->{outputNewline} = '' if $newLineLastLine; # Either way (\n gobbled or not) we clear it
2014 0           my $char;
2015 0 0 0       if (defined $MoreSkipWithin{$familyType} && $cmd->{more_pages} == 0) { # On ISW we have an option to skip more paging
    0 0        
2016 0           $char = $MoreSkipWithin{$familyType};
2017 0           $self->debugMsg(8,"\ncmd() More prompt detected; skipping subsequent by feeding '$char'\n");
2018             }
2019             elsif ($cmd->{more_pages} == 0 || $cmd->{more_pages}-- > 1) { # We get the next page
2020 0           $char = $Space;
2021 0           $self->debugMsg(8,"\ncmd() More prompt detected; feeding 'SPACE'\n");
2022             }
2023             else { # We quit here
2024 0           $char = 'q';
2025 0           $self->debugMsg(8,"\ncmd() More prompt detected; feeding 'Q'\n");
2026             }
2027 0 0         $self->put(string => $char, errmode => 'return')
2028             or return $self->poll_return($self->error("$pkgsub: Unable to page at more prompt // ".$self->errmsg));
2029 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
2030 0           next CMDLOOP;
2031             }
2032 0 0 0       if ($cmd->{yn_prompt} && $cmd->{lastLine} =~ /$cmd->{yn_prompt}/) { # We have a Y/N prompt
2033 0 0         if (++$cmd->{ynPromptCount} > $self->{$Package}{cmd_feed_timeout}) {
2034 0           return $self->poll_return($self->error("$pkgsub: Y/N confirm prompt timeout"));
2035             }
2036 0           $self->debugMsg(8,"\ncmd() Y/N prompt detected; feeding 'Y'\n");
2037 0 0         $self->print(line => 'y', errmode => 'return')
2038             or return $self->poll_return($self->error("$pkgsub: Unable to confirm at Y/N prompt // ".$self->errmsg));
2039 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
2040 0           next CMDLOOP;
2041             }
2042 0 0 0       if ($cmd->{cmd_prompt} && $cmd->{lastLine} =~ /$cmd->{cmd_prompt}/) { # We have a prompt for additional input
2043             # But, this pattern risks matching against transient data; so check if more data coming
2044 0           $self->debugMsg(8,"\ncmd() cmd-prompt detected; forcing readwait\n");
2045 0           $cmd->{stage} = 2; # Force a readwait at next cycle
2046 0 0         return $self->poll_return(0) unless $self->{POLL}{blocking};
2047 0           next CMDLOOP;
2048             }
2049              
2050             # Having lastLine with \n newline can screw up cleanup patterns above, so after above prompt matching we have it removed here
2051 0 0         $self->{POLL}{local_buffer} .= "\n" if $cmd->{lastLine} =~ s/^\n//; # If it's there we take it off
2052             }# CMDLOOP
2053 0           $self->{POLL}{output_result} = $self->_determineOutcome(\$self->{POLL}{local_buffer}, $cmd->{lastPromptEchoedCmd});
2054 0           return $self->poll_return(1);
2055             }
2056              
2057              
2058             sub poll_attribute { # Method to handle attribute for poll methods (used for both blocking & non-blocking modes)
2059 0     0 1   my $self = shift;
2060 0           my $pkgsub = shift;
2061 0           my $pollsub = "${Package}::attribute";
2062              
2063 0 0         unless ($self->{POLLING}) { # Sanity check
2064 0           my (undef, $fileName, $lineNumber) = caller;
2065 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
2066             }
2067              
2068 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
2069 0           my @validArgs = ('attribute', 'reload', 'timeout', 'errmode');
2070 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
2071 0 0 0       if (@_ && !%args) { # Legacy syntax
2072 0           ($args{attribute}, $args{reload}, $args{timeout}, $args{errmode}) = @_;
2073             }
2074             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
2075             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
2076             # Set method argument keys
2077             attribute => $args{attribute},
2078             reload => $args{reload},
2079             # Declare method storage keys which will be used
2080             stage => 0,
2081             debugMsg => 0,
2082             # Declare keys to be set if method called from another polled method
2083             errmode => $args{errmode},
2084 0           };
2085             # Cache poll structure keys which this method will use
2086 0           $self->poll_struct_cache($pollsub, $args{timeout});
2087             }
2088 0           my $attrib = $self->{POLL}{$pollsub};
2089 0 0         local $self->{errmode} = $attrib->{errmode} if defined $attrib->{errmode};
2090 0 0         return $self->poll_return($self->error("$pkgsub: No connection for attributes")) if $self->eof;
2091 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
2092              
2093 0 0         if ($attrib->{stage} < 1) { # 1st stage
2094 0 0         return $self->poll_return($self->error("$pkgsub: No attribute provided")) unless defined $attrib->{attribute};
2095 0 0         return $self->poll_return(1) unless $familyType; # Value returned is undef
2096              
2097 0           $attrib->{stage} += 2; # Assume no login() required and that we move directly to 3rd stage
2098 0 0         if ($attrib->{reload}) { # Force reload, either via forced login() or resetting ATTRIBFLAG
2099 0 0 0       if ($attrib->{attribute} eq 'family_type' || $attrib->{attribute} eq 'is_nncli' || $attrib->{attribute} eq 'is_acli'
      0        
      0        
      0        
2100             || $attrib->{attribute} eq 'is_master_cpu' || $attrib->{attribute} eq 'cpu_slot') {
2101 0 0         $self->print or return $self->poll_return($self->error("$pkgsub: Unable to refresh device connection"));
2102 0           $attrib->{stage}--; # Move to 2nd stage
2103             }
2104             else {
2105 0           $self->{$Package}{ATTRIBFLAG}{$attrib->{attribute}} = undef;
2106             }
2107             }
2108             }
2109              
2110 0 0         if ($attrib->{stage} < 2) { # 2nd stage - wait for login to complete
2111 0           my $ok = $self->poll_login($pkgsub);
2112 0 0         return $self->poll_return($ok) unless $ok;
2113 0           $attrib->{stage}++; # Move to 3rd stage
2114             }
2115              
2116 0 0         if ($attrib->{stage} < 3) { # 3rd stage
2117             # If the attribute is set already, return it at once and quit
2118 0 0         if (defined $self->{$Package}{ATTRIBFLAG}{$attrib->{attribute}}) {
2119 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2120 0           return $self->poll_return(1);
2121             }
2122             # Go no further if generic family type
2123 0 0         return $self->poll_return(1) if $familyType eq $Prm{generic}; # Value returned is undef
2124 0           $attrib->{stage}++; # Move to next stage
2125             }
2126              
2127             # Otherwise go set the attribute
2128 0 0         if ($familyType eq $Prm{pers}) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
2129 0 0         $attrib->{attribute} eq 'is_ha' && do {
2130 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show ha-state', 'show ha-state']);
2131 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2132 0 0         if ($$outref =~ /Current CPU State : Disabled State./) {
    0          
2133 0           $self->_setAttrib('is_ha', 0);
2134             }
2135             elsif ($$outref =~ /Current CPU State/) {
2136 0           $self->_setAttrib('is_ha', 1);
2137             }
2138             else { # For example on ERS8300 or ERS1600
2139 0           $self->_setAttrib('is_ha', undef);
2140             }
2141 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2142 0           return $self->poll_return(1);
2143             };
2144 0 0         $attrib->{attribute} eq 'sw_version' && do {
2145 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show sys sw', 'show sys software']);
2146 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2147 0 0         $$outref =~ /Version : Build (?i:v|REL)?(.+?) / && $self->_setAttrib('sw_version', $1);
2148 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2149 0           return $self->poll_return(1);
2150             };
2151 0 0         $attrib->{attribute} eq 'fw_version' && do {
2152 0 0         if ($attrib->{stage} < 4) { # 4th stage
2153 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show bootconfig info', 'show boot config general']);
2154 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2155 0 0         if ($$outref =~ /Version:\s+(?i:v|REL)?(.+)/) {
2156 0           $self->_setAttrib('fw_version', $1);
2157 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2158 0           return $self->poll_return(1);
2159             }
2160             else {
2161 0           $attrib->{stage}++; # Move to next stage
2162 0           $attrib->{debugMsg} = 0;
2163             }
2164             }
2165 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show bootconfig info', 'show boot config info']); # On 8300 it's 'show boot config info'
2166 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2167 0 0         if ($$outref =~ /Version:\s+(?i:v|REL)?(.+)/) {
2168 0           $self->_setAttrib('fw_version', $1);
2169             }
2170             else { # VSP9000 has no fw_version (when command executed on standby CPU)
2171 0           $self->_setAttrib('fw_version', undef);
2172             }
2173 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2174 0           return $self->poll_return(1);
2175             };
2176 0 0         $attrib->{attribute} eq 'stp_mode' && do {
2177 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show bootconfig flags', 'show boot config flags'], 1);
2178 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2179 0 0         if ($$outref =~ /flags spanning-tree-mode (mstp|rstp)/) {
2180 0           $self->_setAttrib('stp_mode', $1);
2181             }
2182             else {
2183 0           $self->_setAttrib('stp_mode', 'stpg');
2184             }
2185 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2186 0           return $self->poll_return(1);
2187             };
2188 0 0         $attrib->{attribute} eq 'baudrate' && do {
2189 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show bootconfig sio', 'show boot config sio'], 1);
2190 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2191 0 0         if ($$outref =~ /sio (?:console )?baud (\d+)/) { # On VSP/8600 it's "sio console baud 9600"; on 8300/1600 "sio baud 9600"
2192 0           $self->_setAttrib('baudrate', $1);
2193             }
2194 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2195 0           return $self->poll_return(1);
2196             };
2197 0 0         $attrib->{attribute} eq 'max_baud' && do {
2198 0 0         if ($attrib->{stage} < 4) { # 4th stage
2199 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['config bootconfig sio console baud ?', "boot config sio console baud ?$CTRL_C"], undef, 1);
2200 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2201             # VSP9k : <9600 - 115200> Baud rate {9600 | 19200 | 38400 | 57600 | 115200}
2202             # 8600acli : <1200-115200> Rate
2203             # 8600ppcli : = what rate {1200..115200}
2204 0 0         if ($$outref =~ /(?:-|\.\.)\s?(\d+)[>}]/) {
2205 0           $self->_setAttrib('max_baud', $1);
2206 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2207 0           return $self->poll_return(1);
2208             }
2209             else {
2210 0           $attrib->{stage}++; # Move to next stage
2211 0           $attrib->{debugMsg} = 0;
2212             }
2213             }
2214 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['config bootconfig sio baud ?', "boot config sio baud ?$CTRL_C"], undef, 1);
2215 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2216             # 8300nncli : <1200-115200> rate
2217             # 8300ppcli : = what rate {2400|4800|9600|19200|38400|57600|115200} IN {1200..115200}
2218             # 1600 : = what rate {1200..115200}
2219 0 0         if ($$outref =~ /(?:-|\.\.)\s?(\d+)[>}]/) {
2220 0           $self->_setAttrib('max_baud', $1);
2221             }
2222 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2223 0           return $self->poll_return(1);
2224             };
2225 0 0         if ($self->{$Package}{ATTRIB}{'is_master_cpu'}) { # On Master CPU
2226 0 0 0       ($attrib->{attribute} eq 'is_dual_cpu' || $attrib->{attribute} eq 'base_mac') && do {
2227 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show sys info', 'show sys-info'], 4);
2228 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2229 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && do {
2230 0           my $model = $1; # Record it, we need to set the model type after is_apls
2231 0 0         if ($$outref =~ / BoxType: (.+)/gc) { # APLS boxes show a boxtype on same line
2232 0           $self->_setBoxTypeAttrib($1);
2233 0           $self->_setAttrib('is_apls', 1);
2234 0           $self->_setModelAttrib($model); # Must be set after is_apls so that is_voss gets set as well
2235             }
2236             else {
2237 0           $self->_setAttrib('apls_box_type', undef);
2238 0           $self->_setAttrib('is_apls', 0);
2239 0           $self->_setModelAttrib($model);
2240             }
2241             };
2242 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2243 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) {
2244 0 0         if ($$outref =~ /BrandName:?\s+: (.+)/gc) { # On VOSS VSPs we read it
2245 0           (my $brandname = $1) =~ s/, Inc\.$//; # Remove 'Inc.'
2246 0           $brandname =~ s/\.$//; # Remove trailing full stop
2247 0           $self->_setAttrib('brand_name', $brandname);
2248             }
2249             else { # VSP9000 case, it is_voss, but reports no BrandName, so we set it..
2250 0           $self->_setAttrib('brand_name', 'Avaya');
2251             }
2252             }
2253             else { # Non-VOSS PassportERS
2254 0           $self->_setAttrib('brand_name', undef);
2255             }
2256 0 0         $$outref =~ /BaseMacAddr\s+: (.+)/g && $self->_setBaseMacAttrib($1);
2257 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
      0        
2258             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2259             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2260             ) {
2261 0           $self->_setAttrib('is_dual_cpu', 1);
2262             }
2263             else {
2264 0           $self->_setAttrib('is_dual_cpu', 0);
2265             }
2266             # Attributes below are beyond demanded pages of output, but we still check them in case more paging was disabled
2267 0 0         $$outref =~ /Virtual IP\s+: (.+)/g && $self->_setAttrib('oob_virt_ip', $1);
2268 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2269 0           return $self->poll_return(1);
2270             };
2271             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'is_apls' ||
2272             $attrib->{attribute} eq 'is_voss' || $attrib->{attribute} eq 'is_fabric_engine' ||
2273             $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
2274             (!$self->{$Package}{ATTRIBFLAG}{'model'} && ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports')) || # We need 'model' attrib for port/slot ones
2275             (!$self->{$Package}{ATTRIBFLAG}{'is_voss'} && $attrib->{attribute} =~ /^(?:is_)?oob_/) # We need 'is_voss' attrib for oob ones
2276 0 0 0       ) && do {
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
2277 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show sys info', 'show sys-info'], 1);
2278 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2279 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && do {
2280 0           my $model = $1; # Record it, we need to set the model type after is_apls
2281 0 0         if ($$outref =~ / BoxType: (.+)/gc) { # APLS boxes show a boxtype on same line
2282 0           $self->_setBoxTypeAttrib($1);
2283 0           $self->_setAttrib('is_apls', 1);
2284 0           $self->_setModelAttrib($model); # Must be set after is_apls so that is_voss gets set as well
2285             }
2286             else {
2287 0           $self->_setAttrib('apls_box_type', undef);
2288 0           $self->_setAttrib('is_apls', 0);
2289 0           $self->_setModelAttrib($model);
2290             }
2291             };
2292 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2293 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) {
2294 0 0         if ($$outref =~ /BrandName:?\s+: (.+)/gc) { # On VOSS VSPs we read it
2295 0           (my $brandname = $1) =~ s/, Inc\.$//; # Remove 'Inc.'
2296 0           $brandname =~ s/\.$//; # Remove trailing full stop
2297 0           $self->_setAttrib('brand_name', $brandname);
2298             }
2299             else { # VSP9000 case, it is_voss, but reports no BrandName, so we set it..
2300 0           $self->_setAttrib('brand_name', 'Avaya');
2301             }
2302             }
2303             else { # Non-VOSS PassportERS
2304 0           $self->_setAttrib('brand_name', undef);
2305             }
2306 0 0         $$outref =~ /BaseMacAddr\s+: (.+)/g && $self->_setBaseMacAttrib($1); # Might not match on 8600 as on page 2
2307             # Attributes below are beyond demanded pages of output, but we still check them in case more paging was disabled
2308 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
    0 0        
2309             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2310             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2311             ) {
2312 0           $self->_setAttrib('is_dual_cpu', 1);
2313             }
2314             elsif ($$outref =~ /System Error Info :/) { # Output which follows Card Info, i.e. the output was there but no CP dormant matched
2315 0           $self->_setAttrib('is_dual_cpu', 0);
2316             }
2317 0 0         $$outref =~ /Virtual IP\s+: (.+)/g && $self->_setAttrib('oob_virt_ip', $1);
2318 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        
2319             $attrib->{attribute} eq 'apls_box_type' || $attrib->{attribute} eq 'brand_name') { # Needs to match the same listed on beginning of if block above
2320 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2321 0           return $self->poll_return(1);
2322             }
2323             else { # If an attribute that just needed 'model', fall through to appropriate section below
2324 0           $attrib->{debugMsg} = 0;
2325             }
2326             };
2327 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2328 0 0 0       if ($self->{$Package}{ATTRIB}{'is_nncli'} && $self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-8[36]\d\d/) { # 8300/8600 NNCLI case
2329 0 0         if ($attrib->{stage} < 4) { # 4th stage
2330 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show interfaces fastEthernet name']);
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           $self->_setSlotPortAttrib($outref);
2333 0           $attrib->{stage}++; # Move to next stage
2334 0           $attrib->{debugMsg} = 0;
2335             }
2336 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show interfaces gigabitEthernet name']);
2337 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2338 0           $self->_setSlotPortAttrib($outref);
2339 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2340 0           return $self->poll_return(1);
2341             }
2342             else { # All other cases: 8300/8600/8800 PPCLI, 8800 NNCLI, VSP
2343 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show ports info name', 'show interfaces gigabitEthernet high-secure']);
2344 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2345 0           $self->_setSlotPortAttrib($outref);
2346 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2347 0           return $self->poll_return(1);
2348             }
2349             };
2350 0 0         $attrib->{attribute} =~ /^(?:is_)?oob_/ && do {
2351 0 0         if ($self->{$Package}{ATTRIB}{'is_voss'}) { # VSP based PassportERS (VOSS)
2352 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show ip interface vrf MgmtRouter']);
2353 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2354 0           my ($ip1, $ip2, $ipv);
2355 0 0         $ip1 = $1 if $$outref =~ /mgmt-oob\s+ ([\d\.]+)/g;
2356 0 0         $ip1 = $1 if $$outref =~ /Portmgmt\s+ ([\d\.]+)/g;
2357 0 0         $ip1 = $1 if $$outref =~ /Port1\/1\s+ ([\d\.]+)/g;
2358 0 0         $ipv = $1 if $$outref =~ /MgmtVirtIp\s+ ([\d\.]+)/g;
2359 0 0         $ip2 = $1 if $$outref =~ /Port2\/1\s+ ([\d\.]+)/g;
2360 0 0         $ip2 = $1 if $$outref =~ /Portmgmt2\s+ ([\d\.]+)/g;
2361 0 0         if ($self->{$Package}{ATTRIB}{'cpu_slot'} == 1) { # Could be any VSP: 9k, 8k, 4k
2362 0           $self->_setAttrib('oob_ip', $ip1);
2363 0           $self->_setAttrib('oob_standby_ip', $ip2);
2364 0           $self->_setAttrib('oob_virt_ip', $ipv);
2365             }
2366             else { # cpu slot = 2 only on VSP9000
2367 0           $self->_setAttrib('oob_ip', $ip2);
2368 0           $self->_setAttrib('oob_standby_ip', $ip1);
2369 0           $self->_setAttrib('oob_virt_ip', $ipv);
2370             }
2371             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2372             ( (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ||
2373 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_virt_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_virt_ip'}) ) ?
2374             1 : 0 );
2375 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2376 0           return $self->poll_return(1);
2377             }
2378             else { # ERS based PassportERS
2379 0 0         if ($attrib->{stage} < 4) { # 4th stage
2380 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show sys info', 'show sys-info']);
2381 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2382             # No need to set Model, Sysname and BaseMAC as we only get here if Model is set
2383 0 0 0       if ($$outref =~ /CP.+ dormant / # 8600 & VSP9000
      0        
2384             || ($$outref =~ /\s1\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/ &&
2385             $$outref =~ /\s2\s+\d{4}\S{2}\s+1\s+CPU\s+(?:\d+\s+){4}/) # VSP8600 just check for presence of slot1&2
2386             ) {
2387 0           $self->_setAttrib('is_dual_cpu', 1);
2388             }
2389             else {
2390 0           $self->_setAttrib('is_dual_cpu', 0);
2391             }
2392 0 0         if ($$outref =~ /Virtual IP\s+: (.+)/g) {
2393 0           $self->_setAttrib('oob_virt_ip', $1);
2394             }
2395             else { # Not set
2396 0           $self->_setAttrib('oob_virt_ip', undef);
2397             }
2398 0           $attrib->{stage}++; # Move to next stage
2399 0           $attrib->{debugMsg} = 0;
2400             }
2401 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show bootconfig config', 'show boot config running-config']);
2402 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2403 0           my ($ip1, $ip2);
2404 0 0         $ip1 = $1 if $$outref =~ /^net mgmt ip ([\d\.]+)\/[\d\.]+ *(?:cpu-slot [35])?$/m;
2405 0 0         $ip2 = $1 if $$outref =~ /^net mgmt ip ([\d\.]+)\/[\d\.]+ cpu-slot 6$/m;
2406 0 0         if ($self->{$Package}{ATTRIB}{'cpu_slot'} < 5) {
    0          
2407 0           $self->_setAttrib('oob_ip', $ip1);
2408 0           $self->_setAttrib('oob_standby_ip', undef);
2409             }
2410             elsif ($self->{$Package}{ATTRIB}{'cpu_slot'} == 5) {
2411 0           $self->_setAttrib('oob_ip', $ip1);
2412 0 0         $self->_setAttrib('oob_standby_ip', $self->{$Package}{ATTRIB}{'is_dual_cpu'} ? $ip2 : undef);
2413             }
2414             else { # cpu slot = 6
2415 0           $self->_setAttrib('oob_ip', $ip2);
2416 0 0         $self->_setAttrib('oob_standby_ip', $self->{$Package}{ATTRIB}{'is_dual_cpu'} ? $ip1 : undef);
2417             }
2418             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2419             ( (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ||
2420 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_virt_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_virt_ip'}) ) ?
2421             1 : 0 );
2422 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2423 0           return $self->poll_return(1);
2424             }
2425             };
2426             }
2427             else { # On standby CPU
2428 0 0         ($attrib->{attribute} eq 'is_apls') && do { # APLS is never dual_cpu
2429 0           $self->_setAttrib('is_apls', 0);
2430 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2431 0           return $self->poll_return(1);
2432             };
2433 0 0         ($attrib->{attribute} eq 'is_fabric_engine') && do { # FabricEngine is never dual_cpu
2434 0           $self->_setAttrib('is_fabric_engine', 0);
2435 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2436 0           return $self->poll_return(1);
2437             };
2438 0 0         ($attrib->{attribute} eq 'is_voss') && do {
2439 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['cd /', 'cd /']);
2440 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2441 0 0         if ($$outref =~ /Only devices \/intflash/) {
2442 0           $self->_setAttrib('is_voss', 1);
2443             }
2444             else {
2445 0           $self->_setAttrib('is_voss', 0);
2446             }
2447 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2448 0           return $self->poll_return(1);
2449             };
2450             }
2451             }
2452             elsif ($familyType eq $Prm{bstk}) {
2453             ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'switch_mode' ||
2454             $attrib->{attribute} eq 'unit_number' || $attrib->{attribute} eq 'base_unit' || $attrib->{attribute} eq 'stack_size' ||
2455 0 0 0       $attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac') && do {
      0        
      0        
      0        
      0        
      0        
      0        
      0        
2456 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show sys-info']);
2457 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2458 0 0         if ($$outref =~ /Operation Mode:\s+(Switch)/g) {
    0          
2459 0           $self->_setAttrib('switch_mode', $1);
2460 0           $self->_setAttrib('unit_number', undef);
2461 0           $self->_setAttrib('stack_size', undef);
2462 0           $self->_setAttrib('base_unit', undef);
2463             }
2464             elsif ($$outref =~ /Operation Mode:\s+(Stack), Unit # (\d)/g) {
2465 0           $self->_setAttrib('switch_mode', $1);
2466 0           $self->_setAttrib('unit_number', $2);
2467 0           $$outref =~ /Size Of Stack: (\d)/gc; # Use /gc modifier to maintain position at every match
2468 0           $self->_setAttrib('stack_size', $1);
2469 0           $$outref =~ /Base Unit: (\d)/gc; # With /gc modifiers, fileds have to be matched in the right order
2470 0           $self->_setAttrib('base_unit', $1);
2471             }
2472 0 0         $$outref =~ /MAC Address:\s+(.+)/gc && $self->_setBaseMacAttrib($1);
2473 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)
2474             $self->_setModelAttrib($1);
2475 0 0         $$outref =~ /FW:([\d\.]+)\s+SW:v([\d\.]+)/gc && do {
2476 0           $self->_setAttrib('fw_version', $1);
2477 0           $self->_setAttrib('sw_version', $2);
2478             };
2479 0 0         $$outref =~ /sysName: +(\S.*)/gc && $self->_setAttrib('sysname', $1); # \S avoids match when field is blank
2480 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2481 0           return $self->poll_return(1);
2482             };
2483 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2484 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show interfaces']);
2485 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2486 0           $self->_setSlotPortAttrib($outref);
2487 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2488 0           return $self->poll_return(1);
2489             };
2490 0 0         $attrib->{attribute} eq 'stp_mode' && do {
2491 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show spanning-tree mode']);
2492 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2493 0 0         if ($$outref =~ /Current STP Operation Mode: (STPG|MSTP|RSTP)/) {
2494 0           $self->_setAttrib('stp_mode', lc($1));
2495             }
2496             else { # Older stackables will not know the command and only support stpg
2497 0           $self->_setAttrib('stp_mode', 'stpg');
2498             }
2499 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2500 0           return $self->poll_return(1);
2501             };
2502 0 0         $attrib->{attribute} eq 'mgmt_vlan' && do {
2503 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show vlan mgmt']);
2504 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2505 0 0         $$outref =~ /Management VLAN: (\d+)/ && $self->_setAttrib('mgmt_vlan', $1);
2506 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2507 0           return $self->poll_return(1);
2508             };
2509 0 0 0       ($attrib->{attribute} eq 'mgmt_ip' || $attrib->{attribute} eq 'oob_ip' || $attrib->{attribute} eq 'is_oob_connected') && do {
      0        
2510 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show ip']);
2511 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2512 0 0         $$outref =~ /(?:Switch|Stack) IP Address:\s+[\d\.]+\s+([\d\.]+)\s+[\d\.]+/g && $self->_setAttrib('mgmt_ip', $1);
2513 0 0         if ($$outref =~ /Mgmt (?:Switch|Stack) IP Address:\s+[\d\.]+\s+([\d\.]+)\s/g) {
2514 0           $self->_setAttrib('oob_ip', $1);
2515             }
2516             else { # No OOB port on this device
2517 0           $self->_setAttrib('oob_ip', undef);
2518             }
2519             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2520 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ?
2521             1 : 0 );
2522 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2523 0           return $self->poll_return(1);
2524             };
2525 0 0         $attrib->{attribute} eq 'baudrate' && do {
2526 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show terminal']); # Don't need to be in privExec for this
2527 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2528 0 0         if ($$outref =~ /Terminal speed: (\d+)/) {
2529 0           $self->_setAttrib('baudrate', $1);
2530             }
2531 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2532 0           return $self->poll_return(1);
2533             };
2534 0 0         $attrib->{attribute} eq 'max_baud' && do {
2535 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ["terminal speed ?$CTRL_C"]); # Don't need to be in privExec for this
2536 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2537 0           my $baudRate;
2538 0           while ($$outref =~ /^ (\d+)\s*$/mg) {
2539 0 0 0       $baudRate = $1 if !defined $baudRate || $1 > $baudRate;
2540             }
2541 0           $self->_setAttrib('max_baud', $baudRate);
2542 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2543 0           return $self->poll_return(1);
2544             };
2545             }
2546             elsif ($familyType eq $Prm{sr}) {
2547 0 0         $attrib->{attribute} eq 'model' && do {
2548 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show chassis']);
2549 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2550 0 0         $$outref =~ /Chassis Model: (.+)/ && $self->_setModelAttrib($1);
2551 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2552 0           return $self->poll_return(1);
2553             };
2554 0 0 0       ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version') && do {
2555 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show version']);
2556 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2557 0 0         $$outref =~ /Runtime: (.+)/g && $self->_setAttrib('sw_version', $1);
2558 0 0         $$outref =~ /Boot: (.+?) / && $self->_setAttrib('fw_version', $1);
2559 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2560 0           return $self->poll_return(1);
2561             };
2562 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2563 0 0         if ($attrib->{stage} < 4) { # 4th stage
2564 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show interface ethernets']);
2565 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2566 0           $self->_setSlotPortAttrib($outref);
2567 0           $attrib->{stage}++; # Move to next stage
2568 0           $attrib->{debugMsg} = 0;
2569             }
2570 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show module configuration all']);
2571 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2572 0           $self->_setSlotPortAttrib($outref);
2573 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2574 0           return $self->poll_return(1);
2575             };
2576 0 0         $attrib->{attribute} eq 'sysname' && do {
2577 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show hostname']);
2578 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2579 0 0         $$outref =~ /HostName: (.+)/g && $self->_setAttrib('sysname', $1);
2580 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2581 0           return $self->poll_return(1);
2582             };
2583 0 0         $attrib->{attribute} eq 'base_mac' && do {
2584 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show system configuration'], 1);
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 0         $$outref =~ /Mac Address\s+0x(.+)/g && $self->_setBaseMacAttrib($1);
2587 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2588 0           return $self->poll_return(1);
2589             };
2590             }
2591             elsif ($familyType eq $Prm{trpz}) {
2592 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac') && do {
      0        
2593 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show system']);
2594 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2595 0 0         $$outref =~ /Product Name:\s+(.+)/g && $self->_setModelAttrib($1);
2596 0 0         $$outref =~ /System Name:\s+(.+)/g && $self->_setAttrib('sysname', $1);
2597 0 0         $$outref =~ /System MAC:\s+(.+)/g && $self->_setBaseMacAttrib($1);
2598 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2599 0           return $self->poll_return(1);
2600             };
2601 0 0 0       ($attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version') && do {
2602 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show version']);
2603 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2604 0 0         $$outref =~ /Version: (.+?) REL/g && $self->_setAttrib('sw_version', $1);
2605 0 0         $$outref =~ /BootLoader:\s+(.+)/ && $self->_setAttrib('fw_version', $1);
2606 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2607 0           return $self->poll_return(1);
2608             };
2609 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2610 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show port status']);
2611 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2612 0           $self->_setSlotPortAttrib($outref);
2613 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2614 0           return $self->poll_return(1);
2615             };
2616             }
2617             elsif ($familyType eq $Prm{xlr}) {
2618 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version')&& do {
      0        
2619 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show config'], 1);
2620 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2621 0 0         $$outref =~ /# box type\s+: (.+)/g && $self->_setModelAttrib($1);
2622 0 0         $$outref =~ /# boot monitor version\s+: v?(.+)/g && $self->_setAttrib('fw_version', $1);
2623 0 0         $$outref =~ /# software version\s+: v?(.+)/g && $self->_setAttrib('sw_version', $1);
2624 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2625 0           return $self->poll_return(1);
2626             };
2627 0 0 0       ($attrib->{attribute} eq 'is_dual_cpu' || $attrib->{attribute} eq 'sysname') && do {
2628 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show sys info'], 3);
2629 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2630 0 0         $$outref =~ /SysDescr\s+: (.+?) \(/g && $self->_setModelAttrib($1);
2631 0 0         $$outref =~ /SysName\s+: (.+)/g && $self->_setAttrib('sysname', $1);
2632 0 0         if ($$outref =~ /CPU.+ dormant /) {
2633 0           $self->_setAttrib('is_dual_cpu', 1);
2634             }
2635             else {
2636 0           $self->_setAttrib('is_dual_cpu', 0);
2637             }
2638 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2639 0           return $self->poll_return(1);
2640             };
2641 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2642 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show ports info arp']);
2643 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2644 0           $self->_setSlotPortAttrib($outref);
2645 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2646 0           return $self->poll_return(1);
2647             };
2648             }
2649             elsif ($familyType eq $Prm{xirrus}) {
2650             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'base_mac' ||
2651 0 0 0       $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sw_version')&& do {
      0        
      0        
2652 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show system-info']);
2653 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2654 0 0         $$outref =~ /Model: (.+?),/g && $self->_setModelAttrib($1);
2655 0 0         $$outref =~ /IAPs\s+(.+?)-/g && $self->_setBaseMacAttrib($1);
2656 0 0         $$outref =~ /Boot Loader\s+(.+?) \(.+?\), Build: (.+)/g && $self->_setAttrib('fw_version', "$1-$2");
2657 0 0         $$outref =~ /System Software\s+(.+?) \(.+?\), Build: (.+)/g && $self->_setAttrib('sw_version', "$1-$2");
2658 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2659 0           return $self->poll_return(1);
2660             };
2661 0 0         ($attrib->{attribute} eq 'sysname') && do {
2662 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show contact-info']);
2663 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2664 0 0         $$outref =~ /Access Point Hostname\s*(.+)/g && $self->_setAttrib('sysname', $1);
2665 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2666 0           return $self->poll_return(1);
2667             };
2668 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2669 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show ethernet']);
2670 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2671 0           $self->_setSlotPortAttrib($outref);
2672 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2673 0           return $self->poll_return(1);
2674             };
2675             }
2676             elsif ($familyType eq $Prm{xos}) {
2677 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac')&& do {
      0        
2678 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show switch'], 1);
2679 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2680 0 0         $$outref =~ /SysName: (.+)/g && $self->_setAttrib('sysname', $1);
2681 0 0         $$outref =~ /System MAC: (.+)/g && $self->_setBaseMacAttrib($1);
2682 0 0         $$outref =~ /System Type: (?:VPEX )?(\S+)( \(Stack\))?/g && $self->_setModelAttrib($1);
2683 0 0         $self->_setAttrib('switch_mode', 'Stack') if defined $2;
2684 0 0         $$outref =~ /Image Booted: (primary|secondary)/ && do {
2685 0 0         if ($1 eq 'primary') {
2686 0 0         $$outref =~ /Primary ver: (\S+)/g && $self->_setAttrib('sw_version', $1);
2687             }
2688             else { # secondary
2689 0 0         $$outref =~ /Secondary ver: (\S+)/g && $self->_setAttrib('sw_version', $1);
2690             }
2691             };
2692 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2693 0           return $self->poll_return(1);
2694             };
2695 0 0 0       ($attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'fw_version') && do {
2696 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show version']);
2697 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2698 0 0         $$outref =~ /Image : Extreme(?:XOS| Networks Switch Engine) version (.+) by /g && $self->_setAttrib('sw_version', $1);
2699 0 0         $$outref =~ /BootROM : (?:Default )?(\S+)/g && $self->_setAttrib('fw_version', $1);
2700 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2701 0           return $self->poll_return(1);
2702             };
2703 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2704 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show port debounce']);
2705 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2706 0           $self->_setSlotPortAttrib($outref);
2707 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2708 0           return $self->poll_return(1);
2709             };
2710             ($attrib->{attribute} eq 'switch_mode' || $attrib->{attribute} eq 'stack_size' ||
2711 0 0 0       $attrib->{attribute} eq 'unit_number' || $attrib->{attribute} eq 'master_unit') && do {
      0        
      0        
2712 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show stacking']);
2713 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2714 0 0         if ($$outref =~ /(?:This node is not in an Active Topology|stacking-support:\s+\w+\s+Disabled|\*[\d:a-f]+ - Disabled)/) {
2715 0           $self->_setAttrib('switch_mode', 'Switch');
2716 0           $self->_setAttrib('unit_number', undef);
2717 0           $self->_setAttrib('stack_size', undef);
2718 0           $self->_setAttrib('master_unit', undef);
2719             }
2720             else {
2721 0           $self->_setAttrib('switch_mode', 'Stack');
2722 0           my $unitCount = 0;
2723 0           while ($$outref =~ /([\* ])(?:[\da-f]{2}:){5}[\da-f]{2} (\d) \w+\s+(Master|\w+)/g) {
2724 0           $unitCount++;
2725 0 0         $self->_setAttrib('unit_number', $2) if $1 eq '*';
2726 0 0         $self->_setAttrib('master_unit', $2) if $3 eq 'Master';
2727             }
2728 0           $self->_setAttrib('stack_size', $unitCount);
2729             }
2730 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2731 0           return $self->poll_return(1);
2732             };
2733 0 0         ($attrib->{attribute} eq 'stp_mode') && do {
2734 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show stpd s0 | include "Operational Mode"']);
2735 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2736 0 0         if ($$outref =~ /Operational Mode: (802.1D|802.1W|MSTP)/) {
2737 0 0         $self->_setAttrib('stp_mode', 'stpg') if $1 eq '802.1D';
2738 0 0         $self->_setAttrib('stp_mode', 'rstp') if $1 eq '802.1W';
2739 0 0         $self->_setAttrib('stp_mode', 'mstp') if $1 eq 'MSTP';
2740             }
2741             else { # Instance s0 does not exit ?
2742 0           $self->_setAttrib('stp_mode', undef);
2743             }
2744 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2745 0           return $self->poll_return(1);
2746             };
2747 0 0 0       ($attrib->{attribute} eq 'oob_ip' || $attrib->{attribute} eq 'is_oob_connected') && do {
2748 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show vlan mgmt | include "Primary IP"']);
2749 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2750 0 0         if ($$outref =~ /Primary IP: ([\d\.]+)\/\d+/g) {
2751 0           $self->_setAttrib('oob_ip', $1);
2752             }
2753             else { # No OOB port on this device
2754 0           $self->_setAttrib('oob_ip', undef);
2755             }
2756             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2757 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ?
2758             1 : 0 );
2759 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2760 0           return $self->poll_return(1);
2761             };
2762             }
2763             elsif ($familyType eq $Prm{isw}) {
2764             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac' ||
2765 0 0 0       $attrib->{attribute} eq 'sw_version')&& do {
      0        
      0        
2766 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['do show version']);
2767 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2768 0 0         $$outref =~ /MAC Address : (.+)/g && $self->_setBaseMacAttrib($1);
2769 0 0         $$outref =~ /System Name : (.+)/g && $self->_setAttrib('sysname', $1);
2770 0 0         $$outref =~ /Product : (.+)/g && $self->_setModelAttrib($1); # old ISW models
2771 0 0         $$outref =~ /Board Type : (.+)/g && $self->_setModelAttrib($1); # New ISW models
2772 0 0         $$outref =~ /Software Version : V?(.+)/g && $self->_setAttrib('sw_version', $1);
2773 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2774 0           return $self->poll_return(1);
2775             };
2776 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2777 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['do show interface * veriphy']);
2778 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2779 0           $self->_setSlotPortHashAttrib($outref);
2780 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2781 0           return $self->poll_return(1);
2782             };
2783             }
2784             elsif ($familyType eq $Prm{s200}) {
2785             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'base_mac' || $attrib->{attribute} eq 'sw_version' ||
2786 0 0 0       $attrib->{attribute} eq 'fw_version') && do {
      0        
      0        
2787 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show version']);
2788 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2789 0 0         $$outref =~ /Machine Model\.+ (.+)/g && $self->_setModelAttrib($1);
2790 0 0         $$outref =~ /Burned In MAC Address\.+ (.+)/g && $self->_setBaseMacAttrib($1);
2791 0 0         $$outref =~ /Software Version\.+ (.+)/g && $self->_setAttrib('sw_version', $1);
2792 0 0         $$outref =~ /Operating System\.+ Linux (.+)/g && $self->_setAttrib('fw_version', $1);
2793 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2794 0           return $self->poll_return(1);
2795             };
2796 0 0         ($attrib->{attribute} eq 'sysname') && do {
2797 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show sysinfo'], 1);
2798 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2799 0           my ($defSsysname, $setSsysname);
2800 0 0         $$outref =~ /System Description\.+ (.+?)-/g && do {$defSsysname = $1}; # Will use this, if no sysname set on device
  0            
2801 0 0         $$outref =~ /System Name\.+ (.+)/g && do {$setSsysname = $1};
  0            
2802 0 0         if ($setSsysname) {
    0          
2803 0           $self->_setAttrib('sysname', $setSsysname);
2804             }
2805             elsif ($defSsysname) {
2806 0           $self->_setAttrib('sysname', $defSsysname);
2807             }
2808 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2809 0           return $self->poll_return(1);
2810             };
2811 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2812 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show interfaces switchport general']);
2813 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2814 0           $self->_setSlotPortAttrib($outref);
2815 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2816 0           return $self->poll_return(1);
2817             };
2818             ($attrib->{attribute} eq 'switch_mode' || $attrib->{attribute} eq 'stack_size' ||
2819 0 0 0       $attrib->{attribute} eq 'unit_number' || $attrib->{attribute} eq 'manager_unit') && do {
      0        
      0        
2820 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show switch']);
2821 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2822 0           my $unitCount = 0;
2823 0           while ($$outref =~ /(\d) (Mgmt Sw|Stack Mbr)\s/g) {
2824 0           $unitCount++;
2825 0 0         if ($2 eq 'Mgmt Sw') {
2826 0           $self->_setAttrib('unit_number', $1);
2827 0           $self->_setAttrib('manager_unit', $1);
2828             }
2829             }
2830 0 0         if ($unitCount) {
2831 0           $self->_setAttrib('switch_mode', 'Stack');
2832 0           $self->_setAttrib('stack_size', $unitCount);
2833             }
2834             else {
2835 0           $self->_setAttrib('switch_mode', 'Switch');
2836 0           $self->_setAttrib('unit_number', undef);
2837 0           $self->_setAttrib('stack_size', undef);
2838 0           $self->_setAttrib('manager_unit', undef);
2839             }
2840 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2841 0           return $self->poll_return(1);
2842             };
2843 0 0         ($attrib->{attribute} eq 'stp_mode') && do {
2844 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show spanning-tree active']);
2845 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2846 0 0         if ($$outref =~ /Mode: (stp|rstp|mstp)/) {
    0          
2847 0 0         $self->_setAttrib('stp_mode', $1 eq 'stp' ? 'stpg' : $1);
2848             }
2849             elsif ($$outref =~ /Spanning-tree enabled protocol (pvst|rpvst)/) {
2850 0           $self->_setAttrib('stp_mode', $1);
2851             }
2852 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2853 0           return $self->poll_return(1);
2854             };
2855 0 0 0       ($attrib->{attribute} eq 'oob_ip' || $attrib->{attribute} eq 'is_oob_connected') && do {
2856 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show serviceport']);
2857 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2858 0 0         if ($$outref =~ /IP Address\.+ (.+)/g) {
2859 0           $self->_setAttrib('oob_ip', $1);
2860             }
2861             else { # No OOB port on this device
2862 0           $self->_setAttrib('oob_ip', undef);
2863             }
2864             $self->_setAttrib('is_oob_connected', defined $self->socket &&
2865 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ?
2866             1 : 0 );
2867 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2868 0           return $self->poll_return(1);
2869             };
2870 0 0         $attrib->{attribute} eq 'baudrate' && do {
2871 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show serial']); # Don't need to be in privExec for this
2872 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2873 0 0         if ($$outref =~ /Baud Rate \(bps\)\.+ (\d+)/) {
2874 0           $self->_setAttrib('baudrate', $1);
2875             }
2876 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2877 0           return $self->poll_return(1);
2878             };
2879 0 0         $attrib->{attribute} eq 'max_baud' && do {
2880 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, "line console\nserial baudrate ?$CTRL_U"], undef, 1);
2881 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2882 0           my $baudRate;
2883 0           while ($$outref =~ /^(\d+) Set serial speed to \d+\.$/mg) {
2884 0 0 0       $baudRate = $1 if !defined $baudRate || $1 > $baudRate;
2885             }
2886 0           $self->_setAttrib('max_baud', $baudRate);
2887 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2888 0           return $self->poll_return(1);
2889             };
2890             }
2891             elsif ($familyType eq $Prm{wing}) {
2892             ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'base_mac' || $attrib->{attribute} eq 'sw_version' ||
2893 0 0 0       $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} eq 'sysname') && do {
      0        
      0        
      0        
2894 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show version']);
2895 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2896 0 0         $$outref =~ /(\S+) version (.+)/g && do {
2897 0           $self->_setModelAttrib($1);
2898 0           $self->_setAttrib('sw_version', $2);
2899 0           $self->_setAttrib('fw_version', undef);
2900             };
2901 0 0         $$outref =~ /(\S+) uptime is/g && $self->_setAttrib('sysname', $1);
2902 0 0         $$outref =~ /Base ethernet MAC address is (.+)/g && $self->_setBaseMacAttrib($1);
2903 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2904 0           return $self->poll_return(1);
2905             };
2906 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2907 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [undef, 'show interface switchport']);
2908 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2909 0           $self->_setSlotPortAttrib($outref);
2910 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2911 0           return $self->poll_return(1);
2912             };
2913 0 0 0       ($attrib->{attribute} eq 'baudrate' || $attrib->{attribute} eq 'max_baud') && do {
2914 0           $self->_setAttrib('baudrate', 115200);
2915 0           $self->_setAttrib('max_baud', undef);
2916 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2917 0           return $self->poll_return(1);
2918             };
2919             }
2920             elsif ($familyType eq $Prm{slx}) {
2921 0 0 0       ($attrib->{attribute} eq 'sysname' || $attrib->{attribute} eq 'base_mac') && do {
2922             # This command is remarkably slow on SLX, so only use it if we have to
2923 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show system'], 1);
2924 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2925 0 0         $$outref =~ /Stack MAC : (.+)/g && $self->_setBaseMacAttrib($1);
2926 0 0         $$outref =~ /Unit Name : (.+)/g && $self->_setAttrib('sysname', $1);
2927 0 0         $$outref =~ /SLX-OS Version : (\d+([rsx])?.+)/g && do {
2928 0           $self->_setAttrib('sw_version', $1);
2929 0           for my $rsx ('r', 's', 'x') {
2930 0 0 0       $self->_setAttrib("is_slx_$rsx", !defined $2 || $2 eq $rsx ? 1 : undef) if $rsx eq 'r';
    0          
2931 0 0 0       $self->_setAttrib("is_slx_$rsx", defined $2 && $2 eq $rsx ? 1 : undef) unless $rsx eq 'r';
    0          
2932             }
2933             };
2934 0 0         if ($self->{$Package}{ATTRIBFLAG}{'is_dual_mm'}) {
2935             $$outref =~ /Management IP : (.+)/g &&
2936 0 0         $self->_setAttrib($self->{$Package}{ATTRIB}{'is_dual_mm'} ? 'oob_virt_ip' : 'oob_ip', $1);
    0          
2937             }
2938 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2939 0           return $self->poll_return(1);
2940             };
2941 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'switch_type' || $attrib->{attribute} eq 'baudrate') && do {
      0        
2942 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show chassis | include "Chassis Name:|switchType:"']);
2943 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2944 0 0         $$outref =~ /Chassis Name:(?:\t|\e\[\d\w)(?:(?:BR|EN)-)?(.+)/g && $self->_setModelAttrib($1); # On serial port SLX uses \e[3C instead of tab char
2945 0 0 0       $self->_setAttrib('baudrate', defined $self->{$Package}{ATTRIB}{'model'} && $self->{$Package}{ATTRIB}{'model'} =~ /9030/ ? 115200 : undef);
2946 0 0         $$outref =~ /switchType: (\d+)/g && $self->_setAttrib('switch_type', $1);
2947 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2948 0           return $self->poll_return(1);
2949             };
2950 0 0 0       ($attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'fw_version' || $attrib->{attribute} =~ /^is_slx_[rsx]$/) && do {
      0        
2951 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show version']);
2952 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2953 0 0         $$outref =~ /Firmware name: (\d+([rsx])?.+)/g && do {
2954 0           $self->_setAttrib('sw_version', $1);
2955 0           for my $rsx ('r', 's', 'x') {
2956 0 0 0       $self->_setAttrib("is_slx_$rsx", !defined $2 || $2 eq $rsx ? 1 : undef) if $rsx eq 'r';
    0          
2957 0 0 0       $self->_setAttrib("is_slx_$rsx", defined $2 && $2 eq $rsx ? 1 : undef) unless $rsx eq 'r';
    0          
2958             }
2959             };
2960 0 0         $$outref =~ /Kernel: (.+)/g && $self->_setAttrib('fw_version', $1);
2961 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2962 0           return $self->poll_return(1);
2963             };
2964 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
2965 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show interface description']);
2966 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2967 0           $self->_setSlotPortAttrib($outref);
2968 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2969 0           return $self->poll_return(1);
2970             };
2971             ($attrib->{attribute} eq 'is_ha' || $attrib->{attribute} eq 'mm_number' || $attrib->{attribute} eq 'is_dual_mm' ||
2972             $attrib->{attribute} eq 'is_active_mm' ||
2973             (!$self->{$Package}{ATTRIBFLAG}{'mm_number'} && $attrib->{attribute} =~ /oob/) # We need 'mm_number' attrib for oob ones below
2974 0 0 0       ) && do {
      0        
      0        
      0        
      0        
2975 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show ha']);
2976 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2977 0           my ($m1, $m2);
2978 0 0         $$outref =~ /M1: (Active|Standby)/g && do {$m1 = $1};
  0            
2979 0 0         $$outref =~ /M2: (Active|Standby)/g && do {$m2 = $1};
  0            
2980 0 0 0       if ($m1 && $m2) {
2981 0           $self->_setAttrib('is_ha', 1);
2982 0 0         $self->_setAttrib('mm_number', $m1 ? 1 : 2);
2983 0           $self->_setAttrib('is_dual_mm', 1);
2984 0           $self->_setAttrib('is_active_mm', 1);
2985             }
2986             else {
2987 0 0 0       $self->_setAttrib('is_ha', $m1 || $m2 ? 0 : undef);
2988 0 0         $self->_setAttrib('mm_number', $m2 ? 2 : $m1 ? 1 : 0);
    0          
2989 0           $self->_setAttrib('is_dual_mm', 0);
2990 0           $self->_setAttrib('is_active_mm', 1);
2991             }
2992 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
2993 0           return $self->poll_return(1);
2994             };
2995 0 0         ($attrib->{attribute} eq 'stp_mode') && do {
2996 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show spanning-tree brief | include "Spanning-tree Mode:"']);
2997 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
2998 0 0         if ($$outref =~ /Spanning-tree Mode: (.+)/g) {
2999 0 0         $self->_setAttrib('stp_mode', 'mstp') if $1 == 'Multiple Spanning Tree Protocol';
3000 0 0         $self->_setAttrib('stp_mode', 'rstp') if $1 == 'Rapid Spanning Tree Protocol';
3001 0 0         $self->_setAttrib('stp_mode', 'stpg') if $1 == 'Spanning Tree Protocol';
3002 0 0         $self->_setAttrib('stp_mode', 'pvst') if $1 == 'Per-VLAN Spanning Tree Protocol';
3003 0 0         $self->_setAttrib('stp_mode', 'rpvst') if $1 == 'Rapid Per-VLAN Spanning Tree Protocol';
3004             }
3005             else {
3006 0           $self->_setAttrib('stp_mode', undef);
3007             }
3008 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3009 0           return $self->poll_return(1);
3010             };
3011 0 0 0       ($attrib->{attribute} eq 'oob_ip' || $attrib->{attribute} eq 'oob_standby_ip' || $attrib->{attribute} eq 'is_oob_connected') && do {
      0        
3012 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show interface Management']);
3013 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3014 0           for my $i (1..2) { # Do this twice
3015 0 0         $$outref =~ /interface Management (\d)/g && do {
3016 0           my $mslot = $1;
3017             $$outref =~ /ip address \"static (.+)\//g &&
3018 0 0         $self->_setAttrib($mslot == $self->{$Package}{ATTRIB}{'mm_number'} ? 'oob_ip' : 'oob_standby_ip', $1);
    0          
3019             };
3020             }
3021             $self->_setAttrib('is_oob_connected', defined $self->socket &&
3022             ( (defined $self->{$Package}{ATTRIB}{'oob_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_ip'}) ||
3023 0 0 0       (defined $self->{$Package}{ATTRIB}{'oob_virt_ip'} && $self->socket->peerhost eq $self->{$Package}{ATTRIB}{'oob_virt_ip'}) ) ?
3024             1 : 0 );
3025 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3026 0           return $self->poll_return(1);
3027             };
3028 0 0         $attrib->{attribute} eq 'oob_virt_ip' && do {
3029 0 0         my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, [($self->config_context ? 'do ':'') . 'show chassis virtual-ip']);
3030 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3031 0 0         $$outref =~ /chassis virtual-ip \"static (.+)\//g && $self->_setAttrib('oob_virt_ip', $1);
3032 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3033 0           return $self->poll_return(1);
3034             };
3035             }
3036             elsif ($familyType eq $Prm{hive}) {
3037 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'fw_version') && do {
      0        
3038 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show version']);
3039 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3040 0 0         $$outref =~ /Version:\s+HiveOS (\S+) build-\d+/g && $self->_setAttrib('sw_version', $1);
3041 0 0         $$outref =~ /Platform:\s+(\S+)/g && $self->_setModelAttrib($1);
3042 0 0         $$outref =~ /Bootloader ver:\s+v(\S+)/g && $self->_setAttrib('fw_version', $1);
3043 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3044 0           return $self->poll_return(1);
3045             };
3046 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports') && do {
3047 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show interface']);
3048 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3049 0           $self->_setSlotPortAttrib($outref);
3050 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3051 0           return $self->poll_return(1);
3052             };
3053 0 0         ($attrib->{attribute} eq 'sysname') && do {
3054 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show running-config | include hostname']);
3055 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3056 0 0         $$outref =~ /hostname (\S+)/g && $self->_setAttrib('sysname', $1);
3057 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3058 0           return $self->poll_return(1);
3059             };
3060 0 0         ($attrib->{attribute} eq 'base_mac') && do {
3061 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['show hw-info']);
3062 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3063 0 0         $$outref =~ /Ethernet MAC address:\s(.+)/g && $self->_setBaseMacAttrib($1);
3064 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3065 0           return $self->poll_return(1);
3066             };
3067 0 0 0       ($attrib->{attribute} eq 'baudrate' || $attrib->{attribute} eq 'max_baud') && do {
3068 0           $self->_setAttrib('baudrate', 9600);
3069 0           $self->_setAttrib('max_baud', undef);
3070 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3071 0           return $self->poll_return(1);
3072             };
3073             }
3074             elsif ($familyType eq $Prm{ipanema}) {
3075 0 0 0       ($attrib->{attribute} eq 'model' || $attrib->{attribute} eq 'sw_version' || $attrib->{attribute} eq 'fw_version') && do {
      0        
3076 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['version']);
3077 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3078 0 0         $$outref =~ /Name : (\S+)/g && $self->_setModelAttrib($1);
3079 0 0         $$outref =~ / Kernel : (\S+)/g && $self->_setAttrib('fw_version', $1);
3080 0 0         $$outref =~ / Ipe : (\S+)/g && $self->_setAttrib('sw_version', $1);
3081 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3082 0           return $self->poll_return(1);
3083             };
3084 0 0 0       ($attrib->{attribute} eq 'slots' || $attrib->{attribute} eq 'ports' || $attrib->{attribute} eq 'base_mac') && do {
      0        
3085 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['ifconfig']);
3086 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3087 0           my ($outList, $basemac);
3088 0           while ($$outref =~ /^(\w+): flags/mgc) { # Only recover interfaces with a non-zero MAC address
3089 0           my $interface = $1;
3090 0 0 0       $outList .= "$interface\n" if $$outref =~ /ether (\S+)/gc and $1 ne '00:00:00:00:00:00';
3091 0 0         $basemac = $1 if $interface eq 'isw0';
3092             }
3093 0           $self->_setBaseMacAttrib($basemac);
3094 0           $self->_setSlotPortAttrib(\$outList);
3095 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3096 0           return $self->poll_return(1);
3097             };
3098 0 0         ($attrib->{attribute} eq 'sysname') && do {
3099 0           my ($ok, $outref) = $self->_attribExecuteCmd($pkgsub, $attrib, ['ipconfig -d']);
3100 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3101 0 0         $$outref =~ /\s+Hostname\s+: (\S+)/g && $self->_setAttrib('sysname', $1);
3102 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3103 0           return $self->poll_return(1);
3104             };
3105 0 0 0       ($attrib->{attribute} eq 'baudrate' || $attrib->{attribute} eq 'max_baud') && do {
3106 0           $self->_setAttrib('baudrate', 9600);
3107 0           $self->_setAttrib('max_baud', undef);
3108 0           $self->{POLL}{output_result} = $self->{$Package}{ATTRIB}{$attrib->{attribute}};
3109 0           return $self->poll_return(1);
3110             };
3111             }
3112              
3113 0           return $self->poll_return(1); # Undefined output_result for unrecognized attributes
3114             }
3115              
3116              
3117             sub poll_change_baudrate { # Method to handle change_baudrate for poll methods (used for both blocking & non-blocking modes)
3118 0     0 1   my $self = shift;
3119 0           my $pkgsub = shift;
3120 0           my $pollsub = "${Package}::change_baudrate";
3121              
3122 0 0         unless ($self->{POLLING}) { # Sanity check
3123 0           my (undef, $fileName, $lineNumber) = caller;
3124 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
3125             }
3126              
3127 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
3128 0           my @validArgs = ('baudrate', 'timeout', 'errmode', 'forcebaud');
3129 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
3130 0 0 0       if (@_ && !%args) { # Legacy syntax
3131 0           ($args{baudrate}, $args{timeout}, $args{errmode}) = @_;
3132             }
3133             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
3134             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
3135             # Set method argument keys
3136             baudrate => $args{baudrate},
3137             parity => undef,
3138             databits => undef,
3139             stopbits => undef,
3140             handshake => undef,
3141             forcebaud => $args{forcebaud},
3142             local_side_only => 0, # For that functionality, just call Control::CLI's poll_change_baudrate
3143             # Declare method storage keys which will be used
3144             stage => 0,
3145             userExec => undef,
3146             privExec => undef,
3147             maxMode => $args{baudrate} eq 'max' ? 1:0,
3148             # Declare keys to be set if method called from another polled method
3149             errmode => $args{errmode},
3150 0 0         };
3151             # Cache poll structure keys which this method will use
3152 0           $self->poll_struct_cache($pollsub, $args{timeout});
3153             }
3154 0           my $changeBaud = $self->{POLL}{$pollsub};
3155 0 0         local $self->{errmode} = $changeBaud->{errmode} if defined $changeBaud->{errmode};
3156 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3157              
3158 0 0         if ($changeBaud->{local_side_only}) { # Same functionality as Control::CLI::change_baudrate()
3159             my $ok = $self->SUPER::poll_change_baudrate($pkgsub,
3160             BaudRate => $changeBaud->{baudrate},
3161             Parity => $changeBaud->{parity},
3162             DataBits => $changeBaud->{databits},
3163             StopBits => $changeBaud->{stopbits},
3164             Handshake => $changeBaud->{handshake},
3165             ForceBaud => $changeBaud->{forcebaud},
3166 0           );
3167 0           return $self->poll_return($ok); # Come out if error (if errmode='return'), or if nothing to read in non-blocking mode, or completed
3168             }
3169              
3170 0 0         if ($changeBaud->{stage} < 1) { # 1st stage
3171 0 0         unless ($self->connection_type eq 'SERIAL') {
3172 0           return $self->poll_return($self->error("$pkgsub: Cannot change baudrate on Telnet/SSH"));
3173             }
3174 0 0         unless (defined $self->baudrate) { # If no active connection come out
3175 0           return $self->poll_return($self->error("$pkgsub: No serial connection established yet"));
3176             }
3177 0 0         unless (defined $changeBaud->{baudrate}) {
3178 0           return $self->poll_return($self->error("$pkgsub: No baudrate specified!"));
3179             }
3180 0 0         unless ($familyType) {
3181 0           return $self->poll_return($self->error("$pkgsub: Family type of remote device is not detected"));
3182             }
3183 0           $changeBaud->{stage}++; # Move to 2nd stage
3184             }
3185              
3186 0 0         if ($changeBaud->{stage} < 2) { # 2nd stage
3187 0 0         unless (defined $self->{$Package}{ATTRIB}{'baudrate'}) { # Make sure this attribute is set
3188 0           my $ok = $self->poll_attribute($pkgsub, 'baudrate');
3189 0 0         return $self->poll_return($ok) unless $ok;
3190             }
3191 0 0         unless (defined $self->{$Package}{ATTRIB}{'baudrate'}) {
3192 0 0         return $self->poll_return($self->error("$pkgsub: Baudrate cannot be changed on device")) unless $changeBaud->{maxMode};
3193 0           $self->debugMsg(4,"ChangeBaudrate: baudrate attrib undefined - maxMode return success\n");
3194 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
3195 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
3196             }
3197 0 0         unless (defined $self->{$Package}{ATTRIB}{'max_baud'}) { # Make sure this attribute is set
3198 0           my $ok = $self->poll_attribute($pkgsub, 'max_baud');
3199 0 0         return $self->poll_return($ok) unless $ok;
3200             }
3201 0 0 0       if ($changeBaud->{maxMode} && !defined $self->{$Package}{ATTRIB}{'max_baud'}) {
3202 0           $self->debugMsg(4,"ChangeBaudrate: max_baud attrib undefined - maxMode return success\n");
3203 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
3204 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
3205             }
3206 0 0         $changeBaud->{baudrate} = $self->{$Package}{ATTRIB}{'max_baud'} if $changeBaud->{maxMode};
3207              
3208 0 0         if ($changeBaud->{baudrate} == $self->baudrate) { # Desired baudrate is already set
3209 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
3210 0           return $self->poll_return(1);
3211             }
3212              
3213             # Now, depending on family type of connected device, ensure we change the baud rate on the device first
3214 0 0         if ($familyType eq $Prm{generic}) {
    0          
    0          
    0          
3215 0           return $self->poll_return($self->error("$pkgsub: Unable to complete on $Prm{generic} family_type device"));
3216             }
3217             elsif ($familyType eq $Prm{bstk}) {
3218 0 0 0       unless ($changeBaud->{baudrate} == 9600 || $changeBaud->{baudrate} == 19200 || $changeBaud->{baudrate} == 38400) {
      0        
3219 0           return $self->poll_return($self->error("$pkgsub: Supported baud rates for $Prm{bstk} = 9600, 19200, 38400"));
3220             }
3221             }
3222             elsif ($familyType eq $Prm{pers}) {
3223 0 0 0       unless ($changeBaud->{baudrate} == 9600 || $changeBaud->{baudrate} == 19200 || $changeBaud->{baudrate} == 38400 ||
      0        
      0        
      0        
3224             $changeBaud->{baudrate} == 57600 || $changeBaud->{baudrate} == 115200) {
3225 0           return $self->poll_return($self->error("$pkgsub: Supported baud rates for $Prm{pers} = 9600, 19200, 38400, 57600, 115200"));
3226             }
3227             }
3228             elsif ($familyType eq $Prm{s200}) {
3229 0 0 0       unless ($changeBaud->{baudrate} == 9600 || $changeBaud->{baudrate} == 19200 || $changeBaud->{baudrate} == 38400 ||
      0        
      0        
      0        
3230             $changeBaud->{baudrate} == 57600 || $changeBaud->{baudrate} == 115200) {
3231 0           return $self->poll_return($self->error("$pkgsub: Supported baud rates for $Prm{s200} = 9600, 19200, 38400, 57600, 115200"));
3232             }
3233             }
3234             else { # Other family types not supported
3235 0 0         return $self->poll_return($self->error("$pkgsub: Only supported on $Prm{pers} and $Prm{bstk} family_type")) unless $changeBaud->{maxMode};
3236 0           $self->debugMsg(4,"ChangeBaudrate: Not $Prm{pers} or $Prm{bstk} family_type - maxMode return success\n");
3237 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
3238 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
3239             }
3240 0           $changeBaud->{stage}++; # Move to 3rd stage
3241             }
3242              
3243 0 0         if ($changeBaud->{stage} < 3) { # 3rd stage
3244 0 0         if ($familyType eq $Prm{pers}) {
    0          
3245 0 0         unless (defined $self->{$Package}{ATTRIB}{'model'}) { # Make sure this attribute is set
3246 0           my $ok = $self->poll_attribute($pkgsub, 'model');
3247 0 0         return $self->poll_return($ok) unless $ok;
3248             }
3249 0 0         if ($changeBaud->{userExec} = $self->last_prompt =~ />\s?$/) {
3250 0           my $ok = $self->poll_enable($pkgsub);
3251 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3252             }
3253             }
3254             elsif ($familyType eq $Prm{s200}) {
3255 0 0         if ($changeBaud->{userExec} = $self->last_prompt =~ />\s?$/) {
3256 0           my $ok = $self->poll_enable($pkgsub);
3257 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3258             }
3259             }
3260 0           $changeBaud->{stage}++; # Move to 4th stage
3261             }
3262              
3263 0 0         if ($changeBaud->{stage} < 4) { # 4th stage
3264 0 0 0       if ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) {
    0          
3265 0 0         if ($changeBaud->{privExec} = !$self->config_context) {
3266 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'config term');
3267 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3268 0 0         return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device")) unless $$resref;
3269             }
3270             }
3271             elsif ($familyType eq $Prm{s200}) {
3272 0 0         if ($changeBaud->{privExec} = !$self->config_context) {
3273 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "config\nline console");
3274 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3275 0 0         return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device")) unless $$resref;
3276             }
3277             }
3278 0           $changeBaud->{stage}++; # Move to 5th stage
3279             }
3280              
3281 0 0         if ($changeBaud->{stage} < 5) { # 5th stage
3282 0 0         if ($familyType eq $Prm{bstk}) {
    0          
    0          
3283 0 0         $self->print(line => "terminal speed $changeBaud->{baudrate}", errmode => 'return')
3284             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3285             }
3286             elsif ($familyType eq $Prm{pers}) {
3287 0 0         if ($self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-(?:83|16)\d\d/) { # 8300 & 1600
3288 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3289 0 0         $self->print(line => "boot config sio baud $changeBaud->{baudrate}", errmode => 'return')
3290             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3291             }
3292             else {
3293 0 0         $self->print(line => "config bootconfig sio baud $changeBaud->{baudrate}", errmode => 'return')
3294             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3295             }
3296             }
3297             else { # All other PassportERS devices
3298 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3299 0 0         $self->print(line => "boot config sio console baud $changeBaud->{baudrate}", errmode => 'return')
3300             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3301             }
3302             else {
3303 0 0         $self->print(line => "config bootconfig sio console baud $changeBaud->{baudrate}", errmode => 'return')
3304             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3305             }
3306             }
3307             }
3308             elsif ($familyType eq $Prm{s200}) {
3309 0 0         $self->print(line => "serial baudrate $changeBaud->{baudrate}", errmode => 'return')
3310             or return $self->poll_return($self->error("$pkgsub: Unable to set new baud rate on device // ".$self->errmsg));
3311             }
3312 0           $self->debugMsg(4,"ChangeBaudrate: set device to ", \$changeBaud->{baudrate}, "\n");
3313 0           $changeBaud->{stage}++; # Move to 6th stage
3314             }
3315              
3316 0 0         if ($changeBaud->{stage} < 6) { # 6th stage
3317 0           my $ok = $self->poll_readwait($pkgsub, 0);
3318 0 0         return $self->poll_return($ok) unless $ok; # Come out if error, or if nothing to read in non-blocking mode
3319 0 0 0       if (length $self->{POLL}{read_buffer} && $self->{POLL}{read_buffer} =~ /$self->{$Package}{prompt_qr}/) {
3320             # This is a failure, as it would imply that we can see a prompt back, even though we changed the baudrate on the device
3321 0 0         return $self->poll_return($self->error("$pkgsub: Baudrate change had no effect on device; still at $self->baudrate baud")) unless $changeBaud->{maxMode};
3322 0           $self->debugMsg(4,"ChangeBaudrate: Baudrate change had no effect on device - maxMode return success\n");
3323 0           $self->{POLL}{output_result} = $self->baudrate;
3324 0           return $self->poll_return(1); # Can't maximize baudrate, but no error in maxMode
3325             }
3326 0 0         if (defined $self->{$Package}{ORIGBAUDRATE}) { # Clear note following restore
3327 0 0         $self->{$Package}{ORIGBAUDRATE} = undef if $self->{$Package}{ORIGBAUDRATE} == $changeBaud->{baudrate};
3328             }
3329             else { # 1st time this method is run, make a note of original baudrate (needed in DESTROY)
3330 0           $self->{$Package}{ORIGBAUDRATE} = $self->baudrate;
3331             }
3332 0           $changeBaud->{stage}++; # Move to 7th stage
3333             }
3334              
3335 0 0         if ($changeBaud->{stage} < 7) { # 7th stage
3336             my $ok = $self->SUPER::poll_change_baudrate($pkgsub,
3337             BaudRate => $changeBaud->{baudrate},
3338             ForceBaud => $changeBaud->{forcebaud},
3339 0           );
3340 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
3341 0           $self->debugMsg(4,"ChangeBaudrate: changed local serial port to ", \$changeBaud->{baudrate}, "\n");
3342 0           $self->_setAttrib('baudrate', $changeBaud->{baudrate}); # Adjust the attribute as we are sure we changed it now
3343 0           $changeBaud->{stage}++; # Move to 8th stage
3344             }
3345              
3346 0 0         if ($changeBaud->{stage} < 8) { # 8th stage
3347 0           my $ok = $self->poll_cmd($pkgsub, ''); # Send carriage return + ensure we get valid prompt back
3348 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3349 0           $changeBaud->{stage}++; # Move to 9th stage
3350             }
3351              
3352 0 0         if ($changeBaud->{stage} < 9) { # 9th stage
3353 0 0 0       if ( ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) || $familyType eq $Prm{s200}) {
      0        
3354 0 0         if ($changeBaud->{privExec}) {
3355 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, 'end');
3356 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3357 0 0         return $self->poll_return($self->error("$pkgsub: Error while changing baud rate")) unless $$resref;
3358             }
3359             }
3360 0           $changeBaud->{stage}++; # Move to 10th stage
3361             }
3362              
3363 0 0         if ($changeBaud->{stage} < 10) { # 10th stage
3364 0           $changeBaud->{stage}++; # Move to 1th stage
3365 0 0 0       if ( ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) || $familyType eq $Prm{s200}) {
      0        
3366 0 0         if ($changeBaud->{userExec}) {
3367 0           my $disableCmd;
3368 0 0         if (defined $ExitPrivExec{$familyType}) {
3369 0           $disableCmd = $ExitPrivExec{$familyType};
3370 0           $self->put($disableCmd);
3371             }
3372             else {
3373 0           $disableCmd = 'disable';
3374 0           $self->print($disableCmd);
3375             }
3376 0           $self->debugMsg(8,"\npoll_change_baudrate() Sending command:>", \$disableCmd, "<\n");
3377             }
3378             }
3379             }
3380 0 0         if ($changeBaud->{stage} < 11) { # 11th stage
3381 0 0 0       if ( ($familyType eq $Prm{pers} && $self->{$Package}{ATTRIB}{'is_nncli'}) || $familyType eq $Prm{s200}) {
      0        
3382 0 0         if ($changeBaud->{userExec}) {
3383 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub);
3384 0 0         return $self->poll_return($ok) unless $ok; # Come out if error or if not done yet in non-blocking mode
3385 0 0         return $self->poll_return($self->error("$pkgsub: Error while changing baud rate")) unless $$resref;
3386             }
3387             }
3388             }
3389 0           $self->{POLL}{output_result} = $changeBaud->{baudrate};
3390 0           return $self->poll_return(1);
3391             }
3392              
3393              
3394             sub poll_enable { # Method to handle enable for poll methods (used for both blocking & non-blocking modes)
3395 0     0 1   my $self = shift;
3396 0           my $pkgsub = shift;
3397 0           my $pollsub = "${Package}::enable";
3398              
3399 0 0         unless ($self->{POLLING}) { # Sanity check
3400 0           my (undef, $fileName, $lineNumber) = caller;
3401 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
3402             }
3403              
3404 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
3405 0           my @validArgs = ('password', 'prompt_credentials', 'timeout', 'errmode');
3406 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
3407 0 0 0       if (@_ && !%args) { # Legacy syntax
3408 0           ($args{password}, $args{prompt_credentials}, $args{timeout}, $args{errmode}) = @_;
3409             }
3410             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
3411             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
3412             # Set method argument keys
3413             enable_password => defined $args{password} ? $args{password} : $self->{$Package}{ENABLEPWD},
3414             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
3415             # Declare method storage keys which will be used
3416             stage => 0,
3417             login_attempted => undef,
3418             login_failed => undef,
3419             # Declare keys to be set if method called from another polled method
3420             errmode => $args{errmode},
3421 0 0         };
    0          
3422             # Cache poll structure keys which this method will use
3423 0           $self->poll_struct_cache($pollsub, $args{timeout});
3424             }
3425 0           my $enable = $self->{POLL}{$pollsub};
3426 0 0         local $self->{errmode} = $enable->{errmode} if defined $enable->{errmode};
3427 0 0         return $self->poll_return($self->error("$pkgsub: No connection to enable")) if $self->eof;
3428 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3429 0           my $prompt = $self->{$Package}{prompt_qr};
3430 0           my $passwordPrompt = $self->{password_prompt_qr};
3431 0           my $enablePwd;
3432              
3433 0 0         if ($enable->{stage} < 1) { # 1st stage
3434 0           $enable->{stage}++; # Ensure we don't come back here in non-blocking mode
3435 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) unless $familyType;
3436 0 0         return $self->poll_return(1) unless $self->{$Package}{ATTRIB}{'is_nncli'}; # Come out if not in NNCLI mode
3437 0 0         return $self->poll_return(1) unless $self->last_prompt =~ />\s?$/; # Come out if not in UserExec mode
3438             # Flush any unread data which might be pending
3439 0           $self->read(blocking => 0);
3440             # Send enable command
3441 0 0         $self->print(line => 'enable', errmode => 'return')
3442             or return $self->poll_return($self->error("$pkgsub: Unable to send CLI command: enable // ".$self->errmsg));
3443             }
3444              
3445             # Main loop
3446             do {
3447 0           my $ok = $self->poll_read($pkgsub, 'Failed after enable command');
3448 0 0         return $self->poll_return($ok) unless $ok;
3449              
3450 0           $self->{POLL}{local_buffer} .= $self->{POLL}{read_buffer};
3451 0 0         $enable->{login_failed}++ if $self->{POLL}{local_buffer} =~ /error: Access denied/;
3452 0 0         if ($self->{POLL}{local_buffer} =~ /$passwordPrompt/) { # Handle password prompt
3453 0           $enable->{login_attempted}++;
3454 0 0         if (defined $enable->{enable_password}) { # An enable password is supplied
3455 0 0         if ($enable->{login_attempted} == 1) { # First try; use supplied
3456 0           $enablePwd = $enable->{enable_password};
3457 0           $self->debugMsg(4,"enable() Sending supplied password\n");
3458 0 0         $self->print(line => $enablePwd, errmode => 'return')
3459             or return $self->poll_return($self->error("$pkgsub: Unable to send enable password // ".$self->errmsg));
3460             }
3461             else { # Next tries, enter blanks
3462 0           $enablePwd = '';
3463 0           $self->debugMsg(4,"enable() Sending carriage return instead of supplied password\n");
3464 0 0         $self->print(errmode => 'return')
3465             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
3466             }
3467             }
3468             else { # No password supplied
3469 0 0         if ($enable->{login_attempted} == 1) { # First try; use blank
    0          
3470 0           $enablePwd = '';
3471 0           $self->debugMsg(4,"enable() Sending carriage return for password\n");
3472 0 0         $self->print(errmode => 'return')
3473             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
3474             }
3475             elsif ($enable->{login_attempted} == 2) { # Second try; use cached login password
3476 0   0       $enablePwd = $self->password || '';
3477 0           $self->debugMsg(4,"enable() Sending login password for enable password\n");
3478 0 0         $self->print(line => $enablePwd, errmode => 'return')
3479             or return $self->poll_return($self->error("$pkgsub: Unable to send cached password // ".$self->errmsg));
3480             }
3481             else { # Third try; prompt?
3482 0 0         if ($enable->{prompt_credentials}) {
3483 0           $enablePwd = promptCredential($enable->{prompt_credentials}, 'Hide', 'Enable Password');
3484 0 0         $self->print(line => $enablePwd, errmode => 'return')
3485             or return $self->poll_return($self->error("$pkgsub: Unable to send enable password // ".$self->errmsg));
3486             }
3487             else { # Enter blanks
3488 0           $enablePwd = '';
3489 0           $self->debugMsg(4,"enable() Sending carriage return instead of prompting for password\n");
3490 0 0         $self->print(errmode => 'return')
3491             or return $self->poll_return($self->error("$pkgsub: Unable to send blank password // ".$self->errmsg));
3492             }
3493             }
3494             }
3495 0           $self->{POLL}{local_buffer} = '';
3496             }
3497 0           } until ($self->{POLL}{local_buffer} =~ /($prompt)/);
3498 0           $self->_setLastPromptAndConfigContext($1, $2);
3499 0 0         return $self->poll_return($self->error("$pkgsub: Password required")) if $enable->{login_failed};
3500 0 0         return $self->poll_return($self->error("$pkgsub: Failed to enter PrivExec mode")) if $self->last_prompt =~ />\s?$/; # If still in UserExec mode
3501 0 0         $self->{$Package}{ENABLEPWD} = $enablePwd if defined $enablePwd;
3502 0           return $self->poll_return(1);
3503             }
3504              
3505              
3506             sub poll_device_more_paging { # Method to handle device_more_paging for poll methods (used for both blocking & non-blocking modes)
3507 0     0 1   my $self = shift;
3508 0           my $pkgsub = shift;
3509 0           my $pollsub = "${Package}::device_more_paging";
3510              
3511 0 0         unless ($self->{POLLING}) { # Sanity check
3512 0           my (undef, $fileName, $lineNumber) = caller;
3513 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
3514             }
3515              
3516 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
3517 0           my @validArgs = ('enable', 'timeout', 'errmode');
3518 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
3519 0 0 0       if (@_ && !%args) { # Legacy syntax
3520 0           ($args{enable}, $args{timeout}, $args{errmode}) = @_;
3521             }
3522             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
3523             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
3524             # Set method argument keys
3525             enable => $args{enable},
3526             # Declare method storage keys which will be used
3527             stage => 0,
3528             cmdString => undef,
3529             # Declare keys to be set if method called from another polled method
3530             errmode => $args{errmode},
3531 0           };
3532             # Cache poll structure keys which this method will use
3533 0           $self->poll_struct_cache($pollsub, $args{timeout});
3534             }
3535 0           my $devMorePage = $self->{POLL}{$pollsub};
3536 0 0         local $self->{errmode} = $devMorePage->{errmode} if defined $devMorePage->{errmode};
3537 0 0         return $self->poll_return($self->error("$pkgsub: No connection to set more paging on")) if $self->eof;
3538 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3539              
3540 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) unless $familyType;
3541 0 0 0       if ($familyType eq $Prm{bstk}) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
3542 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
3543 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "terminal length $devMorePage->{cmdString}");
3544 0 0         return $self->poll_return($ok) unless $ok;
3545 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3546             }
3547             elsif ($familyType eq $Prm{pers} || $familyType eq $Prm{xlr}) {
3548 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) { # NNCLI
3549 0 0         if ($devMorePage->{stage} < 1) {
3550 0 0         unless (defined $self->{$Package}{ATTRIB}{'model'}) { # This attribute may not yet be set
3551 0           my $ok = $self->poll_attribute($pkgsub, 'model');
3552 0 0         return $self->poll_return($ok) unless $ok;
3553             }
3554 0 0 0       if (defined $self->{$Package}{ATTRIB}{'model'} && $self->{$Package}{ATTRIB}{'model'} =~ /(?:Passport|ERS)-83\d\d/) { # 8300 NNCLI
3555 0           $devMorePage->{stage} += 2; # Go to section after next
3556             }
3557             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..)
3558 0           $devMorePage->{stage}++; # Go to next section
3559             }
3560             }
3561 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..)
3562 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'enable' : 'disable' unless defined $devMorePage->{cmdString};
    0          
3563 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "terminal more $devMorePage->{cmdString}");
3564 0 0         return $self->poll_return($ok) unless $ok;
3565 0 0 0       return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) if !$$resref && defined $self->{$Package}{ATTRIB}{'model'};
3566 0           $devMorePage->{stage}++; # Go to next section (8300) if we failed here and 'model' attrib not defined
3567 0 0         $devMorePage->{stage}++ if $$resref; # Skip next section if we succeded
3568 0           $devMorePage->{cmdString} = undef;
3569             }
3570 0 0         if ($devMorePage->{stage} < 3) { # 8300 NNCLI
3571 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '' : 'no ' unless defined $devMorePage->{cmdString};
    0          
3572 0           my ($ok, undef, $resref) = $self->cmdConfig($pkgsub, '', "$devMorePage->{cmdString}more");
3573 0 0         return $self->poll_return($ok) unless $ok;
3574 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3575             }
3576             }
3577             else { # CLI
3578 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'true' : 'false' unless defined $devMorePage->{cmdString};
    0          
3579 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "config cli more $devMorePage->{cmdString}");
3580 0 0         return $self->poll_return($ok) unless $ok;
3581 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3582             }
3583             }
3584             elsif ($familyType eq $Prm{sr}) {
3585 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
3586 0           my ($ok, undef, $resref) = $self->cmdConfig($pkgsub, '', "terminal length $devMorePage->{cmdString}");
3587 0 0         return $self->poll_return($ok) unless $ok;
3588 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3589             }
3590             elsif ($familyType eq $Prm{trpz}) {
3591 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 23 : 0 unless defined $devMorePage->{cmdString};
    0          
3592 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "set length $devMorePage->{cmdString}");
3593 0 0         return $self->poll_return($ok) unless $ok;
3594 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3595             }
3596             elsif ($familyType eq $Prm{xirrus}) {
3597 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'enable' : 'disable' unless defined $devMorePage->{cmdString};
    0          
3598 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "more $devMorePage->{cmdString}");
3599 0 0         return $self->poll_return($ok) unless $ok;
3600 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3601             }
3602             elsif ($familyType eq $Prm{xos}) {
3603 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'enable' : 'disable' unless defined $devMorePage->{cmdString};
    0          
3604 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "$devMorePage->{cmdString} clipaging");
3605 0 0         return $self->poll_return($ok) unless $ok;
3606 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3607             }
3608             elsif ($familyType eq $Prm{s200}) {
3609 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '24' : '0' unless defined $devMorePage->{cmdString};
    0          
3610 0           my ($ok, undef, $resref) = $self->cmdPrivExec($pkgsub, undef, "terminal length $devMorePage->{cmdString}");
3611 0 0         return $self->poll_return($ok) unless $ok;
3612 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3613             }
3614             elsif ($familyType eq $Prm{isw}) {
3615 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '24' : '0' unless defined $devMorePage->{cmdString};
    0          
3616 0 0         my ($ok, undef, $resref) = $self->cmdPrivExec($pkgsub, undef, ($self->config_context ? 'do ':'') . "terminal length $devMorePage->{cmdString}");
3617 0 0         return $self->poll_return($ok) unless $ok;
3618 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3619             }
3620             elsif ($familyType eq $Prm{wing}) {
3621 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '24' : '0' unless defined $devMorePage->{cmdString};
    0          
3622 0 0         my ($ok, undef, $resref) = $self->cmdPrivExec($pkgsub, undef, ($self->config_context ? 'do ':'') . "terminal length $devMorePage->{cmdString}");
3623 0 0         return $self->poll_return($ok) unless $ok;
3624 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3625             }
3626             elsif ($familyType eq $Prm{slx}) {
3627 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? 'no length' : 'length 0' unless defined $devMorePage->{cmdString};
    0          
3628 0 0         my ($ok, undef, $resref) = $self->cmdPrivExec($pkgsub, undef, ($self->config_context ? 'do ':'') . "terminal $devMorePage->{cmdString}");
3629 0 0         return $self->poll_return($ok) unless $ok;
3630 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3631             }
3632             elsif ($familyType eq $Prm{hive}) {
3633 0 0         $devMorePage->{cmdString} = $devMorePage->{enable} ? '22' : '0' unless defined $devMorePage->{cmdString};
    0          
3634 0           my ($ok, undef, $resref) = $self->poll_cmd($pkgsub, "console page $devMorePage->{cmdString}");
3635 0 0         return $self->poll_return($ok) unless $ok;
3636 0 0         return $self->poll_return($self->error("$pkgsub: Failed to set more-paging mode")) unless $$resref;
3637             }
3638             else {
3639 0           return $self->poll_return($self->error("$pkgsub: Cannot configure more paging on family type $familyType"));
3640             }
3641 0           return $self->poll_return(1);
3642             }
3643              
3644              
3645             sub poll_device_peer_cpu { # Method to handle device_peer_cpu for poll methods (used for both blocking & non-blocking modes)
3646 0     0 1   my $self = shift;
3647 0           my $pkgsub = shift;
3648 0           my $pollsub = "${Package}::device_peer_cpu";
3649              
3650 0 0         unless ($self->{POLLING}) { # Sanity check
3651 0           my (undef, $fileName, $lineNumber) = caller;
3652 0           croak "$pollsub (called from $fileName line $lineNumber) can only be used within polled methods";
3653             }
3654              
3655 0 0         unless (defined $self->{POLL}{$pollsub}) { # Only applicable if called from another method already in polling mode
3656 0           my @validArgs = ('username', 'password', 'prompt_credentials', 'timeout', 'errmode');
3657 0           my %args = parseMethodArgs($pkgsub, \@_, \@validArgs, 1);
3658 0 0 0       if (@_ && !%args) { # Legacy syntax
3659 0           ($args{username}, $args{password}, $args{prompt_credentials}, $args{timeout}, $args{errmode}) = @_;
3660             }
3661             # In which case we need to setup the poll structure for them here (the main poll structure remains unchanged)
3662             $self->{POLL}{$pollsub} = { # Populate structure with method arguments/storage
3663             # Set method argument keys
3664             username => defined $args{username} ? $args{username} : $self->username,
3665             password => defined $args{password} ? $args{password} : $self->password,
3666             prompt_credentials => defined $args{prompt_credentials} ? $args{prompt_credentials} : $self->{prompt_credentials},
3667             # Declare method storage keys which will be used
3668             stage => 0,
3669             # Declare keys to be set if method called from another polled method
3670             errmode => $args{errmode},
3671 0 0         };
    0          
    0          
3672             # Cache poll structure keys which this method will use
3673 0           $self->poll_struct_cache($pollsub, $args{timeout});
3674             }
3675 0           my $devPeerCpu = $self->{POLL}{$pollsub};
3676 0 0         local $self->{errmode} = $devPeerCpu->{errmode} if defined $devPeerCpu->{errmode};
3677 0 0         return $self->poll_return($self->error("$pkgsub: No connection established")) if $self->eof;
3678 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3679              
3680 0 0         if ($devPeerCpu->{stage} < 1) { # 1st stage
3681 0 0         unless ($familyType) {
3682 0           return $self->poll_return($self->error("$pkgsub: Attribute family_type not set"));
3683             }
3684 0 0         unless ($familyType eq $Prm{pers}) {
3685 0           return $self->poll_return($self->error("$pkgsub: No peer CPU on family_type $familyType"));
3686             }
3687 0 0 0       unless (($devPeerCpu->{username} && $devPeerCpu->{password}) || $devPeerCpu->{prompt_credentials}) {
      0        
3688 0           return $self->poll_return($self->error("$pkgsub: Username & password required"));
3689             }
3690 0           $devPeerCpu->{stage}++; # Move to 2nd stage
3691             }
3692              
3693 0 0         if ($devPeerCpu->{stage} < 2) { # 2nd stage
3694 0           my $ok = $self->poll_enable($pkgsub); # If in nncli mode, need to be in PrivExec
3695 0 0         return $self->poll_return($ok) unless $ok;
3696              
3697 0 0         $self->print(line => 'peer telnet', errmode => 'return')
3698             or return $self->poll_return($self->error("$pkgsub: Unable to send peer telnet command // ".$self->errmsg));
3699 0           $devPeerCpu->{stage}++; # Move to 3rd stage
3700             }
3701              
3702 0 0         if ($devPeerCpu->{stage} < 3) { # 3rd stage
3703 0           my $ok = $self->poll_waitfor($pkgsub, 'Login: $', undef, 'return');
3704 0 0         return $self->poll_return($self->error("$pkgsub: Never got peer login prompt // ".$self->errmsg)) unless defined $ok;
3705 0 0         return $self->poll_return($ok) unless $ok;
3706              
3707 0 0         $devPeerCpu->{username} = promptCredential($devPeerCpu->{prompt_credentials}, 'Clear', 'Username') unless defined $devPeerCpu->{username};
3708 0 0         $self->print(line => $devPeerCpu->{username}, errmode => 'return')
3709             or return $self->poll_return($self->error("$pkgsub: Unable to send username // ".$self->errmsg));
3710 0           $devPeerCpu->{stage}++; # Move to 4th stage
3711             }
3712              
3713 0 0         if ($devPeerCpu->{stage} < 4) { # 4th stage
3714 0           my $ok = $self->poll_waitfor($pkgsub, 'Password: $', undef, 'return');
3715 0 0         return $self->poll_return($self->error("$pkgsub: Never got peer password prompt // ".$self->errmsg)) unless defined $ok;
3716 0 0         return $self->poll_return($ok) unless $ok;
3717              
3718 0 0         $devPeerCpu->{password} = promptCredential($devPeerCpu->{prompt_credentials}, 'Hide', 'Password') unless defined $devPeerCpu->{password};
3719 0 0         $self->print(line => $devPeerCpu->{password}, errmode => 'return')
3720             or return $self->poll_return($self->error("$pkgsub: Unable to send password // ".$self->errmsg));
3721 0           $devPeerCpu->{stage}++; # Move to last stage
3722             }
3723              
3724             # Use cmd() to expect a new prompt now
3725 0           my $ok = $self->poll_cmd($pkgsub, More_pages => 0, Reset_prompt => 1);
3726 0 0         return $self->poll_return($ok) unless $ok;
3727              
3728 0           $self->{LASTPROMPT} =~ /$InitPrompt{$self->{$Package}{PROMPTTYPE}}/;
3729 0           $self->_setAttrib('cpu_slot', $2);
3730 0 0         $self->_setAttrib('is_master_cpu', $self->{LASTPROMPT} =~ /^@/ ? 0 : 1);
3731 0 0         $self->_setAttrib('is_dual_cpu', 1) if $self->{LASTPROMPT} =~ /^@/;
3732 0           return $self->poll_return(1);
3733             }
3734              
3735              
3736             sub cmdPrivExec { # If nncli send command in PrivExec mode and restore mode on exit; if not nncli just sends command; used for show commands
3737 0     0 1   my ($self, $pkgsub, $cmdcli, $cmdnncli, $morePages) = @_;
3738 0           my $pollsub = "${Package}::cmdPrivExec";
3739 0           my ($ok, $outref, $resref);
3740              
3741 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3742 0           $self->{POLL}{$pollsub} = {
3743             stage => 0,
3744             userExec => undef,
3745             outref => undef,
3746             resref => undef,
3747             };
3748             }
3749 0           my $cmdPrivExec = $self->{POLL}{$pollsub};
3750 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3751              
3752 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3753 0 0         if ($cmdPrivExec->{stage} < 1) { # 1st stage
3754 0 0         if ($self->{WRITEFLAG}) { # Direct writes were performed, need a new prompt to be sure about last_prompt
3755 0           $ok = $self->poll_cmd($pkgsub, ''); # Send just carriage return
3756 0 0         return $ok unless $ok;
3757             }
3758 0           $cmdPrivExec->{stage}++; # Move to 2nd stage
3759             }
3760 0 0         if ($cmdPrivExec->{stage} < 2) { # 2nd stage
3761 0 0         if ($cmdPrivExec->{userExec} = $self->last_prompt =~ />\s?$/) {
3762 0           $ok = $self->poll_enable($pkgsub);
3763 0 0         return $ok unless $ok;
3764             }
3765 0           $cmdPrivExec->{stage}++; # Move to 3rd stage
3766             }
3767 0 0         if ($cmdPrivExec->{stage} < 3) { # 3rd stage
3768 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, Command => $cmdnncli, More_pages => $morePages);
3769 0 0         return $ok unless $ok;
3770 0           $cmdPrivExec->{outref} = $outref;
3771 0           $cmdPrivExec->{resref} = $resref;
3772 0           $cmdPrivExec->{stage}++; # Move to 4th stage
3773             }
3774 0 0         if ($cmdPrivExec->{stage} < 4) { # 4th stage
3775 0           $cmdPrivExec->{stage}++; # Move to 5th stage - we only spend one cycle here
3776 0 0         if ($cmdPrivExec->{userExec}) {
3777 0           my $disableCmd;
3778 0 0         if (defined $ExitPrivExec{$familyType}) {
3779 0           $disableCmd = $ExitPrivExec{$familyType};
3780 0           $self->put($disableCmd);
3781             }
3782             else {
3783 0           $disableCmd = 'disable';
3784 0           $self->print($disableCmd);
3785             }
3786 0           $self->debugMsg(8,"\ncmdPrivExec() Sending command:>", \$disableCmd, "<\n");
3787             }
3788             }
3789 0 0         if ($cmdPrivExec->{stage} < 5) { # 5th stage
3790 0 0         if ($cmdPrivExec->{userExec}) {
3791 0           $ok = $self->poll_cmd($pkgsub); # We don't bother getting $resref here...
3792 0 0         return $ok unless $ok;
3793             # ... because even if $$resref was false, we want to fall through
3794             }
3795 0           ($outref, $resref) = ($cmdPrivExec->{outref}, $cmdPrivExec->{resref});
3796 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3797 0           return (1, $outref, $resref);
3798             }
3799             }
3800             else {
3801 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, Command => $cmdcli, More_pages => $morePages);
3802 0 0         return $ok unless $ok;
3803 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3804 0           return (1, $outref, $resref);
3805             }
3806             }
3807              
3808              
3809             sub cmdConfig { # If nncli send command in Config mode and restore mode on exit; if not nncli just sends command; used for config commands
3810 0     0 1   my ($self, $pkgsub, $cmdcli, $cmdnncli) = @_;
3811 0           my $pollsub = "${Package}::cmdConfig";
3812 0           my ($ok, $outref, $resref);
3813              
3814 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3815 0           $self->{POLL}{$pollsub} = {
3816             stage => 0,
3817             userExec => undef,
3818             privExec => undef,
3819             outref => undef,
3820             resref => undef,
3821             };
3822             }
3823 0           my $cmdConfig = $self->{POLL}{$pollsub};
3824 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
3825              
3826 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
3827 0 0         if ($cmdConfig->{stage} < 1) { # 1st stage
3828 0 0         if ($self->{WRITEFLAG}) { # Direct writes were performed, need a new prompt to be sure about last_prompt
3829 0           $ok = $self->poll_cmd($pkgsub, ''); # Send just carriage return
3830 0 0         return $ok unless $ok;
3831             }
3832 0           $cmdConfig->{stage}++; # Move to 2nd stage
3833             }
3834 0 0         if ($cmdConfig->{stage} < 2) { # 2nd stage
3835 0 0         if ($cmdConfig->{userExec} = $self->last_prompt =~ />\s?$/) {
3836 0           $ok = $self->poll_enable($pkgsub);
3837 0 0         return $ok unless $ok;
3838             }
3839 0           $cmdConfig->{stage}++; # Move to 3rd stage
3840             }
3841 0 0         if ($cmdConfig->{stage} < 3) { # 3rd stage
3842 0 0         if ($cmdConfig->{privExec} = !$self->config_context) { # This needs to match '(config[-if])' or SecureRouter '/configure' or '(conf-if..)' SLX
3843 0 0 0       my $configCmd = $familyType eq 'WLAN9100' || $familyType eq 'Series200' ? 'config' : 'config term';
3844 0           ($ok, undef, $resref) = $self->poll_cmd($pkgsub, $configCmd);
3845 0 0         return $ok unless $ok;
3846 0 0         return ($ok, '', $resref) unless $$resref; # Never return undef output with true $ok
3847             }
3848 0           $cmdConfig->{stage}++; # Move to 4th stage
3849             }
3850 0 0         if ($cmdConfig->{stage} < 4) { # 4th stage
3851 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, $cmdnncli);
3852 0 0         return $ok unless $ok;
3853 0           $cmdConfig->{outref} = $outref;
3854 0           $cmdConfig->{resref} = $resref;
3855 0           $cmdConfig->{stage}++; # Move to 5th stage
3856             }
3857 0 0         if ($cmdConfig->{stage} < 5) { # 5th stage
3858 0 0         if ($cmdConfig->{privExec}) {
3859 0           $ok = $self->poll_cmd($pkgsub, 'end'); # We don't bother getting $resref here...
3860 0 0         return $ok unless $ok;
3861             # ... because even if $$resref was false, we want to fall through
3862             }
3863 0           $cmdConfig->{stage}++; # Move to 6th stage
3864             }
3865 0 0         if ($cmdConfig->{stage} < 6) { # 6th stage
3866 0           $cmdConfig->{stage}++; # Move to 7th stage - we only spend one cycle here
3867 0 0         if ($cmdConfig->{userExec}) {
3868 0           my $disableCmd;
3869 0 0         if (defined $ExitPrivExec{$familyType}) {
3870 0           $disableCmd = $ExitPrivExec{$familyType};
3871 0           $self->put($disableCmd);
3872             }
3873             else {
3874 0           $disableCmd = 'disable';
3875 0           $self->print($disableCmd);
3876             }
3877 0           $self->debugMsg(8,"\cmdConfig() Sending command:>", \$disableCmd, "<\n");
3878             }
3879             }
3880 0 0         if ($cmdConfig->{stage} < 7) { # 7th stage
3881 0 0         if ($cmdConfig->{userExec}) {
3882 0           $ok = $self->poll_cmd($pkgsub); # We don't bother getting $resref here...
3883 0 0         return $ok unless $ok;
3884             # ... because even if $$resref was false, we want to fall through
3885             }
3886 0           ($outref, $resref) = ($cmdConfig->{outref}, $cmdConfig->{resref});
3887 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3888 0           return (1, $outref, $resref);
3889             }
3890             }
3891             else {
3892 0 0         $cmdcli = "config $cmdcli" unless $cmdcli =~ /^config /; # Prepend config if not already there
3893 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, $cmdcli);
3894 0 0         return $ok unless $ok;
3895 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3896 0           return (1, $outref, $resref);
3897             }
3898             }
3899              
3900              
3901             sub cmdIpanema { # If on Ipanema, make sure we come out of virtual router instances before sending command, and restore on exit
3902 0     0 1   my ($self, $pkgsub, $cmdcli, $morePages) = @_;
3903 0           my $pollsub = "${Package}::cmdIpanema";
3904 0           my ($ok, $outref, $resref);
3905              
3906 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3907 0           $self->{POLL}{$pollsub} = {
3908             stage => 0,
3909             virtualRt => undef,
3910             virtualRtCli => undef,
3911             outref => undef,
3912             resref => undef,
3913             };
3914             }
3915 0           my $cmdIpanema = $self->{POLL}{$pollsub};
3916              
3917 0 0         if ($cmdIpanema->{stage} < 1) { # 1st stage
3918 0 0         if ($self->{WRITEFLAG}) { # Direct writes were performed, need a new prompt to be sure about last_prompt
3919 0           $ok = $self->poll_cmd($pkgsub, ''); # Send just carriage return
3920 0 0         return $ok unless $ok;
3921             }
3922 0           $cmdIpanema->{stage}++; # Move to 2nd stage
3923             }
3924 0 0         if ($cmdIpanema->{stage} < 2) { # 2nd stage
3925 0           $cmdIpanema->{stage}++; # Move to 3rd stage - we only spend one cycle here
3926 0 0 0       if ( ( ($cmdIpanema->{virtualRt}) = $self->last_prompt =~ /\.rt(\d):/g )
3927             || ( ($cmdIpanema->{virtualRtCli}) = $self->last_prompt =~ /\.rt(\d)\]/g )
3928             ) {
3929 0           my $exitCmd = 'exit';
3930 0           $self->print($exitCmd);
3931 0           $self->debugMsg(8,"\ncmdIpanema() Sending command:>", \$exitCmd, "<\n");
3932             }
3933             }
3934 0 0         if ($cmdIpanema->{stage} < 3) { # 3rd stage
3935 0 0 0       if ($cmdIpanema->{virtualRt} || $cmdIpanema->{virtualRtCli}) {
3936 0           $ok = $self->poll_cmd($pkgsub); # We don't bother getting $resref here...
3937 0 0         return $ok unless $ok;
3938             # ... because even if $$resref was false, we want to fall through
3939             }
3940 0           $cmdIpanema->{stage}++; # Move to 4th stage
3941             }
3942 0 0         if ($cmdIpanema->{stage} < 4) { # 4th stage
3943 0           ($ok, $outref, $resref) = $self->poll_cmd($pkgsub, Command => $cmdcli, More_pages => $morePages);
3944 0 0         return $ok unless $ok;
3945 0           $cmdIpanema->{outref} = $outref;
3946 0           $cmdIpanema->{resref} = $resref;
3947 0           $cmdIpanema->{stage}++; # Move to 5th stage
3948             }
3949 0 0         if ($cmdIpanema->{stage} < 5) { # 5th stage
3950 0           $cmdIpanema->{stage}++; # Move to 6th stage - we only spend one cycle here
3951 0           my $virtualRtCmd;
3952 0 0         if ($cmdIpanema->{virtualRt}) {
    0          
3953 0           $virtualRtCmd = 'ns rt' . $cmdIpanema->{virtualRt} . ' bash';
3954             }
3955             elsif ($cmdIpanema->{virtualRtCli}) {
3956 0           $virtualRtCmd = 'router ' . $cmdIpanema->{virtualRtCli};
3957             }
3958 0 0         if (defined $virtualRtCmd) {
3959 0           $self->print($virtualRtCmd);
3960 0           $self->debugMsg(8,"\ncmdIpanema() Sending command:>", \$virtualRtCmd, "<\n");
3961             }
3962             }
3963 0 0         if ($cmdIpanema->{stage} < 6) { # 6th stage
3964 0 0         if ($cmdIpanema->{virtualRt}) {
3965 0           $ok = $self->poll_cmd($pkgsub); # We don't bother getting $resref here...
3966 0 0         return $ok unless $ok;
3967             # ... because even if $$resref was false, we want to fall through
3968             }
3969 0           ($outref, $resref) = ($cmdIpanema->{outref}, $cmdIpanema->{resref});
3970 0           $self->{POLL}{$pollsub} = undef; # Undef once completed
3971 0           return (1, $outref, $resref);
3972             }
3973             }
3974              
3975              
3976             sub discoverDevice { # Issues CLI commands to host, to determine what family type it belongs to
3977 0     0 1   my ($self, $pkgsub) = @_;
3978 0           my $pollsub = "${Package}::discoverDevice";
3979              
3980 0 0         unless (defined $self->{POLL}{$pollsub}) { # Create polling structure on 1st call
3981 0           $self->{POLL}{$pollsub} = {
3982             stage => 0,
3983             };
3984             }
3985 0           my $discDevice = $self->{POLL}{$pollsub};
3986              
3987 0 0         if ($discDevice->{stage} < 1) { # Initial loginstage checking - do only once
3988 0           $discDevice->{stage}++; # Ensure we don't come back here in non-blocking mode
3989 0           $self->debugMsg(4,"\nATTEMPTING EXTENDED DISCOVERY OF HOST DEVICE !\n");
3990             # Output from commands below is prone to false triggers on the generic prompt;
3991             # On top of that, the 1st prompt received from login() can be "spoilt" with extra character pre-pended (SLX does that).
3992             }
3993 0 0         if ($discDevice->{stage} < 2) { # Get a fresh new prompt
3994             # .. so we get a fresh new promp
3995 0           my $ok = $self->poll_cmd($pkgsub, ''); # Send just carriage return
3996 0 0         return $ok unless $ok;
3997 0           $discDevice->{stage}++; # Move to next stage on next cycle
3998             # .. and we lock it down to the minimum length required
3999 0           $self->last_prompt =~ /(.*)($GenericPromptRegex)/;
4000 0           $self->prompt(join('', ".{", length($1), ",}\Q$2\E\$"));
4001             }
4002              
4003             # Prefer commands unique to platform, and with small output (not more paged)
4004             # Check more common family types first
4005              
4006 0 0         if ($discDevice->{stage} < 3) { # Next stage
4007             # BaystackERS detection command
4008 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show ip address');
4009 0 0         return $ok unless $ok;
4010 0           $discDevice->{stage}++; # Move to next stage on next cycle
4011 0 0         if ($$outref =~ /\s+Configured\s+In Use\s+Last BootP/) {
4012 0           $self->_setFamilyTypeAttrib($Prm{bstk}, is_nncli => 1);
4013 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{bstk}}/;
4014 0           $self->_setDevicePrompts($Prm{bstk}, $1);
4015 0           return (1, $Prm{bstk});
4016             }
4017             # Ipanema detection via CLI error message
4018 0 0         if ($$outref =~/^\? Unknown command \(try "help"\)\.$/) {
4019 0           $self->_setFamilyTypeAttrib($Prm{ipanema}, is_nncli => 0, is_sdwan => 1, baudrate => 9600);
4020 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{ipanema}}/;
4021 0           $self->_setDevicePrompts($Prm{ipanema}, $1);
4022 0           return (1, $Prm{ipanema});
4023             }
4024             }
4025 0 0         if ($discDevice->{stage} < 4) { # Next stage
4026             # PassportERS-nncli detection command
4027 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show basic config');
4028 0 0         return $ok unless $ok;
4029 0           $discDevice->{stage}++; # Move to next stage on next cycle
4030 0 0         if ($$outref =~ /^\s+auto-recover-delay :/m) {
4031 0           $self->_setFamilyTypeAttrib($Prm{pers}, is_nncli => 1, is_master_cpu => 1);
4032 0           $self->{LASTPROMPT} =~ /$InitPrompt{"$Prm{pers}_nncli"}/;
4033 0           $self->_setDevicePrompts("$Prm{pers}_nncli", $1);
4034 0           return (1, $Prm{pers});
4035             }
4036             }
4037 0 0         if ($discDevice->{stage} < 5) { # Next stage
4038             # SLX detection command
4039 0 0         my ($ok, $outref) = $self->poll_cmd($pkgsub, ($self->config_context ? 'do ':'') . 'show chassis | include "Chassis Name:|switchType:"');
4040 0 0         return $ok unless $ok;
4041 0           $discDevice->{stage}++; # Move to next stage on next cycle
4042 0 0         if ($$outref =~ /^Chassis Name:(?:\t|\e\[\d\w)(?:(?:BR|EN)-)?(.+)/m) { # On serial port SLX uses \e[3C instead of tab char
4043 0           my $model = $1;
4044 0           $self->_setFamilyTypeAttrib($Prm{slx}, is_nncli => 1, is_slx => 1);
4045 0           $self->_setModelAttrib($model);
4046 0 0 0       $self->_setAttrib('baudrate', defined $self->{$Package}{ATTRIB}{'model'} && $self->{$Package}{ATTRIB}{'model'} =~ /9030/ ? 115200 : undef);
4047 0 0         $self->_setAttrib('switch_type', $1) if $$outref =~ /switchType: (\d+)/g;
4048 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{slx}}/;
4049 0           $self->_setDevicePrompts($Prm{slx}, $1);
4050 0           return (1, $Prm{slx});
4051             }
4052             }
4053 0 0         if ($discDevice->{stage} < 6) { # Next stage
4054             # ExtremeXOS detection command
4055 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show version | include "by release-manager"');
4056 0 0         return $ok unless $ok;
4057 0           $discDevice->{stage}++; # Move to next stage on next cycle
4058 0 0         if ($$outref =~ /^Image : Extreme(XOS| Networks Switch Engine) version (.+) by /m) {
4059 0 0         my $switchEngine = $1 eq 'XOS' ? 0 : 1;
4060 0           $self->_setFamilyTypeAttrib($Prm{xos}, is_nncli => 0, is_xos => 1, is_switch_engine => $switchEngine, sw_version => $2);
4061 0 0         $self->_setAttrib('fw_version', $1) if $$outref =~ /^BootROM :(.+)$/m;
4062 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{xos}}/;
4063 0           $self->_setDevicePrompts($Prm{xos}, $1);
4064 0           return (1, $Prm{xos});
4065             }
4066             }
4067 0 0         if ($discDevice->{stage} < 7) { # Next stage
4068             # ISW detection command
4069 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'do show version | include ISW'); # Must add do, as we may be in config mode
4070 0 0         return $ok unless $ok;
4071 0           $discDevice->{stage}++; # Move to next stage on next cycle
4072 0 0         if ($$outref =~ /^(Product |Board Type) : (.+)(?:, PoE Switch)?/m) {
4073 0           my $model = $1;
4074 0           $self->_setFamilyTypeAttrib($Prm{isw}, is_nncli => 1, is_isw => 1, baudrate => 115200);
4075 0           $self->_setModelAttrib($model);
4076 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{isw}}/;
4077 0           $self->_setDevicePrompts($Prm{isw}, $1);
4078 0           return (1, $Prm{isw});
4079             }
4080             }
4081 0 0         if ($discDevice->{stage} < 8) { # Next stage
4082             # Wing detection command
4083 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show version | include version|Extreme|MAC|uptime');
4084 0 0         return $ok unless $ok;
4085 0           $discDevice->{stage}++; # Move to next stage on next cycle
4086 0 0         if ($$outref =~ /^(\S+) version (.+)\nCopyright \(c\) [\d-]+ Extreme Networks/m) {
4087 0           my $model = $1;
4088 0           $self->_setFamilyTypeAttrib($Prm{wing}, is_nncli => 1, is_wing => 1, baudrate => 115200, sw_version => $2, fw_version => undef);
4089 0           $self->_setModelAttrib($model);
4090 0 0         $self->_setAttrib('sysname', $1) if $$outref =~ /^(\S+) uptime is/m;
4091 0 0         $self->_setBaseMacAttrib($1) if $$outref =~ /^Base ethernet MAC address is (.+)$/m;
4092 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{wing}}/;
4093 0           $self->_setDevicePrompts($Prm{wing}, $1);
4094 0           return (1, $Prm{wing});
4095             }
4096             }
4097 0 0         if ($discDevice->{stage} < 9) { # Next stage
4098             # HiveOS detection command
4099 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show version');
4100 0 0         return $ok unless $ok;
4101 0           $discDevice->{stage}++; # Move to next stage on next cycle
4102 0 0         if ($$outref =~ /^Version: HiveOS (\S+) build-\d+/m) {
4103 0           $self->_setFamilyTypeAttrib($Prm{hive}, is_nncli => 0, is_hiveos => 1, baudrate => 9600, sw_version => $1);
4104 0 0         $self->_setModelAttrib($1) if $$outref =~ /^Platform:\s+(\S+)/m;
4105 0 0         $self->_setAttrib('fw_version', $1) if $$outref =~ /^Bootloader ver: v(\S+)/m;
4106 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{hive}}/;
4107 0           $self->_setDevicePrompts($Prm{hive}, $1);
4108 0           return (1, $Prm{hive});
4109             }
4110             }
4111 0 0         if ($discDevice->{stage} < 10) { # Next stage
4112             # Ipanema detection command
4113 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'ipconfig -d');
4114 0 0         return $ok unless $ok;
4115 0           $discDevice->{stage}++; # Move to next stage on next cycle
4116 0 0         if ($$outref =~ /^ \[LAN\] IPaddr : /m) {
4117 0           $self->_setFamilyTypeAttrib($Prm{ipanema}, is_nncli => 0, is_sdwan => 1, baudrate => 9600);
4118 0 0         $self->_setAttrib('sysname', $1) if $$outref =~ /^\s+Hostname\s+: (\S+)/m;
4119 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{ipanema}}/;
4120 0           $self->_setDevicePrompts($Prm{ipanema}, $1);
4121 0           return (1, $Prm{ipanema});
4122             }
4123             }
4124 0 0         if ($discDevice->{stage} < 11) { # Next stage
4125             # Series200 detection command
4126 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show slot');
4127 0 0         return $ok unless $ok;
4128 0           $discDevice->{stage}++; # Move to next stage on next cycle
4129 0 0         if ($$outref =~ /^\d(?:\/\d)?\s+\S+\s+\S+\s+\S+\s+Extreme\s+(\S+)/m) {
4130 0           my $model = $1;
4131 0           $self->_setFamilyTypeAttrib($Prm{s200}, is_nncli => 1);
4132 0           $self->_setModelAttrib($model);
4133 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{s200}}/;
4134 0           $self->_setDevicePrompts($Prm{s200}, $1);
4135 0           return (1, $Prm{s200});
4136             }
4137             }
4138 0 0         if ($discDevice->{stage} < 12) { # Next stage
4139             # PassportERS-cli detection command
4140 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show bootconfig info');
4141 0 0         return $ok unless $ok;
4142 0           $discDevice->{stage}++; # Move to next stage on next cycle
4143 0 0         if ($$outref =~ /^Version:\s+(?i:v|REL)?(.+)/m) {
4144 0           my $version = $1;
4145 0           $self->_setFamilyTypeAttrib($Prm{pers}, is_nncli => 0, is_master_cpu => 1);
4146 0           $self->_setAttrib('fw_version', $version);
4147 0           $self->{LASTPROMPT} =~ /$InitPrompt{"$Prm{pers}_cli"}/;
4148 0           $self->_setDevicePrompts("$Prm{pers}_cli", $1);
4149 0           return (1, $Prm{pers});
4150             }
4151             }
4152 0 0         if ($discDevice->{stage} < 13) { # Next stage
4153             # WLAN 9100 detection command
4154 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show contact-info');
4155 0 0         return $ok unless $ok;
4156 0           $discDevice->{stage}++; # Move to next stage on next cycle
4157 0 0         if ($$outref =~ /^Access Point Hostname\s*(.+)$/m) {
4158 0           my $sysname = $1;
4159 0           $self->_setFamilyTypeAttrib($Prm{xirrus}, is_nncli => 1);
4160 0           $self->_setAttrib('sysname', $sysname);
4161 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{xirrus}}/;
4162 0           $self->_setDevicePrompts($Prm{xirrus}, $1);
4163 0           return (1, $Prm{xirrus});
4164             }
4165             }
4166 0 0         if ($discDevice->{stage} < 14) { # Next stage
4167             # Secure Router detection command
4168 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show chassis');
4169 0 0         return $ok unless $ok;
4170 0           $discDevice->{stage}++; # Move to next stage on next cycle
4171 0 0         if ($$outref =~ /^Chassis Model: (.+)$/m) {
4172 0           my $model = $1;
4173 0           $self->_setFamilyTypeAttrib($Prm{sr}, is_nncli => 1);
4174 0           $self->_setModelAttrib($model);
4175 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{sr}}/;
4176 0           $self->_setDevicePrompts($Prm{sr}, $1);
4177 0           return (1, $Prm{sr});
4178             }
4179             }
4180 0 0         if ($discDevice->{stage} < 15) { # Next stage
4181             # WLAN 2300 detection command
4182 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show system');
4183 0 0         return $ok unless $ok;
4184 0           $discDevice->{stage}++; # Move to next stage on next cycle
4185 0 0         if ($$outref =~ /Product Name:\s+(.+)/g) {
4186 0           my $model = $1;
4187 0           $self->_setFamilyTypeAttrib($Prm{trpz}, is_nncli => 1);
4188 0           $self->_setModelAttrib($model);
4189 0 0         $$outref =~ /System Name:\s+(.+)/g && $self->_setAttrib('sysname', $1);
4190 0 0         $$outref =~ /System MAC:\s+(.+)/g && $self->_setBaseMacAttrib($1);
4191 0           $self->{LASTPROMPT} =~ /$InitPrompt{$Prm{trpz}}/;
4192 0           $self->_setDevicePrompts($Prm{trpz}, $1);
4193 0           return (1, $Prm{trpz});
4194             }
4195             }
4196 0 0         if ($discDevice->{stage} < 16) { # Next stage
4197             # Accelar detection command
4198 0           my ($ok, $outref) = $self->poll_cmd($pkgsub, 'show sys perf');
4199 0 0         return $ok unless $ok;
4200 0           $discDevice->{stage}++; # Move to next stage on next cycle
4201 0 0         if ($$outref =~ /^\s+NVRamSize:/m) {
4202 0           $self->_setFamilyTypeAttrib($Prm{xlr}, is_nncli => 0, is_master_cpu => 1);
4203 0           $self->{$Package}{PROMPTTYPE} = $Prm{xlr};
4204 0           $self->_setDevicePrompts($Prm{xlr}, $1);
4205 0           return (1, $Prm{xlr});
4206             }
4207             }
4208              
4209             # We give up; set as generic device
4210 0           $self->_setFamilyTypeAttrib($Prm{generic}, is_nncli => 0);
4211 0           $self->_setDevicePrompts($Prm{generic});
4212 0           return (1, $Prm{generic});
4213             }
4214              
4215              
4216             sub debugMsg { # Print a debug message
4217 0     0 1   my $self = shift;
4218 0 0         if (shift() & $self->{debug}) {
4219 0           my $string1 = shift();
4220 0   0       my $stringRef = shift() || \"";#" Ultraedit hack!
4221 0   0       my $string2 = shift() || "";
4222 0 0         if ($self->{$Package}{DEBUGLOGFH}) {
4223 0           print {$self->{$Package}{DEBUGLOGFH}} $string1, $$stringRef, $string2;
  0            
4224             }
4225             else {
4226 0           print $string1, $$stringRef, $string2;
4227             }
4228             }
4229 0           return;
4230             }
4231              
4232              
4233             ########################################## Internal Private Methods ##########################################
4234              
4235             sub _attribExecuteCmd { # Executes commands for attribute retrieval
4236 0     0     my ($self, $pkgsub, $attrib, $cmd, $pages, $cmdConfig) = @_;
4237 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
4238              
4239 0 0         unless ($attrib->{debugMsg}) {
4240 0 0         my $cmdInfo = $#$cmd ? $cmd->[$self->{$Package}{ATTRIB}{'is_nncli'}] : $cmd->[0];
4241 0 0         my $pageInfo = defined $pages ? "($pages pages)" : '';
4242 0           $self->debugMsg(4,"Seeking attribute $attrib->{attribute} value by issuing command: ", \$cmdInfo, " $pageInfo\n");
4243 0           $attrib->{debugMsg} = 1;
4244             }
4245 0 0 0       return $self->cmdConfig($pkgsub, $cmd->[0], $cmd->[1]) if $#$cmd && $cmdConfig;
4246 0 0         return $self->cmdPrivExec($pkgsub, $cmd->[0], $cmd->[1], $pages) if $#$cmd;
4247 0 0         return $self->cmdIpanema($pkgsub, $cmd->[0], $pages) if $familyType eq $Prm{ipanema};
4248 0           return $self->poll_cmd($pkgsub, $cmd->[0], $pages);
4249             }
4250              
4251              
4252             sub _setDevicePrompts { # Steps to set the actual device prompt & more prompt
4253 0     0     my ($self, $keyType, $actualPrompt) = @_;
4254 0           my $setPrompt;
4255              
4256 0 0         if (defined $keyType) {
4257 0           $self->{$Package}{PROMPTTYPE} = $keyType;
4258 0           $self->debugMsg(4,"setDevicePrompts() Prompt type = $self->{$Package}{PROMPTTYPE}\n");
4259             }
4260             else {
4261 0           $keyType = $self->{$Package}{PROMPTTYPE};
4262             }
4263 0           $setPrompt = $Prompt{$keyType};
4264 0 0         if ($actualPrompt) { # Generic prompt will skip this
4265             # If Perl's metacharacters are used in the switch prompt, backslash them not to mess up prompt regex
4266 0           $actualPrompt =~ s/([\{\}\[\]\(\)\^\$\.\|\*\+\?\\])/\\$1/g;
4267 0           $setPrompt =~ s/SWITCHNAME/$actualPrompt/g;
4268 0           $self->debugMsg(4,"setDevicePrompts() Embedding in prompt switch name = '$actualPrompt'\n");
4269             }
4270 0           $self->prompt($setPrompt);
4271 0 0         $self->more_prompt($MorePrompt{$keyType}, defined $MorePromptDelay{$keyType} ? $MorePromptDelay{$keyType} : undef);
4272 0 0         $self->no_refresh_cmd($RefreshCommands{$keyType}{pattern}, $RefreshCommands{$keyType}{send}) if defined $RefreshCommands{$keyType};
4273 0           return;
4274             }
4275              
4276              
4277             sub _setLastPromptAndConfigContext { # Sets the LASTPROMPT and package CONFIGCONTEXT keys
4278 0     0     my ($self, $capturedPrompt, $configContext) = @_;
4279 0           ($self->{LASTPROMPT} = $capturedPrompt) =~ s/$LastPromptClense//o;
4280 0           $self->debugMsg(4,"_setLastPromptAndConfigContext() LASTPROMPT = >$self->{LASTPROMPT}<\n");
4281 0 0         if (defined $configContext) {
4282 0           $self->{$Package}{CONFIGCONTEXT} = $configContext;
4283             }
4284             else { # With generic prompt or the one used by discoverDevice() it will be undefined, so we need extract it from last prompt
4285 0           my $match;
4286 0           for my $regex (@PromptConfigContext) {
4287 0 0         if ($self->{LASTPROMPT} =~ /$regex/) {
4288 0           $self->{$Package}{CONFIGCONTEXT} = $1;
4289 0           $match = 1;
4290 0           last;
4291             }
4292             }
4293 0 0         $self->{$Package}{CONFIGCONTEXT} = '' unless $match;
4294             }
4295 0           $self->debugMsg(4,"_setLastPromptAndConfigContext() CONFIGCONTEXT = >$self->{$Package}{CONFIGCONTEXT}<\n");
4296 0           $self->{WRITEFLAG} = 0;
4297             }
4298              
4299              
4300             sub _setFamilyTypeAttrib { # Set family_type attribute and other related settings
4301 0     0     my ($self, $family, %attribList) = @_;
4302 0           $self->_setAttrib('family_type', $family);
4303 0 0         if (defined $Attribute{$family}) {
4304 0           $self->_setAttrib('all', [@{$Attribute{Global}}, @{$Attribute{$family}}]);
  0            
  0            
4305 0           $self->debugMsg(4,"Attribute - all = Global + $family attributes\n");
4306             }
4307             else {
4308 0           $self->_setAttrib('all', $Attribute{Global});
4309 0           $self->debugMsg(4,"Attribute - all = Global only\n");
4310             }
4311 0 0         if (%attribList) { # Set other fixed value attributes
4312 0           foreach my $attrib (keys %attribList) {
4313 0           $self->_setAttrib($attrib, $attribList{$attrib});
4314             }
4315             }
4316 0           return;
4317             }
4318              
4319              
4320             sub _setSlotPortAttrib { # Set the Slot & Port attributes
4321 0     0     my ($self, $outref) = @_;
4322 0           my (@slots, @ports, $currentSlot);
4323             # Get current attribute if partly stored
4324 0 0         @slots = @{$self->{$Package}{ATTRIB}{'slots'}} if $self->{$Package}{ATTRIBFLAG}{'slots'};
  0            
4325 0 0         @ports = @{$self->{$Package}{ATTRIB}{'ports'}} if $self->{$Package}{ATTRIBFLAG}{'ports'};
  0            
4326 0           my $slotRegex = '\d{1,3}';
4327 0           while ($$outref =~ /^(?:\s*|interface\s+ethernet|Eth )?(?:($slotRegex)[\/:])?((?:\d{1,3}|(?:gig|ge|fe|Eth|lan|wan)\d|s\d)(?:[\/:]\d{1,2})?)/mg) {
4328 0 0 0       if (defined $1 && (!defined $currentSlot || $1 != $currentSlot)) { # New slot
      0        
4329 0           $currentSlot = $1;
4330 0 0         push(@slots, $currentSlot) unless grep {$_ eq $currentSlot} @slots;
  0            
4331             }
4332 0 0         if (defined $currentSlot) {
4333 0 0         push(@{$ports[$currentSlot]}, lc($2)) unless grep {$_ eq lc($2)} @{$ports[$currentSlot]};
  0            
  0            
  0            
4334             }
4335             else {
4336 0 0         push(@ports, lc($2)) unless grep {$_ eq lc($2)} @ports;
  0            
4337             }
4338 0 0         $slotRegex = '' unless defined $1; # Exos 5720 new channelized port format on standalone unit
4339             }
4340 0           @slots = sort {$a <=> $b} @slots; # Slots might need re-arranging on older swithes with fastEther & gigEther ports
  0            
4341 0           $self->_setAttrib('slots', \@slots);
4342 0           $self->_setAttrib('ports', \@ports);
4343 0           return;
4344             }
4345              
4346              
4347             sub _setSlotPortHashAttrib { # Set the Slot & Port attributes where the port attribute is a hash (not an array)
4348 0     0     my ($self, $outref) = @_;
4349 0           my (@slots, %ports, $currentHash);
4350             # Get current attribute if partly stored
4351 0 0         @slots = @{$self->{$Package}{ATTRIB}{'slots'}} if $self->{$Package}{ATTRIBFLAG}{'slots'};
  0            
4352 0 0         %ports = %{$self->{$Package}{ATTRIB}{'ports'}} if $self->{$Package}{ATTRIBFLAG}{'ports'};
  0            
4353 0           while ($$outref =~ /^(FastEthernet|(?:\d+)?GigabitEthernet) (\d\/\d{1,2})/mg) {
4354 0 0 0       if (!defined $currentHash || $1 ne $currentHash) { # New hash
4355 0           $currentHash = $1;
4356 0 0         push(@slots, $currentHash) unless grep {$_ eq $currentHash} @slots;
  0            
4357             }
4358 0 0         push(@{$ports{$currentHash}}, $2) unless grep {$_ eq $2} @{$ports{$currentHash}};
  0            
  0            
  0            
4359             }
4360 0           @slots = sort {$a cmp $b} @slots; # Slots might need re-arranging on older swithes with fastEther & gigEther ports
  0            
4361 0           $self->_setAttrib('slots', \@slots);
4362 0           $self->_setAttrib('ports', \%ports);
4363 0           return;
4364             }
4365              
4366              
4367             sub _setModelAttrib { # Set & re-format the Model attribute
4368 0     0     my ($self, $model) = @_;
4369              
4370 0           $model =~ s/\s+$//; # Remove trailing spaces
4371 0           $model =~ s/^\s+//; # Remove leading spaces
4372              
4373 0 0         if ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{bstk}) {
    0          
    0          
    0          
    0          
    0          
    0          
4374             # Try and reformat the model number into something like ERS-5510
4375 0           $model =~ s/Ethernet Routing Switch /ERS-/;
4376 0           $model =~ s/Ethernet Switch /ES-/;
4377 0           $model =~ s/Business Policy Switch /BPS-/;
4378 0           $model =~ s/Wireless LAN Controller WC/WC-/;
4379 0           $model =~ s/Virtual Services Platform /VSP-/;
4380 0           $model =~ s/(-\d{3,})([A-Z])/$1-$2/;
4381             }
4382             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{pers}) {
4383 0           $model =~ s/(-\d{3,})([A-Z])/$1-$2/;
4384             }
4385             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{sr}) {
4386             # Try and reformat the model number into something like SR-4134
4387 0           $model =~ s/SR(\d+)/SR-$1/; # From show chassis
4388 0           $model =~ s/Secure Router /SR-/; # From banner
4389             }
4390             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{trpz}) {
4391             # Try and reformat the model number into something like WSS-2380
4392 0           $model = 'WSS-' . $model;
4393             }
4394             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{xirrus}) {
4395             # Try and reformat the model number into something like WAP-9132
4396 0           $model =~ s/(\D+)(\d+)/$1-$2/; # From show chassis
4397             }
4398             elsif ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{isw}) {
4399             # Try and reformat the model number into something like ISW_8-10/100P_4-SFP
4400 0           $model =~ s/, (?:PoE )?Switch$//;
4401 0           $model =~ s/ /_/g; # From: ISW 8-10/100P, 4-SFP
4402 0           $model =~ s/,//g; # From: ISW_8-10/100P_4-SFP
4403             }
4404             elsif ($self->{$Package}{ATTRIB}{'is_apls'}) {
4405             # Try and reformat from DSG6248CFP to DSG-6248-CFP
4406 0           $model =~ s/^([A-Z]{3})(\d{3,})/$1-$2/;
4407 0           $model =~ s/(-\d{3,})([A-Z])/$1-$2/;
4408             }
4409 0           $self->_setAttrib('model', $model);
4410              
4411             # VOSS is a PassportERS with a VSP model name
4412 0 0         if ($self->{$Package}{ATTRIB}{'family_type'} eq $Prm{pers}) {
4413             # Requires is_apls to always be set before is_voss
4414 0 0 0       if ($model =~ /FabricEngine$/) {
    0 0        
4415 0           $self->_setAttrib('is_voss', 1);
4416 0           $self->_setAttrib('is_fabric_engine', 1);
4417             }
4418             elsif ($self->{$Package}{ATTRIB}{'is_apls'} || $model =~ /^(?:VSP|XA)/ || $model =~ /VOSS$/) {
4419 0           $self->_setAttrib('is_voss', 1);
4420 0           $self->_setAttrib('is_fabric_engine', 0);
4421             }
4422             else {
4423 0           $self->_setAttrib('is_voss', 0);
4424 0           $self->_setAttrib('is_fabric_engine', 0);
4425             }
4426             }
4427 0           return;
4428             }
4429              
4430              
4431             sub _setBoxTypeAttrib { # Set & re-format the APLS BoxType attribute
4432 0     0     my ($self, $boxType) = @_;
4433              
4434 0           $boxType =~ s/\s+$//; # Remove trailing spaces
4435 0           $boxType =~ s/^\s+//; # Remove leading spaces
4436              
4437 0           $boxType =~ s/^([A-Z]{3})(\d{3,})/$1-$2/;
4438 0           $boxType =~ s/(-\d{3,})([A-Z])/$1-$2/;
4439 0           $self->_setAttrib('apls_box_type', $boxType);
4440 0           return;
4441             }
4442              
4443              
4444             sub _setBaseMacAttrib { # Set & re-format the Base_Mac attribute
4445 0     0     my ($self, $mac) = @_;
4446              
4447 0           $mac =~ s/\s+$//; # Remove trailing spaces
4448 0           $mac =~ s/^\s+//; # Remove leading spaces
4449              
4450             # Reformat the MAC from xx:xx:xx:xx:xx:xx to xx-xx-xx-xx-xx-xx
4451 0           $mac =~ s/:/-/g;
4452              
4453             # Reformat the MAC from xxxx-xxxx-xxxx to xx-xx-xx-xx-xx-xx
4454 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/;
4455              
4456             # Reformat the MAC from xxxxxxxxxxxx to xx-xx-xx-xx-xx-xx
4457 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/;
4458              
4459 0           $self->_setAttrib('base_mac', $mac);
4460 0           return;
4461             }
4462              
4463              
4464             sub _setAttrib { # Set attribute
4465 0     0     my ($self, $attrib, $value) = @_;
4466 0 0 0       if ($attrib eq 'is_nncli' || $attrib eq 'is_acli') {
4467 0           $self->{$Package}{ATTRIB}{'is_nncli'} = $value;
4468 0           $self->{$Package}{ATTRIBFLAG}{'is_nncli'} = 1;
4469 0           $self->{$Package}{ATTRIB}{'is_acli'} = $value;
4470 0           $self->{$Package}{ATTRIBFLAG}{'is_acli'} = 1;
4471             }
4472             else {
4473 0           $self->{$Package}{ATTRIB}{$attrib} = $value;
4474 0           $self->{$Package}{ATTRIBFLAG}{$attrib} = 1;
4475             }
4476 0 0         if (defined $value) {
4477 0           $self->debugMsg(4,"Attribute - $attrib => $value\n");
4478             }
4479             else {
4480 0           $self->debugMsg(4,"Attribute - $attrib => undef\n");
4481             }
4482 0           return;
4483             }
4484              
4485              
4486             sub _determineOutcome { # Determine if an error message was returned by host
4487 0     0     my ($self, $outref, $lastPromptEchoedCmd) = @_;
4488 0           my $familyType;
4489              
4490 0 0         return unless $familyType = $self->{$Package}{ATTRIB}{'family_type'};
4491 0 0         return if $familyType eq $Prm{generic};
4492 0 0         if ($$outref =~ /$ErrorPatterns{$familyType}/m) {
4493 0           (my $errmsg = $1) =~ s/\x07//g; # Suppress bell chars if any
4494 0           $self->debugMsg(4,"\ncmd() Detected error message from host:\n", \$errmsg, "\n");
4495 0           $self->{$Package}{last_cmd_errmsg} = $lastPromptEchoedCmd . $errmsg;
4496 0           return $self->{$Package}{last_cmd_success} = 0;
4497             }
4498             else {
4499 0           return $self->{$Package}{last_cmd_success} = 1;
4500             }
4501             }
4502              
4503              
4504             sub _restoreDeviceBaudrate { # Check done in disconnect and DESTROY to restore device baudrate before quiting
4505 0     0     my $self = shift;
4506 0   0       my $familyType = $self->{$Package}{ATTRIB}{'family_type'} || '';
4507             # If change_bauderate() was called and serial connection still up...
4508 0 0 0       if (defined $self->baudrate && defined (my $origBaud = $self->{$Package}{ORIGBAUDRATE}) ) {
4509             # ...try and restore original baudrate on device before quiting
4510 0 0         if ($familyType eq $Prm{bstk}) {
    0          
4511 0           $self->errmode('return');
4512 0           $self->put($CTRL_C);
4513 0           $self->print("terminal speed $origBaud");
4514             }
4515             elsif ($familyType eq $Prm{pers}) {
4516 0           $self->errmode('return');
4517 0 0         if ($self->{$Package}{ATTRIB}{'is_nncli'}) {
4518 0           $self->printlist('enable', 'config term', "boot config sio console baud $origBaud");
4519             }
4520             else {
4521 0           $self->print("config bootconfig sio console baud $origBaud");
4522             }
4523             }
4524             }
4525 0           return 1;
4526             }
4527              
4528              
4529             sub _printDot {
4530 0     0     local $| = 1; # Flush STDOUT buffer
4531 0           print '.';
4532 0           return;
4533             }
4534              
4535              
4536             1;
4537             __END__;