File Coverage

blib/lib/Control/CLI/Extreme.pm
Criterion Covered Total %
statement 30 2452 1.2
branch 12 2348 0.5
condition 1 755 0.1
subroutine 6 67 8.9
pod 49 49 100.0
total 98 5671 1.7


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