netutils 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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,30 @@
1
+ require 'netutils/fsm'
2
+
3
+ class Parser < FSM
4
+ def initialize
5
+ super
6
+ @regexp = Array.new
7
+ end
8
+
9
+ def add(sname, cb, regexp = nil)
10
+ super(sname, cb)
11
+ @regexp.push(regexp)
12
+ end
13
+
14
+ def regexp
15
+ r = @regexp[@state]
16
+ r = /^.*$/ if r == nil
17
+ return r
18
+ end
19
+
20
+ def parse(buf)
21
+ buf.each_line do |l|
22
+ unless l.chomp! =~ regexp
23
+ raise(ArgumentError,
24
+ "No match at #{state_name}: \"#{l}\" " +
25
+ "to #{regexp.to_s}")
26
+ end
27
+ send(cb, l, $~)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,80 @@
1
+ require 'ipaddr'
2
+
3
+ class RIB
4
+ class Route
5
+ attr_reader :proto, :dst, :prefixlen, :nh, :interface
6
+
7
+ def initialize(proto, dst, nh, interface)
8
+ dst = ip_address_normalize(dst)
9
+ @proto = proto
10
+ @dst = dst
11
+ @prefixlen = prefixlen_get(dst)
12
+ @nh = nh
13
+ @interface = interface
14
+ end
15
+
16
+ def prefixlen_get(dst)
17
+ return dst.split('/')[1].to_i
18
+ end
19
+ private :prefixlen_get
20
+
21
+ def ip_address_normalize(dst)
22
+ # XXX IPv4 dependent...
23
+ n = dst.count('.')
24
+ case n
25
+ when 0, 1, 2
26
+ ia = dst.split('/')[0]
27
+ for i in 1..3 - n do
28
+ ia += '.0'
29
+ end
30
+ dst = "#{ia}/#{prefixlen_get(dst)}"
31
+ when 3
32
+ else
33
+ raise(ArgumentError,
34
+ "Invalid IP address: #{dst}")
35
+ end
36
+ return dst
37
+ end
38
+ private :ip_address_normalize
39
+
40
+ def tunnel?
41
+ @interface =~ /^[tT]unnel/
42
+ end
43
+
44
+ def compare(other)
45
+ return self if ! other
46
+ if defined?(PREFERRED_NEXTHOPS)
47
+ PREFERRED_NEXTHOPS.each do |prefix|
48
+ ir = IPAddr.new(prefix)
49
+ if ir.include?(@nh) &&
50
+ ! ir.include?(other.nh)
51
+ return self
52
+ elsif ! ir.include?(@nh) &&
53
+ ir.include?(other.nh)
54
+ return other
55
+ end
56
+ end
57
+ end
58
+ return other if @prefixlen < other.prefixlen
59
+ return self
60
+ end
61
+ end
62
+
63
+ def initialize
64
+ @rib = []
65
+ end
66
+
67
+ def add(proto, dst, nh, interface)
68
+ @rib.push(Route.new(proto, dst, nh, interface))
69
+ end
70
+
71
+ def get(dst, proto = nil)
72
+ m = []
73
+ @rib.each do |r|
74
+ next if proto && r.proto != proto
75
+ m.push(r) if IPAddr.new(r.dst).include?(dst)
76
+ end
77
+ return nil if m.empty?
78
+ return m
79
+ end
80
+ end
@@ -0,0 +1,402 @@
1
+ require 'netutils/cli'
2
+ require 'netutils/oncequeue'
3
+
4
+ class Switch
5
+ module Type
6
+ ROUTER = 1
7
+ SWITCH = 2
8
+ BRIDGE = 3
9
+ HOST = 4
10
+ UNKNOWN = 5
11
+ end
12
+
13
+ class PortName
14
+ MAX_ARGS = 3
15
+
16
+ attr_reader :numbers
17
+
18
+ def initialize(name)
19
+ if name =~ /^([^0-9]+)([0-9]?.*)/
20
+ @type = $1.strip
21
+ @numbers = $2.split('/')
22
+ else
23
+ @type = name.strip
24
+ @numbers = Array.new
25
+ end
26
+ end
27
+
28
+ def to_s
29
+ @type + ' ' + @numbers.join('/')
30
+ end
31
+
32
+ def to_csv
33
+ n = @numbers.dup
34
+ while n.length < MAX_ARGS do
35
+ n.unshift('-')
36
+ end
37
+ [ @type, n ].join(',')
38
+ end
39
+ end
40
+
41
+ class Peer
42
+ attr_reader :sw, :port
43
+
44
+ def initialize(sw, portname)
45
+ @sw = sw
46
+ @port = portname
47
+ end
48
+
49
+ def has_backlink?
50
+ return @sw.ports.exists?(@port)
51
+ end
52
+
53
+ def _to_ascii(sep)
54
+ return "#{@sw.name}#{sep}#{@port}"
55
+ end
56
+
57
+ def to_s
58
+ _to_ascii(' ')
59
+ end
60
+
61
+ def to_csv
62
+ _to_ascii(',')
63
+ end
64
+ end
65
+
66
+ class Peers < Array
67
+ def add(sw, portname)
68
+ push(Peer.new(sw, portname))
69
+ end
70
+ end
71
+
72
+ class Port
73
+ attr_reader :name, :peers
74
+ attr_accessor :up
75
+
76
+ def initialize(name, model, type, speed, duplex)
77
+ @name = PortName.new(name)
78
+ @model = model
79
+ @type = type_name(type)
80
+ @speed = speed
81
+ @duplex = duplex
82
+ @peers = Peers.new
83
+ @up = false
84
+ end
85
+
86
+ def type_name(type)
87
+ type = $1 if type =~ %r{10/100/(1000BaseT).?}
88
+ type = $1 if type =~ %r{10/(100BaseTX)}
89
+ type = '1000BaseT' if type =~ %r{10/100/1000-T.?}
90
+ type = $1 if type =~ /^(.*) SFP/
91
+ type = 'none' if type =~ /^Not? .*/
92
+ return type
93
+ end
94
+
95
+ def dump(p)
96
+ up = @up? '*' : ' '
97
+ s = sprintf "#{p}#{up}#{@name.to_s} #{@model} #{@type} "
98
+ print s
99
+ indent = 0
100
+ @peers.each { |peer|
101
+ l = peer.has_backlink? ? '<->' : '->'
102
+ printf "% *s#{l} #{peer.to_s}\n", indent, ''
103
+ indent = s.length
104
+ }
105
+ puts '' if @peers.length == 0
106
+ end
107
+
108
+ def dump_csv(p)
109
+ up = @up? '*' : ' '
110
+ printf "#{p},#{up},#{@name.to_csv},#{@type}"
111
+ @peers.each { |peer|
112
+ printf ",#{peer.to_csv}"
113
+ }
114
+ puts ''
115
+ end
116
+ end
117
+
118
+ class Ports
119
+ def initialize
120
+ @hash = Hash.new
121
+ @list = Array.new
122
+ end
123
+
124
+ def add(name, model, type, speed, duplex)
125
+ port = Port.new(name, model, type, speed, duplex)
126
+ @list << @hash[port.name.to_s] = port
127
+ end
128
+
129
+ def key(name)
130
+ PortName.new(name).to_s
131
+ end
132
+
133
+ def [](name)
134
+ return @hash[key(name)]
135
+ end
136
+
137
+ def exists?(name)
138
+ return @hash.key?(key(name))
139
+ end
140
+
141
+ def length
142
+ return @list.length
143
+ end
144
+
145
+ def each
146
+ @list.each { |p| yield p }
147
+ end
148
+ end
149
+
150
+ attr_reader :name, :type, :ports, :ia
151
+ attr_accessor :platform, :firmware, :time
152
+
153
+ @@retrieve_all = false
154
+ @@db = Hash.new
155
+ @@unretrieved = OnceQueue.new
156
+ @@warn = Array.new
157
+
158
+ def initialize(name, type, ia, retrieve = true)
159
+ name_set(name)
160
+ @type = type
161
+ @ports = Ports.new
162
+ @retrieve = retrieve
163
+ @cli = nil
164
+ ip_address_set(ia)
165
+
166
+ return self
167
+ end
168
+
169
+ def name_set(name)
170
+ raise "Already name is set: #{@name}" if @name
171
+ @name = name
172
+ @@db[name] = self if name
173
+ end
174
+
175
+ def ip_address_set(ia)
176
+ return if ! ia
177
+ return if @ia
178
+ @ia = ia
179
+ case @type
180
+ when Type::ROUTER, Type::SWITCH
181
+ @@unretrieved.enqueue(self) if @retrieve
182
+ end
183
+ end
184
+
185
+ def login
186
+ return if @cli
187
+ @cli = CLI.new(@name, @ia)
188
+ @cli.login
189
+ name_set(@cli.name) if ! @name
190
+ #
191
+ # retrieve interfaces because many commands require
192
+ # interface name.
193
+ #
194
+ interface_gets
195
+ end
196
+
197
+ def logout
198
+ return if ! @cli
199
+ @cli.logout
200
+ @cli = nil
201
+ end
202
+
203
+ def cmd(*argv)
204
+ @cli.cmd(*argv)
205
+ end
206
+
207
+ def configure
208
+ @cli.configure
209
+ end
210
+
211
+ def unconfigure
212
+ @cli.unconfigure
213
+ end
214
+
215
+ def exec(name, *argv)
216
+ @cli.send(name, *argv)
217
+ end
218
+ private :exec
219
+
220
+ def maker
221
+ @cli.maker
222
+ end
223
+
224
+ def maker_to_s
225
+ @cli.maker_to_s
226
+ end
227
+
228
+ def product
229
+ @cli.product
230
+ end
231
+
232
+ def prompt
233
+ @cli.prompt
234
+ end
235
+
236
+ def syslog(*arg)
237
+ exec(__method__, *arg)
238
+ end
239
+
240
+ def route_gets(ia)
241
+ exec(__method__, ia)
242
+ end
243
+
244
+ def vrf_gets
245
+ exec(__method__)
246
+ end
247
+
248
+ def arp_resolve(ia, vrf)
249
+ exec(__method__, ia, vrf)
250
+ end
251
+
252
+ def mac_address_table_get(ma, vlan)
253
+ exec(__method__, self, ma, vlan)
254
+ end
255
+
256
+ def interface_gets
257
+ raise "ERROR: cannot get interfaces twice" if @interface_got
258
+ @interface_got = true
259
+ exec(__method__, self)
260
+ end
261
+
262
+ def interface_name(name)
263
+ exec(__method__, self, name)
264
+ end
265
+
266
+ def interface_name_cli(name)
267
+ exec(__method__, name)
268
+ end
269
+
270
+ def interface_shutdown(port)
271
+ exec(__method__, port)
272
+ end
273
+
274
+ def interface_noshutdown(port)
275
+ exec(__method__, port)
276
+ end
277
+
278
+ def acl_exists?(*arg)
279
+ exec(__method__, *arg)
280
+ end
281
+
282
+ def acl_add(*arg)
283
+ exec(__method__, *arg)
284
+ end
285
+
286
+ def acl_delete(*arg)
287
+ exec(__method__, *arg)
288
+ end
289
+
290
+ def neighbor_gets(*arg)
291
+ exec(__method__, self, *arg)
292
+ end
293
+
294
+ def macaddr_resolve(ia)
295
+ vrf_gets.each do |name, vrf|
296
+ arp = arp_resolve(ia, vrf)
297
+ next if ! arp
298
+ #
299
+ # bark here if static ARP entry is found because
300
+ # static ARP entry may be for this system itself.
301
+ #
302
+ if arp.static
303
+ raise "ERROR: Static MAC address " +
304
+ "found for #{ia}"
305
+ end
306
+ return arp.ma, vrf, arp.interface
307
+ end
308
+ raise "No MAC address found for #{ia}"
309
+ end
310
+
311
+ def self.get(name, type, platform = nil, firmware = nil, time = nil,
312
+ ia = nil)
313
+ # XXX should lock
314
+ if @@db.key?(name)
315
+ sw = @@db[name]
316
+ sw.platform = platform if sw.platform == nil
317
+ sw.firmware = firmware if sw.firmware == nil
318
+ sw.time = time if sw.time == nil
319
+ sw.ip_address_set(ia)
320
+ else
321
+ sw = Switch.new(name, type, ia, @@retrieve_all)
322
+ end
323
+ return sw
324
+ end
325
+
326
+ def self.retrieve
327
+ raise(ArgumentError, 'no user defined') if USERS.empty?
328
+ raise(ArgumentError, 'no password defined') if PASSWORDS.empty?
329
+ if ENABLES.empty?
330
+ raise(ArgumentError, 'no enable password defined')
331
+ end
332
+
333
+ thread_concurrency = 64
334
+ for i in 1..thread_concurrency do
335
+ Thread.new do
336
+ while true
337
+ sw = @@unretrieved.dequeue
338
+ begin
339
+ yield sw
340
+ sw.neighbor_gets
341
+ rescue Errno::ECONNREFUSED,
342
+ Errno::ECONNRESET,
343
+ Errno::EPERM,
344
+ Timeout::Error => e
345
+ msg = "WARNING: cannot " +
346
+ "retrieve #{sw.name} " +
347
+ "(#{sw.ia}): #{e}"
348
+ puts msg
349
+ @@warn.push(msg)
350
+ @@unretrieved.error
351
+ rescue => e
352
+ msg = "WARNING: #{sw.name} " +
353
+ "(#{sw.ia}): #{e}"
354
+ puts msg
355
+ @@warn.push(msg)
356
+ ensure
357
+ @@unretrieved.done(sw)
358
+ end
359
+ end
360
+ end
361
+ end
362
+ @@unretrieved.wait_all
363
+ puts 'Finished!!'
364
+ puts "Total: #{@@unretrieved.total}, Error: #{@@unretrieved.errors}"
365
+ end
366
+
367
+ def self.set_retrieve_all
368
+ @@retrieve_all = true
369
+ end
370
+
371
+ def each_peer
372
+ @ports.each { |p| p.peers.each { |peer| yield peer } }
373
+ end
374
+
375
+ def if_dump
376
+ puts "#{@name}: #{@platform}, #{@time}, #{@ia.to_s}"
377
+ @ports.each { |p| p.dump(' ') }
378
+ end
379
+
380
+ def if_dump_csv
381
+ @ports.each { |p| p.dump_csv(@ia.to_s + ',' + @name) }
382
+ end
383
+
384
+ def config_get
385
+ @config = @cli.config_get
386
+ end
387
+
388
+ def config_dump(dir = 'conf')
389
+ begin
390
+ Dir.mkdir(dir)
391
+ rescue Errno::EEXIST
392
+ end
393
+ path = "#{dir}/#{@name}-#{@ia}.conf"
394
+ f = File.open(path, 'w')
395
+ f.puts @config
396
+ f.close
397
+ end
398
+
399
+ def self.warn
400
+ @@warn.each { |w| puts w }
401
+ end
402
+ end