rbeapi 0.3.0 → 0.4.0

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 (39) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/Gemfile +3 -1
  3. data/Guardfile +2 -2
  4. data/README.md +35 -24
  5. data/Rakefile +48 -18
  6. data/gems/inifile/inifile.spec.tmpl +50 -14
  7. data/gems/net_http_unix/net_http_unix.spec.tmpl +48 -15
  8. data/gems/netaddr/netaddr.spec.tmpl +47 -14
  9. data/lib/rbeapi/api/bgp.rb +100 -4
  10. data/lib/rbeapi/api/interfaces.rb +4 -5
  11. data/lib/rbeapi/api/radius.rb +1 -1
  12. data/lib/rbeapi/api/routemaps.rb +405 -37
  13. data/lib/rbeapi/api/system.rb +21 -0
  14. data/lib/rbeapi/api/users.rb +361 -0
  15. data/lib/rbeapi/api/varp.rb +50 -22
  16. data/lib/rbeapi/api/vrrp.rb +1072 -0
  17. data/lib/rbeapi/client.rb +12 -4
  18. data/lib/rbeapi/eapilib.rb +1 -1
  19. data/lib/rbeapi/version.rb +1 -1
  20. data/rbeapi.spec.tmpl +57 -25
  21. data/spec/system/rbeapi/api/dns_spec.rb +2 -2
  22. data/spec/system/rbeapi/api/routemaps_spec.rb +344 -0
  23. data/spec/system/rbeapi/api/switchports_spec.rb +1 -1
  24. data/spec/system/rbeapi/api/system_spec.rb +44 -4
  25. data/spec/system/{api_varp_interfaces_spec.rb → rbeapi/api/varp_interfaces_spec.rb} +25 -16
  26. data/spec/system/rbeapi/api/varp_spec.rb +76 -0
  27. data/spec/unit/rbeapi/api/bgp/bgp_neighbors_spec.rb +2 -0
  28. data/spec/unit/rbeapi/api/bgp/bgp_spec.rb +54 -1
  29. data/spec/unit/rbeapi/api/interfaces/portchannel_spec.rb +1 -1
  30. data/spec/unit/rbeapi/api/routemaps/default_spec.rb +370 -0
  31. data/spec/unit/rbeapi/api/routemaps/fixture_routemaps.text +27 -0
  32. data/spec/unit/rbeapi/api/system/default_spec.rb +102 -0
  33. data/spec/unit/rbeapi/api/system/fixture_system.text +2 -0
  34. data/spec/unit/rbeapi/api/users/default_spec.rb +280 -0
  35. data/spec/unit/rbeapi/api/users/fixture_users.text +4 -0
  36. data/spec/unit/rbeapi/api/vrrp/default_spec.rb +582 -0
  37. data/spec/unit/rbeapi/api/vrrp/fixture_vrrp.text +186 -0
  38. metadata +28 -8
  39. data/spec/system/api_varp_spec.rb +0 -41
@@ -155,7 +155,7 @@ module Rbeapi
155
155
  # @return [Hash<Symbol, Object>] resource hash attribute
156
156
  def parse_shutdown(config)
157
157
  value = /no shutdown/ =~ config
158
- { shutdown: value.nil? }
158
+ { shutdown: value.nil? }
159
159
  end
160
160
  private :parse_shutdown
161
161
 
@@ -640,7 +640,7 @@ module Rbeapi
640
640
  def parse_members(name)
641
641
  grpid = name.scan(/(?<=Port-Channel)\d+/)[0]
642
642
  command = "show port-channel #{grpid} all-ports"
643
- config = node.enable(command, format: 'text')
643
+ config = node.enable(command, encoding: 'text')
644
644
  values = config.first[:result]['output'].scan(/\bEthernet[^\s]+/)
645
645
  { members: values }
646
646
  end
@@ -664,7 +664,7 @@ module Rbeapi
664
664
  return { lacp_mode: DEFAULT_LACP_MODE } unless members
665
665
  config = get_block("interface #{members.first}")
666
666
  mdata = /channel-group \d+ mode (\w+)/.match(config)
667
- { lacp_mode: mdata ? mdata[1] : DEFAULT_LACP_MODE }
667
+ { lacp_mode: mdata ? mdata[1] : DEFAULT_LACP_MODE }
668
668
  end
669
669
  private :parse_lacp_mode
670
670
 
@@ -911,8 +911,7 @@ module Rbeapi
911
911
  # @param [Hash] :opts optional keyword arguments
912
912
  #
913
913
  # @option :opts [String] :value Specifies the value to configure for
914
- # the port-channel lacp fallback timeout. Valid values range from
915
- # 1 to 100 seconds
914
+ # the port-channel lacp fallback timeout.
916
915
  #
917
916
  # @option :opts [Boolean] :enable If false then the command is
918
917
  # negated. Default is true.
@@ -148,7 +148,7 @@ module Rbeapi
148
148
  # @return [Array<Hash<Symbol,Object>>] Array of resource hashes
149
149
  def parse_servers
150
150
  tuples = config.scan(SERVER_REGEXP)
151
- tuples.map do |(host, vrf, authp, acctp, tout, tries, keyfm, key)|
151
+ tuples.map do |(host, vrf, authp, acctp, tout, tries, keyfm, key)|
152
152
  hsh = {}
153
153
  hsh[:hostname] = host
154
154
  hsh[:vrf] = vrf
@@ -43,63 +43,431 @@ module Rbeapi
43
43
  # the basis of such criteria as route metrics, access control lists, next
44
44
  # hop addresses, and route tags.
45
45
  #
46
- # rubocop:disable Metrics/MethodLength
46
+ # rubocop:disable Metrics/ClassLength
47
47
  #
48
48
  class Routemaps < Entity
49
+ ##
50
+ # get returns a hash of routemap configurations for the given name
51
+ #
52
+ # @example
53
+ # {
54
+ # <action>: {
55
+ # <seqno>: {
56
+ # match: <array>,
57
+ # set: <array>,
58
+ # continue: <integer>,
59
+ # description: <string>
60
+ # },
61
+ # <seqno>: {
62
+ # match: <array>,
63
+ # set: <array>,
64
+ # continue: <integer>,
65
+ # description: <string>
66
+ # }
67
+ # },
68
+ # <action>: {
69
+ # <seqno>: {
70
+ # match: <array>,
71
+ # set: <array>,
72
+ # continue: <integer>,
73
+ # description: <string>
74
+ # },
75
+ # <seqno>: {
76
+ # match: <array>,
77
+ # set: <array>,
78
+ # continue: <integer>,
79
+ # description: <string>
80
+ # }
81
+ # }
82
+ # }
83
+ #
84
+ # @param [String] name The routemap name to return a resource for from the
85
+ # nodes configuration
86
+ #
87
+ # @return [nil, Hash<Symbol, Object>] Returns the routemap resource as a
88
+ # Hash. If the specified name is not found in the nodes current
89
+ # configuration a nil object is returned
49
90
  def get(name)
91
+ parse_entries(name)
92
+ end
93
+
94
+ ##
95
+ # getall returns a collection of routemap resource hashes from the nodes
96
+ # running configuration. The routemap resource collection hash is keyed
97
+ # by the unique routemap name.
98
+ #
99
+ # @example
100
+ # {
101
+ # <name>: {
102
+ # <action>: {
103
+ # <seqno>: {
104
+ # match: <array>,
105
+ # set: <array>,
106
+ # continue: <integer>,
107
+ # description: <string>
108
+ # },
109
+ # <seqno>: {
110
+ # match: <array>,
111
+ # set: <array>,
112
+ # continue: <integer>,
113
+ # description: <string>
114
+ # }
115
+ # },
116
+ # <action>: {
117
+ # <seqno>: {
118
+ # match: <array>,
119
+ # set: <array>,
120
+ # continue: <integer>,
121
+ # description: <string>
122
+ # },
123
+ # <seqno>: {
124
+ # match: <array>,
125
+ # set: <array>,
126
+ # continue: <integer>,
127
+ # description: <string>
128
+ # }
129
+ # }
130
+ # },
131
+ # <name>: {
132
+ # <action>: {
133
+ # <seqno>: {
134
+ # match: <array>,
135
+ # set: <array>,
136
+ # continue: <integer>,
137
+ # description: <string>
138
+ # },
139
+ # <seqno>: {
140
+ # match: <array>,
141
+ # set: <array>,
142
+ # continue: <integer>,
143
+ # description: <string>
144
+ # }
145
+ # },
146
+ # <action>: {
147
+ # <seqno>: {
148
+ # match: <array>,
149
+ # set: <array>,
150
+ # continue: <integer>,
151
+ # description: <string>
152
+ # },
153
+ # <seqno>: {
154
+ # match: <array>,
155
+ # set: <array>,
156
+ # continue: <integer>,
157
+ # description: <string>
158
+ # }
159
+ # }
160
+ # }
161
+ # }
162
+ #
163
+ # @return [nil, Hash<Symbol, Object>] returns a hash that represents the
164
+ # entire routemap collection from the nodes running configuration. If
165
+ # there are no routemap names configured, this method will return nil.
166
+ def getall
167
+ routemaps = config.scan(/(?<=^route-map\s)[^\s]+/)
168
+ return nil if routemaps.empty?
169
+ routemaps.each_with_object({}) do |name, response|
170
+ response[name] = parse_entries(name)
171
+ end
172
+ end
173
+
174
+ ##
175
+ # parse entries is a private method to get the routemap rules.
176
+ #
177
+ # @return [nil, Hash<Symbol, Object>] returns a hash that represents the
178
+ # rules for routemaps from the nodes running configuration. If
179
+ # there are no routemaps configured, this method will return nil.
180
+ #
181
+ def parse_entries(name)
50
182
  entries = config.scan(/^route-map\s#{name}\s.+$/)
183
+ return nil if entries.empty?
184
+ entries.each_with_object({}) do |rm, response|
185
+ mdata = /^route-map\s(.+)\s(.+)\s(\d+)$/.match(rm)
186
+ rule_hsh = parse_rules(get_block(rm))
187
+ if response[mdata[2]]
188
+ response[mdata[2]].merge!(mdata[3].to_i => rule_hsh)
189
+ else
190
+ response[mdata[2]] = { mdata[3].to_i => rule_hsh }
191
+ end
192
+ end
193
+ end
194
+ private :parse_entries
195
+
196
+ ##
197
+ # parse rule is a private method to parse a rule.
198
+ #
199
+ # @return [Hash<Symbol, Object>] returns a hash that represents the
200
+ # rules for routemaps from the nodes running configuration. If
201
+ # there are no routemaps configured, this method will return an empty
202
+ # hash.
203
+ #
204
+ def parse_rules(rules)
205
+ rules.split("\n").each_with_object({}) do |rule, rule_hsh|
206
+ mdata = /\s{3}(\w+)\s/.match(rule)
207
+ case mdata.nil? ? nil : mdata[1]
208
+ when 'match'
209
+ rule_hsh[:match] = [] unless rule_hsh.include?(:match)
210
+ rule_hsh[:match] << rule.sub('match', '').strip
211
+ when 'set'
212
+ rule_hsh[:set] = [] unless rule_hsh.include?(:set)
213
+ rule_hsh[:set] << rule.sub('set', '').strip
214
+ when 'continue'
215
+ rule_hsh[:continue] = nil unless rule_hsh.include?(:continue)
216
+ rule_hsh[:continue] = rule.sub('continue', '').strip.to_i
217
+ when 'description'
218
+ rule_hsh[:description] = nil unless rule_hsh.include?(:description)
219
+ rule_hsh[:description] = rule.sub('description', '').strip
220
+ end
221
+ end
222
+ end
223
+ private :parse_rules
51
224
 
52
- entries.each_with_object([]) do |rm, arry|
53
- mdata = /route-map\s(.+)\s(.+)\s(\d+)$/.match(rm)
54
- rules = get_block(rm)
55
- rule_hsh = { 'action' => mdata[2], 'seqno' => mdata[3],
56
- 'match_rules' => [], 'set_rules' => [],
57
- 'continue_rules' => [] }
225
+ ##
226
+ # name_commands is utilized to initially prepare the routemap
227
+ def name_commands(name, action, seqno, opts = {})
228
+ if opts[:default] == true
229
+ cmd = "default route-map #{name}"
230
+ elsif opts[:enable] == false
231
+ cmd = "no route-map #{name}"
232
+ else
233
+ cmd = "route-map #{name}"
234
+ end
235
+ cmd << " #{action}"
236
+ cmd << " #{seqno}"
237
+ [cmd]
238
+ end
239
+ private :name_commands
58
240
 
59
- parsed = rules.split("\n").each_with_object({}) do |rule, hsh|
60
- mdata = /\s{3}(\w+)\s/.match(rule)
61
- case mdata.nil? ? nil : mdata[1]
62
- when 'match'
63
- hsh['match_rules'] = [] unless hsh.include?('match')
64
- hsh['match_rules'] << rule.strip
65
- when 'set'
66
- hsh['set_rules'] = [] unless hsh.include?('set')
67
- hsh['set_rules'] << rule.strip
68
- when 'continue'
69
- hsh['continue_rules'] = [] unless hsh.include?('continue')
70
- hsh['continue_rules'] << rule.strip
241
+ ##
242
+ # create will create a new routemap with the specified name.
243
+ #
244
+ # rubocop:disable Metrics/MethodLength
245
+ #
246
+ # @commands
247
+ # route-map <name> action <value> seqno <value> description <value>
248
+ # match <value> set <value> continue <value>
249
+ #
250
+ # @param [String] :name The name of the routemap to create
251
+ #
252
+ # @param [String] :action Either permit or deny
253
+ #
254
+ # @param [Integer] :seqno The sequence number
255
+ #
256
+ # @param [hash] :opts Optional keyword arguments
257
+ #
258
+ # @option :opts [Boolean] :default Set routemap to default
259
+ #
260
+ # @option :opts [String] :description A description for the routemap
261
+ #
262
+ # @option :opts [Array] :match routemap match rule
263
+ #
264
+ # @option :opts [String] :set Sets route attribute
265
+ #
266
+ # @option :opts [String] :continue The routemap sequence number to
267
+ # continue on.
268
+ #
269
+ # @option :opts [Boolean] :enable If false then the command is
270
+ # negated. Default is true.
271
+ #
272
+ # @option :opts [Boolean] :default Configure the routemap to default.
273
+ #
274
+ # @return [Boolean] returns true if the command completed successfully
275
+ def create(name, action, seqno, opts = {})
276
+ if opts.empty?
277
+ cmds = name_commands(name, action, seqno)
278
+ else
279
+ cmds = name_commands(name, action, seqno, opts)
280
+ if opts[:description]
281
+ cmds << 'no description'
282
+ cmds << "description #{opts[:description]}"
283
+ end
284
+ if opts[:continue]
285
+ cmds << 'no continue'
286
+ cmds << "continue #{opts[:continue]}"
287
+ end
288
+ if opts[:match]
289
+ remove_match_statements(name, action, seqno, cmds)
290
+ opts[:match].each do |options|
291
+ cmds << "match #{options}"
292
+ end
293
+ end
294
+ if opts[:set]
295
+ remove_set_statements(name, action, seqno, cmds)
296
+ opts[:set].each do |options|
297
+ cmds << "set #{options}"
71
298
  end
72
299
  end
73
- rule_hsh.update(parsed)
74
- arry << rule_hsh
75
300
  end
301
+ configure(cmds)
76
302
  end
77
303
 
78
- def getall
79
- maps = config.scan(/(?<=^route-map\s)[^\s]+/)
80
- maps.each_with_object({}) do |name, hsh|
81
- hsh[name] = get name unless hsh.include?(name)
304
+ ##
305
+ # remove_match_statemements removes all match rules for the
306
+ # specified routemap
307
+ def remove_match_statements(name, action, seqno, cmds)
308
+ entries = parse_entries(name)
309
+ return nil unless entries
310
+ entries.each do |entry|
311
+ next unless entry[0] == action && entry[1].assoc(seqno) && \
312
+ entry[1].assoc(seqno)[0] == seqno
313
+ Array(entry[1].assoc(seqno)[1][:match]).each do |options|
314
+ cmds << "no match #{options}"
315
+ end
82
316
  end
83
317
  end
318
+ private :remove_match_statements
84
319
 
85
- def create(name)
86
- configure "route-map #{name}"
320
+ ##
321
+ # remove_set_statemements removes all set rules for the
322
+ # specified routemap
323
+ def remove_set_statements(name, action, seqno, cmds)
324
+ entries = parse_entries(name)
325
+ return nil unless entries
326
+ entries.each do |entry|
327
+ next unless entry[0] == action && entry[1].assoc(seqno) && \
328
+ entry[1].assoc(seqno)[0] == seqno
329
+ Array(entry[1].assoc(seqno)[1][:set]).each do |options|
330
+ cmds << "no set #{options}"
331
+ end
332
+ end
87
333
  end
334
+ private :remove_set_statements
88
335
 
89
- def delete(name)
90
- configure "no route-map #{name}"
336
+ ##
337
+ # delete will delete an existing routemap name from the nodes current
338
+ # running configuration. If the delete method is called and the
339
+ # routemap name does not exist, this method will succeed.
340
+ #
341
+ # @commands
342
+ # no route-map <name> <action> <seqno>
343
+ #
344
+ # @param [String] :name The routemap name to delete from the node.
345
+ #
346
+ # @param [String] :action Either permit or deny
347
+ #
348
+ # @param [Integer] :seqno The sequence number
349
+ #
350
+ # @return [Boolean] returns true if the command completed successfully
351
+ def delete(name, action, seqno)
352
+ configure(["no route-map #{name} #{action} #{seqno}"])
353
+ end
354
+
355
+ ##
356
+ # This method will attempt to default the routemap from the nodes
357
+ # operational config. Since routemaps do not exist by default,
358
+ # the default action is essentially a negation and the result will
359
+ # be the removal of the routemap clause.
360
+ # If the routemap does not exist then this
361
+ # method will not perform any changes but still return True
362
+ #
363
+ # @commands
364
+ # no route-map <name>
365
+ #
366
+ # @param [String] :name The routemap name to set to default.
367
+ #
368
+ # @param [String] :action Either permit or deny
369
+ #
370
+ # @param [Integer] :seqno The sequence number
371
+ #
372
+ # @return [Boolean] returns true if the command completed successfully
373
+ def default(name, action, seqno)
374
+ configure(["default route-map #{name} #{action} #{seqno}"])
375
+ end
376
+
377
+ ##
378
+ # set_match_statements will set the match values for a specified routemap.
379
+ # If the specified routemap does not exist, it will be created.
380
+ #
381
+ # @commands
382
+ # route-map <name> action <value> seqno <value> match <value>
383
+ #
384
+ # @param [String] :name The name of the routemap to create
385
+ #
386
+ # @param [String] :action Either permit or deny
387
+ #
388
+ # @param [Integer] :seqno The sequence number
389
+ #
390
+ # @param [Array] :value The routemap match rules
391
+ #
392
+ # @return [Boolean] returns true if the command completed successfully
393
+ def set_match_statements(name, action, seqno, value)
394
+ cmds = ["route-map #{name} #{action} #{seqno}"]
395
+ remove_match_statements(name, action, seqno, cmds)
396
+ Array(value).each do |options|
397
+ cmds << "match #{options}"
398
+ end
399
+ configure(cmds)
400
+ end
401
+
402
+ ##
403
+ # set_set_statements will set the set values for a specified routemap.
404
+ # If the specified routemap does not exist, it will be created.
405
+ #
406
+ # @commands
407
+ # route-map <name> action <value> seqno <value> set <value>
408
+ #
409
+ # @param [String] :name The name of the routemap to create
410
+ #
411
+ # @param [String] :action Either permit or deny
412
+ #
413
+ # @param [Integer] :seqno The sequence number
414
+ #
415
+ # @param [Array] :value The routemap set rules
416
+ #
417
+ # @return [Boolean] returns true if the command completed successfully
418
+ def set_set_statements(name, action, seqno, value)
419
+ cmds = ["route-map #{name} #{action} #{seqno}"]
420
+ remove_set_statements(name, action, seqno, cmds)
421
+ Array(value).each do |options|
422
+ cmds << "set #{options}"
423
+ end
424
+ configure(cmds)
91
425
  end
92
426
 
93
- def add_rule(name, action, rule, seqno = nil)
94
- cmd = "route-map #{name} #{action}"
95
- cmd << " #{seqno}" if seqno
96
- cmds = [*cmds]
97
- cmds << rule
98
- configure cmds
427
+ ##
428
+ # set_continue will set the continue value for a specified routemap.
429
+ # If the specified routemap does not exist, it will be created.
430
+ #
431
+ # @commands
432
+ # route-map <name> action <value> seqno <value> continue <value>
433
+ #
434
+ # @param [String] :name The name of the routemap to create
435
+ #
436
+ # @param [String] :action Either permit or deny
437
+ #
438
+ # @param [Integer] :seqno The sequence number
439
+ #
440
+ # @param [Integer] :value The continue value
441
+ #
442
+ # @return [Boolean] returns true if the command completed successfully
443
+ def set_continue(name, action, seqno, value)
444
+ cmds = ["route-map #{name} #{action} #{seqno}"]
445
+ cmds << 'no continue'
446
+ cmds << "continue #{value}"
447
+ configure(cmds)
99
448
  end
100
449
 
101
- def remove_rule(name, action, seqno)
102
- configure "no route-map #{name} #{action} #{seqno}"
450
+ ##
451
+ # set_description will set the description for a specified routemap.
452
+ # If the specified routemap does not exist, it will be created.
453
+ #
454
+ # @commands
455
+ # route-map <name> action <value> seqno <value> description <value>
456
+ #
457
+ # @param [String] :name The name of the routemap to create
458
+ #
459
+ # @param [String] :action Either permit or deny
460
+ #
461
+ # @param [Integer] :seqno The sequence number
462
+ #
463
+ # @param [String] :value The description value
464
+ #
465
+ # @return [Boolean] returns true if the command completed successfully
466
+ def set_description(name, action, seqno, value)
467
+ cmds = ["route-map #{name} #{action} #{seqno}"]
468
+ cmds << 'no description'
469
+ cmds << "description #{value}"
470
+ configure(cmds)
103
471
  end
104
472
  end
105
473
  end
@@ -54,6 +54,7 @@ module Rbeapi
54
54
  def get
55
55
  response = {}
56
56
  response.merge!(parse_hostname(config))
57
+ response.merge!(parse_iprouting(config))
57
58
  response
58
59
  end
59
60
 
@@ -62,6 +63,11 @@ module Rbeapi
62
63
  { hostname: mdata.nil? ? '' : mdata[1] }
63
64
  end
64
65
 
66
+ def parse_iprouting(config)
67
+ mdata = /no\sip\srouting/.match(config)
68
+ { iprouting: mdata.nil? ? true : false }
69
+ end
70
+
65
71
  ##
66
72
  # Configures the system hostname value in the running-config
67
73
  #
@@ -76,6 +82,21 @@ module Rbeapi
76
82
  cmd = command_builder('hostname', opts)
77
83
  configure(cmd)
78
84
  end
85
+
86
+ ##
87
+ # Configures the state of global ip routing
88
+ #
89
+ # @param [Hash] opts The configuration parameters
90
+ # @option :opts [Boolean] :enable True if ip routing should be enabled
91
+ # or False if ip routing should be disabled. Default is true.
92
+ # @option opts [Boolean] :default Controls the use of the default
93
+ # keyword. Default is false.
94
+ #
95
+ # @return [Boolean] returns true if the command completed successfully
96
+ def set_iprouting(opts = {})
97
+ cmd = command_builder('ip routing', opts)
98
+ configure(cmd)
99
+ end
79
100
  end
80
101
  end
81
102
  end