File Coverage

blib/lib/Control/CLI/Extreme.pm
Criterion Covered Total %
statement 30 2526 1.1
branch 12 2440 0.4
condition 1 833 0.1
subroutine 6 67 8.9
pod 49 49 100.0
total 98 5915 1.6


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