netutils 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/README.md +36 -0
  8. data/Rakefile +6 -0
  9. data/bin/acl +109 -0
  10. data/bin/alaxala-deploy +271 -0
  11. data/bin/config-diff-check +111 -0
  12. data/bin/config-gets +64 -0
  13. data/bin/console +14 -0
  14. data/bin/host-locate-on-demand +102 -0
  15. data/bin/ipaddr-resolv +74 -0
  16. data/bin/ipaddr-resolv.sh +97 -0
  17. data/bin/mac-drop +84 -0
  18. data/bin/mac-nodrop +45 -0
  19. data/bin/port-shutdown +78 -0
  20. data/bin/setup +8 -0
  21. data/lib/netutils.rb +118 -0
  22. data/lib/netutils/arp.rb +28 -0
  23. data/lib/netutils/cli.rb +702 -0
  24. data/lib/netutils/cli/alaxala.rb +121 -0
  25. data/lib/netutils/cli/alaxala/interface.rb +137 -0
  26. data/lib/netutils/cli/alaxala/lldp.rb +166 -0
  27. data/lib/netutils/cli/alaxala/macfib.rb +62 -0
  28. data/lib/netutils/cli/alaxala/showarp.rb +51 -0
  29. data/lib/netutils/cli/alaxala/showroute.rb +86 -0
  30. data/lib/netutils/cli/alaxala/showvrf.rb +46 -0
  31. data/lib/netutils/cli/aruba.rb +15 -0
  32. data/lib/netutils/cli/cisco.rb +45 -0
  33. data/lib/netutils/cli/cisco/cdp.rb +117 -0
  34. data/lib/netutils/cli/cisco/ifsummary.rb +32 -0
  35. data/lib/netutils/cli/cisco/interface.rb +67 -0
  36. data/lib/netutils/cli/cisco/macfib.rb +38 -0
  37. data/lib/netutils/cli/cisco/showarp.rb +27 -0
  38. data/lib/netutils/cli/cisco/showinterface.rb +27 -0
  39. data/lib/netutils/cli/cisco/showroute.rb +73 -0
  40. data/lib/netutils/cli/cisco/showvrf.rb +45 -0
  41. data/lib/netutils/cli/nec.rb +20 -0
  42. data/lib/netutils/cli/nec/lldp.rb +16 -0
  43. data/lib/netutils/cli/paloalto.rb +21 -0
  44. data/lib/netutils/fsm.rb +43 -0
  45. data/lib/netutils/macaddr.rb +51 -0
  46. data/lib/netutils/oncequeue.rb +78 -0
  47. data/lib/netutils/parser.rb +30 -0
  48. data/lib/netutils/rib.rb +80 -0
  49. data/lib/netutils/switch.rb +402 -0
  50. data/lib/netutils/tunnel.rb +8 -0
  51. data/lib/netutils/version.rb +3 -0
  52. data/lib/netutils/vrf.rb +42 -0
  53. data/log/.gitignore +1 -0
  54. data/netutils.gemspec +33 -0
  55. metadata +195 -0
@@ -0,0 +1,121 @@
1
+ require 'netutils/cli/alaxala/interface'
2
+ require 'netutils/cli/alaxala/lldp'
3
+ require 'netutils/cli/alaxala/macfib'
4
+ require 'netutils/cli/alaxala/showarp'
5
+ require 'netutils/cli/alaxala/showroute'
6
+ require 'netutils/cli/alaxala/showvrf'
7
+
8
+ module Alaxala
9
+ CONFIG_RE = /\A((?:#configuration list for [^\n]+|#Last modified [^\n]+)\n.*)\n\Z/m
10
+
11
+ def disable_logging_console
12
+ case @product
13
+ when /^AX86[0-9]{2}/
14
+ # AX8600
15
+ configure
16
+ cmd('username default_user logging-console event-level 0')
17
+ unconfigure
18
+ when /^AX22[0-9]{2}/
19
+ # XXX: AX2230 seems not to have set logging commands...
20
+ when /^AX[23][0-9]{2,3}/
21
+ # AX3800, AX3600, AX2500, AX260
22
+ for level in 3..9
23
+ cmd("set logging console disable E#{level}")
24
+ end
25
+ end
26
+ end
27
+
28
+ def interface_name(sw, name)
29
+ if name =~ /^Port\s+(.*)$/i
30
+ name = $1
31
+ elsif name =~ /^[^\s0-9]/
32
+ return name
33
+ end
34
+ numbers = name.delete(' ').split('/')
35
+ sw.ports.each do |port|
36
+ return port.name.to_s if port.name.numbers == numbers
37
+ if sw.product =~ /^AX3[0-9]{3}/ &&
38
+ port.name.numbers.size === 3 &&
39
+ numbers.size === 2 &&
40
+ port.name.numbers.drop(1) == numbers
41
+ return port.name.to_s
42
+ end
43
+ end
44
+ nil
45
+ end
46
+
47
+ def interface_name_cli(name)
48
+ name.split(' ')[1]
49
+ end
50
+
51
+ def unconfigure
52
+ cmd('save')
53
+ super
54
+ end
55
+
56
+ #
57
+ # XXX: forcely override method because Alaxala
58
+ # interface command accept lower case characters only...
59
+ # e.g., GigabitEthernet should be gigabitethernet...
60
+ #
61
+ def interface_name_interface_command(port)
62
+ port.downcase
63
+ end
64
+ private :interface_name_interface_command
65
+
66
+ def interface_shutdown(port)
67
+ super(interface_name_interface_command(port))
68
+ end
69
+
70
+ def interface_noshutdown(port)
71
+ super(interface_name_interface_command(port))
72
+ end
73
+
74
+ def acl_definition(type, name)
75
+ case type
76
+ when 'ip'
77
+ when 'mac'
78
+ "#{type} access-list extended #{name}"
79
+ when 'advance' # XXX: AX8600 only
80
+ "#{type} access-list #{name}"
81
+ else
82
+ raise(ArgumentError, "Unsupported ACL type: #{type}")
83
+ end
84
+ end
85
+
86
+ def acl_type_to_cmd(type)
87
+ case type
88
+ when 'ip'
89
+ when 'mac'
90
+ when 'advance' # XXX: AX8600 only
91
+ #
92
+ # we here use only ``mac'' even though mac-ip and
93
+ # mac-ipv6 are available.
94
+ #
95
+ type = 'mac'
96
+ else
97
+ raise(ArgumentError, "Unsupported ACL type: #{type}")
98
+ end
99
+ type
100
+ end
101
+
102
+ def show_running_config
103
+ return cmd('show running-config')
104
+ end
105
+
106
+ def syslog(host, vrf = 1)
107
+ configure
108
+ case @product
109
+ when /^AX86[0-9]{2}/
110
+ # AX8600
111
+ cmd("logging syslog-host #{host} vrf #{vrf} no-date-info")
112
+ when /^AX(?:25[0-9]{2}|3[0-9]{3})/
113
+ # AX2500, AX3000
114
+ cmd("logging host #{host} no-date-info")
115
+ when /^AX22[0-9]{2}/
116
+ # AX2230
117
+ cmd("logging host #{host}")
118
+ end
119
+ unconfigure
120
+ end
121
+ end
@@ -0,0 +1,137 @@
1
+ require 'netutils/parser'
2
+
3
+ module Alaxala
4
+
5
+ class Interface < Parser
6
+ def cmd
7
+ return 'show port'
8
+ end
9
+
10
+ # AX2500
11
+ # Date 2017/10/14 21:00:10 JST
12
+ # Port Counts: 52
13
+ # Port Name Status Speed Duplex FCtl FrLen ChGr/Status
14
+ # 0/1 geth0/1 down - - - - -/-
15
+ # 0/2 geth0/2 down - - - - -/-
16
+
17
+ # AX2200
18
+ # Date 2017/10/14 20:47:34 JST
19
+ # Port Counts: 28
20
+ # Port Name Status Speed Duplex FCtl FrLen ChGr/Status
21
+ # 0/1 gigaether0/1 up 1000BASE-T full(auto) off 1518 -/-
22
+ # 0/2 gigaether0/2 up 1000BASE-T full(auto) off 1518 -/-
23
+
24
+ # AX3800 (stack)
25
+ # Switch 1 (Master)
26
+ # -----------------
27
+ # Date 2017/10/15 23:10:44 JST
28
+ # Port Counts: 48
29
+ # Port Name Status Speed Duplex FCtl FrLen ChGr/Status
30
+ # 0/ 1 tengeth1/0/1 up 10GBASE-SR full off 1518 1/up
31
+ # 0/ 2 tengeth1/0/2 up 10GBASE-SR full off 1518 2/up
32
+ #
33
+ #
34
+ # Switch 2 (Backup)
35
+ # -----------------
36
+ # Date 2017/10/15 23:10:44 JST
37
+ # Port Counts: 48
38
+ # Port Name Status Speed Duplex FCtl FrLen ChGr/Status
39
+ # 0/ 1 tengeth2/0/1 up 10GBASE-SR full off 1518 1/-
40
+ # 0/ 2 tengeth2/0/2 up 10GBASE-SR full off 1518 2/-
41
+
42
+ # AX8600
43
+ # Date 2017/10/14 20:16:41 JST
44
+ # Port Counts: 54
45
+ # Port Status Speed Duplex FCtl FrLen Description
46
+ # 1/1 up 10GBASE-SR full off 1518 hogehoge
47
+ # 1/2 up 10GBASE-SR full off 1518 fugafuga
48
+ PORT_RE = /^\s*([0-9\/\s]+)\s+([^\s]*)\s*(up|down|dis|inact|init)\s+([^\s]+)\s+(full|half|-)(?:\(auto\))?\s+(on|off|-)\s+(?:[0-9]+|-)\s+.*$/
49
+
50
+ def initialize(sw)
51
+ super()
52
+ add('Init', :init)
53
+ add('Date', :date, /^Port Counts:.*$/)
54
+ add('Count', :count, /^Port\s+(?:Name|Status).*$/)
55
+ add('Port', :port)
56
+ @sw = sw
57
+ end
58
+
59
+ private
60
+
61
+ def init(l, m)
62
+ case l
63
+ when /^Date/
64
+ changeto('Date')
65
+ when /^Switch/
66
+ when /^-------/
67
+ when /^$/
68
+ end
69
+ end
70
+
71
+ def date(l, m)
72
+ changeto('Count')
73
+ end
74
+
75
+ def count(l, m)
76
+ changeto('Port')
77
+ end
78
+
79
+ def port_name_normalize(port0, name, speed)
80
+ port0.delete!(' ')
81
+ # XXX: should check actual port capability...
82
+ if ! name || name.empty?
83
+ case speed
84
+ when /^(10|100|1000)BASE/
85
+ name = 'Gigabit'
86
+ when /^10GBASE/
87
+ name = 'TenG'
88
+ when /^40GBASE/
89
+ name = 'FortyG'
90
+ when /^100GBASE/
91
+ name = 'HundredG'
92
+ when '-'
93
+ else
94
+ raise "Unknown Speed: #{speed}"
95
+ end
96
+ elsif @sw.product =~ /^AX3[0-9]{3}/
97
+ #
98
+ # AX3000 series uses 3 numbers for port numbers
99
+ # in many commands of CLI but ``show port''
100
+ # command returns 2 numbers only like below.
101
+ #
102
+ # 0/23 tengeth1/0/23 up 1000BASE-SX...
103
+ #
104
+ if name !~ /^[a-z]+([0-9\/]+)$/
105
+ raise "Invalid port format: \"#{name}\""
106
+ end
107
+ port0 = $1
108
+ end
109
+ case name
110
+ when /^TenG/i
111
+ prefix = 'Ten'
112
+ when /^FourtyG/i
113
+ prefix = 'Fourty'
114
+ when /^HundredG/i
115
+ prefix = 'Hundred'
116
+ else
117
+ prefix = ''
118
+ end
119
+ return "#{prefix}GigabitEthernet #{port0}"
120
+ end
121
+
122
+ def port(l, m)
123
+ if l =~ /^Switch/
124
+ changeto('Init')
125
+ return
126
+ end
127
+ return if l =~ /^$/ # AX2500, or others...
128
+ if l !~ PORT_RE
129
+ raise "CLI invalid format of port: #{l}"
130
+ end
131
+ name = port_name_normalize($1, $2, $4)
132
+ @sw.ports.add(name, nil, $4, $4, $5)
133
+ @sw.ports[name].up = $3 == 'up'
134
+ end
135
+ end
136
+
137
+ end
@@ -0,0 +1,166 @@
1
+ require 'netutils/parser'
2
+ require 'netutils/switch'
3
+
4
+ module Alaxala
5
+
6
+ class LLDP < Parser
7
+ def cmd(port)
8
+ if port
9
+ port = @sw.interface_name_cli(port)
10
+ # XXX: how about stack configuration...
11
+ if @sw.product =~ /^AX3[0-9]{3}/
12
+ numbers = port.split('/')
13
+ if numbers.size != 3
14
+ raise "Invalid port format: #{numbers}"
15
+ end
16
+ port = "#{numbers[1]}/#{numbers[2]}"
17
+ end
18
+ port = "port #{port} "
19
+ end
20
+ return "show lldp #{port}detail"
21
+ end
22
+
23
+ # Date 2017/10/15 00:06:36 JST
24
+ # Status: Enabled Chassis ID: Type=MAC Info=beef.dad.beef
25
+ # Interval Time: 30 Hold Count: 4 TTL: 121 Draft TTL: 120
26
+ # System Name: hogehoge
27
+ # System Description: ALAXALA AX8600S AX-8600-S16 [AX8616S] Switching software (including encryption) Ver. 12.7.B [OS-SE]
28
+ # Neighbor Counts=1
29
+ # Draft Neighbor Counts=0
30
+ # Port Counts=1
31
+ # Port 1/ 1(CH: 1)
32
+ # Link: Up PortEnabled: TRUE AdminStatus: enabledRxTx
33
+ # Neighbor Counts: 1 Draft Neighbor Counts: 0
34
+ # Port ID: Type=MAC Info=beef.dead.beef
35
+ # Port Description: hogehoge
36
+ # Neighbor 1 TTL: 95
37
+ # Chassis ID: Type=MAC Info=dead.beef.dead
38
+ # System Name: fugafuga
39
+ # System Description: ALAXALA AX2530 AX-2530-48T2X-B [AX2530S-48T2X] Switching software Ver. 4.6.A [OS-L2B]
40
+ # Port ID: Type=MAC Info=dead.beef.dead
41
+ # Port Description: hogehoge
42
+
43
+ attr_reader :rsw
44
+ def initialize(sw)
45
+ super()
46
+ add('Init', :init)
47
+ add('Port', :port)
48
+ add('PortInfo', :port_info)
49
+ add('ChassisID', :chassis_id, /^ Chassis ID: Type=[^\s]+ \s+Info=[0-9a-z.]+$/)
50
+ add('SystemName', :system_name, /^\s{4,6}System Name: ([^\s]+)$/)
51
+ add('SystemDescription',:system_description, /^\s{4,6}System Description: (.*)$/)
52
+ add('PortID', :port_id)
53
+ add('PortDescription', :port_description, /^\s{4,6}Port Description: .*$/)
54
+ add('TagID', :tag_id)
55
+ @sw = sw
56
+ end
57
+
58
+ private
59
+
60
+ def init(l, m)
61
+ changeto('Port') if l =~ /^Port Counts.*$/
62
+ end
63
+
64
+ def port(l, m)
65
+ raise 'Invalid format' if l !~ /^Port\s+([0-9\s\/]+).*$/
66
+ @lport = @sw.interface_name($1)
67
+ changeto('PortInfo')
68
+ end
69
+
70
+ def port_info(l, m)
71
+ if l =~ /^ (?:Draft Neighbor|Neighbor)\s+[0-9]+\s+TTL: [0-9]+\s*.*$/
72
+ neighbor(l, m)
73
+ elsif l =~ /^ [0-9]+\s+TTL:\s*[0-9]+\s+Chassis ID: Type=[^\s]+\s+Info=[0-9a-z.]+$/
74
+ neighbor_ax3000(l, m)
75
+ elsif l =~ /^Port/
76
+ port(l, m)
77
+ else
78
+ end
79
+ end
80
+
81
+ def neighbor(l, m)
82
+ @rname = nil
83
+ changeto('ChassisID')
84
+ end
85
+
86
+ def neighbor_ax3000(l, m)
87
+ @rname = nil
88
+ changeto('SystemName')
89
+ end
90
+
91
+ def chassis_id(l, m)
92
+ changeto('SystemName')
93
+ end
94
+
95
+ def system_name(l, m)
96
+ @rname = m[1]
97
+ changeto('SystemDescription')
98
+ end
99
+
100
+ def system_description(l, m)
101
+ desc = m[1]
102
+ case desc
103
+ when /^(ALAXALA.*) Switching software Ver. (.*)$/
104
+ platform = $1
105
+ firmware = $2
106
+ else
107
+ platform = desc
108
+ firmware = nil
109
+ end
110
+ case platform
111
+ when /Cisco*/,
112
+ /^ALAXALA AX[87643]/, /ALAXALA AX2[0-9]{2}[^0-9]/
113
+ type = Switch::Type::ROUTER
114
+ when /^ALAXALA AX[12][0-9]{3}[^0-9]/
115
+ type = Switch::Type::SWITCH
116
+ # elsif /XXX/
117
+ # type = Switch::Type::BRIDGE
118
+ else
119
+ type = Switch::Type::HOST
120
+ end
121
+ neighbor_add(@lport, @rname, type, platform, firmware)
122
+ changeto('PortID')
123
+ end
124
+
125
+ def port_id(l, m)
126
+ # XXX: this may be left system description.
127
+ return if l !~ /^\s{4,6}Port ID: Type=[^\s]+ \s+Info=.*+$/
128
+ changeto('PortDescription')
129
+ end
130
+
131
+ def port_description(l, m)
132
+ changeto('TagID')
133
+ end
134
+
135
+ def tag_id(l, m)
136
+ case l
137
+ when /^Port+/
138
+ if ! @rsw.ia
139
+ raise "no IP address found for #{@rsw.name} "
140
+ "on #{@sw.name} #{@lport}"
141
+ end
142
+ port(l, m)
143
+ when /^ [^\s]+/
144
+ port_info(l, m)
145
+ when /^\s{4,6}Tag ID: .*$/
146
+ return
147
+ when /^\s{4,6}IPv4 Address: (?:Untagged|Tagged:\s+[0-9]+)\s+([0-9.]+)$/,
148
+ /^\s{4,6}Management Address:\s+([0-9.]+)$/
149
+ @rsw.ip_address_set($1)
150
+ end
151
+ end
152
+
153
+ def neighbor_add(lport, rname, type, platform, firmware)
154
+ # XXX: this is valid for cisco only...
155
+ if firmware =~ /^.*Copyright \(c\) .*([0-9]{4}) .*$/
156
+ time = $1
157
+ else
158
+ time = 'unknown'
159
+ end
160
+ static_neighbor_resolve(@sw.name, lport)
161
+ @rsw = Switch.get(rname, type, platform, firmware, time)
162
+ @sw.ports[lport].peers.add(@rsw, nil) # XXX: no way to know remote port...
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,62 @@
1
+ require 'netutils/parser'
2
+ require 'netutils/macaddr'
3
+
4
+ module Alaxala
5
+
6
+ class MACFIB < Parser
7
+ attr_reader :ports
8
+
9
+ #
10
+ # Date 2017/10/15 08:24:22 JST
11
+ # Aging time : 300
12
+ # MAC address VLAN Type Port-list
13
+ # dead.beef.dead 9999 Dynamic 0/26-27
14
+ #
15
+ AX2000_RE = /^[0-9a-z.]+\s+[0-9]+\s+[^\s]+\s+([0-9\/,\-]+)$/
16
+
17
+ # Date 2017/10/14 19:39:20 JST
18
+ # MAC address VLAN C-Tag Aging-Time Type Port-list
19
+ # dead.beef.dead 9999 - 479 Dynamic 7/5,11/5
20
+ AX8600_RE = /^[0-9a-z.]+\s+[0-9]+\s+[^\s]+\s+[0-9]+\s+[^\s]+\s+([0-9\/,\-]+)$/
21
+
22
+ def cmd(ma, vlan)
23
+ mac = "mac " if @sw.product =~ /^AX2[0-9]{3}/
24
+ return "show mac-address-table #{mac}#{ma.to_s} vlan #{vlan}"
25
+ end
26
+
27
+ def initialize(sw)
28
+ @ports = {}
29
+ super()
30
+ add('Init', :init)
31
+ @sw = sw
32
+ end
33
+
34
+ def init(l, m)
35
+ case l
36
+ # XXX: other switches...
37
+ when AX2000_RE, AX8600_RE
38
+ $1.split(',').each do |port|
39
+ port, last = port.split('-')
40
+ if port =~ /^(.*\/)([0-9]+)$/
41
+ prefix = $1
42
+ first = $2.to_i
43
+ else
44
+ raise(ArgumentError,
45
+ "Unknown port name format: #{port}")
46
+ end
47
+ if last
48
+ last = last.to_i
49
+ else
50
+ last = first
51
+ end
52
+ for i in first .. last do
53
+ name = prefix + i.to_s
54
+ name = @sw.interface_name(name)
55
+ @ports[name] = name
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ end