rbeapi 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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