Class: CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/netutils/cli.rb

Defined Under Namespace

Modules: Maker

Constant Summary collapse

FIRST_PROMPT_RE =
/^.*\n+\(?!?([^\r\n\(\)\s>#]+)\)? ?([>#]) ?\r?\n?.*$/m
TIMEOUT =
30
LOGIN_TIMEOUT =
3
PRODUCT_DETECTION_MAXRETRIES =
3

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, ia, types = CLI_SESSION_TYPES, maker = Maker::UNKNOWN) ⇒ CLI

Returns a new instance of CLI.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/netutils/cli.rb', line 46

def initialize(name, ia, types = CLI_SESSION_TYPES, maker = Maker::UNKNOWN)
	@ia = ia
	@users = USERS.dup
	@passwds = PASSWORDS.dup
	@enable_passwds = ENABLES.dup
	@enabled = false
	@type = nil
	@types = types
	@session = nil
	@maker = maker
	@product = nil
	@cr = ''
	@name_supplied = name
	name_update(nil)
	@userprompt = [
	    'login:',		# Alaxala
	    '[Uu]sername:',	# Cisco
	    'User:'		# Cisco WiSM/WLC
	    ]
	# Cisco has trailing space, ``Password: '', but Alaxala not
	@passwdprompt = 'Password:'
	@telnetopt = Hash.new
	@telnetopt['Timeout' ] = TIMEOUT
	if defined?(LOGDIR)
		path = File.dirname(__FILE__)
		path += "/../../"
		path += "/#{LOGDIR}/#{ia}.log"
		path = File.expand_path(path).untaint
		@telnetopt['Output_log'] = path
	end
	#@telnetopt['Dump_log'] = '/dev/stdout'
end

Instance Attribute Details

#enable_passwdsObject (readonly)

Returns the value of attribute enable_passwds.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def enable_passwds
  @enable_passwds
end

#iaObject (readonly)

Returns the value of attribute ia.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def ia
  @ia
end

#makerObject (readonly)

Returns the value of attribute maker.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def maker
  @maker
end

#nameObject (readonly)

Returns the value of attribute name.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def name
  @name
end

#passwdsObject (readonly)

Returns the value of attribute passwds.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def passwds
  @passwds
end

#productObject (readonly)

Returns the value of attribute product.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def product
  @product
end

#promptObject (readonly)

Returns the value of attribute prompt.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def prompt
  @prompt
end

#usersObject (readonly)

Returns the value of attribute users.



43
44
45
# File 'lib/netutils/cli.rb', line 43

def users
  @users
end

Instance Method Details

#acl_add(type, name, addr, seq = nil) ⇒ Object



657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
# File 'lib/netutils/cli.rb', line 657

def acl_add(type, name, addr, seq = nil)
	case type
	when 'ip'
		# XXX: we may need sanity check.
	when 'mac', 'advance'
		addr = MACAddr.new(addr)
	else
		raise("Unknown ACL type: #type")
	end
	cmd = acl_type_to_cmd(type)
	configure
	cmd(acl_definition(type, name))
	cmd("#{seq} deny #{cmd} host #{addr} any")
	unconfigure
end

#acl_delete(type, name, seq) ⇒ Object



673
674
675
676
677
678
# File 'lib/netutils/cli.rb', line 673

def acl_delete(type, name, seq)
	configure
	cmd(acl_definition(type, name))
	cmd("no #{seq}")
	unconfigure
end

#acl_exists?(type, name) ⇒ Boolean

Returns:

  • (Boolean)


650
651
652
653
654
655
# File 'lib/netutils/cli.rb', line 650

def acl_exists?(type, name)
	configure
	filters = cmd("show #{acl_definition(type, name)}")
	unconfigure
	! filters.empty?
end

#arp_resolve(ia, vrf) ⇒ Object



600
601
602
603
604
605
606
607
608
609
# File 'lib/netutils/cli.rb', line 600

def arp_resolve(ia, vrf)
	if vrf.name == 'default'
		output = cmd("show ip arp #{ia}")
	else
		output = cmd("show ip arp vrf #{vrf.name} #{ia}")
	end
	a = _new(:ShowARP)
	a.parse(output)
	return a.arps[ia]
end

#cmd(s, nextprompt = nil, ignoreerror = false) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/netutils/cli.rb', line 215

def cmd(s, nextprompt = nil, ignoreerror = false)
	re = [ @prompt ]
	re.push('#') if @maker == Maker::UNKNOWN # XXX dirty hack for now...
	re.push(nextprompt) if nextprompt
	re = '(?:' + re.join('|') + ')'
	r = @session.cmd('String' => s + @cr, 'Match' => /#{re}\Z/)
	r = handle_control_characters(r)
	#
	# XXX: allows a command not to be echo-ed like FTP server.
	#
	if r !~ /^#{re}?(?:#{s.sub('*', '\\*')})?\n+(.*)#{re}\Z/m
		raise(ArgumentError, "CLI output error: \"#{r}\"")
	end
	r = $1
	r.slice!(-1) if @maker === Maker::ALAXALA && r[-1] === '!'
	if ignoreerror === false && error?(r)
		raise(ArgumentError,
		    "Command failed on #{@name}: #{s}: #{r}")
	end
	@prompt = nextprompt if nextprompt
	return r
end

#config_getObject



568
569
570
571
572
573
574
575
# File 'lib/netutils/cli.rb', line 568

def config_get
	enable
	re = Module.const_get(maker_to_s).const_get('CONFIG_RE')
	if show_running_config !~ re
		raise("Invalid configuration format for #{@name}")
	end
	return $1
end

#configureObject



557
558
559
560
561
# File 'lib/netutils/cli.rb', line 557

def configure
	raise "already configuring" if @prompt == @configprompt
	enable
	return cmd('configure terminal', @configprompt)
end

#enableObject



550
551
552
553
554
555
# File 'lib/netutils/cli.rb', line 550

def enable
	return if @enabled
	passwd(nil, @enable_passwds.dup, @enable_passwds.dup,
	    @enableprompt.dup, 'enable')
	@enabled = true
end

#error?(r) ⇒ Boolean

Returns:

  • (Boolean)


189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/netutils/cli.rb', line 189

def error?(r)
	case r
	when /%[^\n]+\n+\Z/m # XXX: this is not accurate
		maker = Maker::CISCO
	when /Error: Bad command\. \n\Z/m,
	     /[^\s]+: not found\n\Z/m,
	     /% The command or parameter at the ^ marker is invalid\./m,
	     /Error: Invalid parameter\./m
		maker = Maker::ALAXALA
	when /Invalid syntax\..*\Z/m, /Unknown command: .*\Z/m
		maker = Maker::PALOALTO
	when /^ *\^ *\n% [^\n]+ error/m,
	     /^ *\^ *\n% Invalid input detected at/m	# same as Cisco
		maker = Maker::ARUBA
	else
		return false
	end
	#maker_update(maker)
	return true
end

#interface_gets(sw) ⇒ Object



617
618
619
620
621
622
623
624
625
626
# File 'lib/netutils/cli.rb', line 617

def interface_gets(sw)
	i = _new(:Interface, sw)
	i.parse(cmd(i.cmd))
	#
	# XXX: hack for Cisco because Cisco cannot obtain up/down of
	#      an interface with interfaces capability command...
	#
	is = _new(:IfSummary, sw)
	is.parse(cmd(is.cmd)) if is
end

#interface_name(port) ⇒ Object



628
629
630
# File 'lib/netutils/cli.rb', line 628

def interface_name(port)
	port
end

#interface_name_cli(port) ⇒ Object



632
633
634
# File 'lib/netutils/cli.rb', line 632

def interface_name_cli(port)
	port
end

#interface_noshutdown(port) ⇒ Object



643
644
645
646
647
648
# File 'lib/netutils/cli.rb', line 643

def interface_noshutdown(port)
	configure
	cmd("interface #{port}")
	cmd('no shutdown')
	unconfigure
end

#interface_shutdown(port) ⇒ Object



636
637
638
639
640
641
# File 'lib/netutils/cli.rb', line 636

def interface_shutdown(port)
	configure
	cmd("interface #{port}")
	cmd('shutdown')
	unconfigure
end

#loginObject



375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/netutils/cli.rb', line 375

def 
	users = @users.dup
	passwds = @passwds.dup
	@type = @types[0]
	begin
		r = send("login_#{@type}", users, passwds)
	rescue Errno::ECONNREFUSED => e
		raise e if @type === @types.last
		@type = @types[@types.index(@type) + 1]
		retry
	end
	r = handle_control_characters(r)
	if r =~ FIRST_PROMPT_RE
		name_update($1)
		if $2 === '#'
			@enabled = true
		end
	end
	if r =~ /NEC Corporation.*OpenROUTE.*J\. Noel Chiappa/m
		maker_update(Maker::NEC)
	end
	product_detect
end

#logoutObject



480
481
482
# File 'lib/netutils/cli.rb', line 480

def logout
	@session.close
end

#mac_address_table_get(sw, ma, vlan) ⇒ Object



611
612
613
614
615
# File 'lib/netutils/cli.rb', line 611

def mac_address_table_get(sw, ma, vlan)
	fib = _new(:MACFIB, sw)
	fib.parse(cmd(fib.cmd(ma, vlan)))
	return fib.ports
end

#maker_to_sObject



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/netutils/cli.rb', line 445

def maker_to_s
	# XXX: smarter way please...
	case @maker
	when Maker::CISCO
		'Cisco'
	when Maker::WLC
		'WLC'
	when Maker::ALAXALA
		'Alaxala'
	when Maker::PALOALTO
		'Paloalto'
	when Maker::ARUBA
		'Aruba'
	when Maker::NEC
		'NEC'
	else
		'Unknown'
	end
end

#name_update(name) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/netutils/cli.rb', line 79

def name_update(name)
	if name =~ /^([^@]*)@(.*)$/
		@user = $1
		name = $2
	end
	raise "Invalid host name given: \"#{name}\"" if name =~ /[>#\n]/
	if @name === nil && name != nil &&
	   @name_supplied != nil && name != @name_supplied
		raise(ArgumentError, "host name mismatch: " +
		    "\"#{@name_supplied}\" is supplied " +
		    "but actually \"#{name}\"")
	end
	@name = name
	case @maker
	when Maker::CISCO
		prefix = suffix = trailer = ''
	when Maker::ALAXALA
		prefix = '!?'
		suffix = ''
		trailer = "\0? "
	when Maker::PALOALTO
		raise('Invalid host name for Paloalto') if ! @user
		prefix = "#{@user}@"
		suffix = ''
		trailer = ' '
	when Maker::ARUBA
		prefix  = '\('
		suffix  = '\) '
		trailer = ''
	else
		prefix  = '\(?'
		if @user
			prefix = "#{prefix}#{@user}@"
		else
			prefix = "#{prefix}.*"
		end
		suffix  = '\)? ?'
		trailer = "\0? ?"
	end
	name = '[^\r\n\(\)]+' if ! name
	@normalprompt = "#{prefix}#{name}#{suffix}>#{trailer}"
	@enableprompt = "#{prefix}#{name}#{suffix}##{trailer}"
	@configprompt = "#{prefix}#{name}#{suffix}\\(config[^ ]*\\)##{trailer}"
	if @enabled
		@prompt = @enableprompt
	else
		# XXX configuring node... dirty....
		@prompt = @normalprompt
	end
end

#neighbor_gets(sw, port = nil) ⇒ Object



680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
# File 'lib/netutils/cli.rb', line 680

def neighbor_gets(sw, port = nil)
	c = _new(:CDP, sw)
	if c
		begin
			c.parse(cmd(c.cmd(port)))
			return c.rsw if port && c.rsw
		rescue ArgumentError => e
			if e.to_s !~ /% CDP is not enabled/
				raise e
			end
		end
	end
	l = _new(:LLDP, sw)
	if l
		l.parse(cmd(l.cmd(port)))
		return l.rsw if port && l.rsw
	end
	if ! c && ! l
		raise 'No method found for retrieving a neighbor!!'
	end
	nil
end

#no(re = nil) ⇒ Object



476
477
478
# File 'lib/netutils/cli.rb', line 476

def no(re = nil)
	yes_or_no('n', re)
end

#pager_disable_wlcObject



496
497
498
# File 'lib/netutils/cli.rb', line 496

def pager_disable_wlc
	cmd('config paging disable')
end

#route_gets(ia) ⇒ Object



587
588
589
590
591
# File 'lib/netutils/cli.rb', line 587

def route_gets(ia)
	r = _new(:ShowRoute)
	r.parse(cmd(r.cmd(ia), nil, true))
	return r.rib.get(ia)
end

#unconfigureObject



563
564
565
566
# File 'lib/netutils/cli.rb', line 563

def unconfigure
	raise "currently not configuring" if @prompt != @configprompt
	return cmd('end', @enableprompt)
end

#vrf_getsObject



593
594
595
596
597
598
# File 'lib/netutils/cli.rb', line 593

def vrf_gets
	v = _new(:ShowVRF)
	v.parse(cmd(v.cmd))
	v.vrfs.add('default', '0:0') if v.vrfs.empty?
	return v.vrfs
end

#yes(re = nil) ⇒ Object



472
473
474
# File 'lib/netutils/cli.rb', line 472

def yes(re = nil)
	yes_or_no('y', re)
end