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
@@ -0,0 +1,1072 @@
1
+ #
2
+ # Copyright (c) 2015, Arista Networks, Inc.
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are
7
+ # met:
8
+ #
9
+ # Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ #
12
+ # Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # Neither the name of Arista Networks nor the names of its
17
+ # contributors may be used to endorse or promote products derived from
18
+ # this software without specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
24
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29
+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
30
+ # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+ require 'rbeapi/api'
33
+
34
+ ##
35
+ # Eos is the toplevel namespace for working with Arista EOS nodes
36
+ module Rbeapi
37
+ ##
38
+ # Api is module namespace for working with the EOS command API
39
+ module Api
40
+ ##
41
+ # The Vrrp class manages the set of virtual routers.
42
+ # rubocop:disable Metrics/ClassLength
43
+ class Vrrp < Entity
44
+ def initialize(node)
45
+ super(node)
46
+ end
47
+
48
+ ##
49
+ # get returns the all the virtual router IPs for the given layer 3
50
+ # interface name from the nodes current configuration.
51
+ #
52
+ # rubocop:disable Metrics/MethodLength
53
+ #
54
+ # @example
55
+ # {
56
+ # 1: {
57
+ # enable: <True|False>
58
+ # primary_ip: <String>
59
+ # priority: <Integer>
60
+ # description: <String>
61
+ # secondary_ip: [ <ip_string1>, <ip_string2> ]
62
+ # ip_version: <Integer>
63
+ # timers_advertise: <Integer>
64
+ # mac_addr_adv_interval: <Integer>
65
+ # preempt: <True|False>
66
+ # preempt_delay_min: <Integer>
67
+ # preempt_delay_reload: <Integer>
68
+ # delay_reload: <Integer>
69
+ # track: [
70
+ # { name: 'Ethernet3', action: 'decrement', amount: 33 },
71
+ # { name: 'Ethernet2', action: 'decrement', amount: 22 },
72
+ # { name: 'Ethernet2', action: 'shutdown' }
73
+ # ]
74
+ # }
75
+ # }
76
+ #
77
+ # @param [String] :name The layer 3 interface name.
78
+ #
79
+ # @return [nil, Hash<Symbol, Object>] Returns the VRRP resource as a
80
+ # Hash with the virtual router ID as the key. If the interface name
81
+ # does not exist then a nil object is returned.
82
+ def get(name)
83
+ config = get_block("^interface #{name}")
84
+ return nil unless config
85
+
86
+ response = {}
87
+
88
+ vrids = config.scan(/^\s+(?:no |)vrrp (\d+)/)
89
+ vrids.uniq.each do |vrid_arr|
90
+ # Parse the vrrp configuration for the vrid(s) in the list
91
+ entry = {}
92
+ vrid = vrid_arr[0]
93
+ entry.merge!(parse_delay_reload(config, vrid))
94
+ entry.merge!(parse_description(config, vrid))
95
+ entry.merge!(parse_enable(config, vrid))
96
+ entry.merge!(parse_ip_version(config, vrid))
97
+ entry.merge!(parse_mac_addr_adv_interval(config, vrid))
98
+ entry.merge!(parse_preempt(config, vrid))
99
+ entry.merge!(parse_preempt_delay_min(config, vrid))
100
+ entry.merge!(parse_preempt_delay_reload(config, vrid))
101
+ entry.merge!(parse_primary_ip(config, vrid))
102
+ entry.merge!(parse_priority(config, vrid))
103
+ entry.merge!(parse_secondary_ip(config, vrid))
104
+ entry.merge!(parse_timers_advertise(config, vrid))
105
+ entry.merge!(parse_track(config, vrid))
106
+
107
+ response[vrid.to_i] = entry unless entry.nil?
108
+ end
109
+ response
110
+ end
111
+
112
+ ##
113
+ # getall returns the collection of virtual router IPs for all the
114
+ # layer 3 interfaces from the nodes running configuration as a hash.
115
+ # The resource collection hash is keyed by the ACL name.
116
+ #
117
+ # @example
118
+ # {
119
+ # 'Vlan100': {
120
+ # 1: { data },
121
+ # 250: { data },
122
+ # },
123
+ # 'Vlan200': {
124
+ # 2: { data },
125
+ # 250: { data },
126
+ # }
127
+ # }
128
+ #
129
+ # @return [nil, Hash<Symbol, Object>] Returns a hash that represents
130
+ # the entire virtual router IPs collection for all the layer 3
131
+ # interfaces from the nodes running configuration. If there are no
132
+ # virtual routers configured, this method will return an empty hash.
133
+ def getall
134
+ interfaces = config.scan(/(?<=^interface\s).+$/)
135
+ interfaces.each_with_object({}) do |name, hsh|
136
+ data = get(name)
137
+ hsh[name] = data if data
138
+ end
139
+ end
140
+
141
+ ##
142
+ # parse_primary_ip scans the nodes configurations for the given
143
+ # virtual router id and extracts the primary IP.
144
+ #
145
+ # @api private
146
+ #
147
+ # @param [String] :config The interface config.
148
+ # @param [String] :vrid The virtual router id.
149
+ #
150
+ # @return [Hash<'primary_ip', String>] Where string is the IPv4
151
+ # address or nil if the value is not set.
152
+ def parse_primary_ip(config, vrid)
153
+ match = config.scan(/^\s+vrrp #{vrid} ip (\d+\.\d+\.\d+\.\d+)$/)
154
+ if match.empty?
155
+ fail 'Did not get a default value for primary_ip'
156
+ else
157
+ value = match[0][0]
158
+ end
159
+ { primary_ip: value }
160
+ end
161
+ private :parse_primary_ip
162
+
163
+ ##
164
+ # parse_priority scans the nodes configurations for the given
165
+ # virtual router id and extracts the priority value.
166
+ #
167
+ # @api private
168
+ #
169
+ # @param [String] :config The interface config.
170
+ # @param [String] :vrid The virtual router id.
171
+ #
172
+ # @return [Hash<'priority', Integer>] The priority is between
173
+ # <1-255> or nil if the value is not set.
174
+ def parse_priority(config, vrid)
175
+ match = config.scan(/^\s+vrrp #{vrid} priority (\d+)$/)
176
+ if match.empty?
177
+ fail 'Did not get a default value for priority'
178
+ else
179
+ value = match[0][0].to_i
180
+ end
181
+ { priority: value }
182
+ end
183
+ private :parse_priority
184
+
185
+ ##
186
+ # parse_timers_advertise scans the nodes configurations for the given
187
+ # virtual router id and extracts the timers advertise value.
188
+ #
189
+ # @api private
190
+ #
191
+ # @param [String] :config The interface config.
192
+ # @param [String] :vrid The virtual router id.
193
+ #
194
+ # @return [nil, Hash<'timers_advertise', Integer>] The timers_advertise
195
+ # is between <1-255> or nil if the value is not set.
196
+ def parse_timers_advertise(config, vrid)
197
+ match = config.scan(/^\s+vrrp #{vrid} timers advertise (\d+)$/)
198
+ if match.empty?
199
+ fail 'Did not get a default value for timers advertise'
200
+ else
201
+ value = match[0][0].to_i
202
+ end
203
+ { timers_advertise: value }
204
+ end
205
+ private :parse_timers_advertise
206
+
207
+ ##
208
+ # parse_preempt scans the nodes configurations for the given
209
+ # virtual router id and extracts the preempt value.
210
+ #
211
+ # @api private
212
+ #
213
+ # @param [String] :config The interface config.
214
+ # @param [String] :vrid The virtual router id.
215
+ #
216
+ # @return [nil, Hash<'preempt', Integer>] The preempt is
217
+ # between <1-255> or nil if the value is not set.
218
+ def parse_preempt(config, vrid)
219
+ match = config.scan(/^\s+vrrp #{vrid} preempt$/)
220
+ if match.empty?
221
+ value = false
222
+ else
223
+ value = true
224
+ end
225
+ { preempt: value }
226
+ end
227
+ private :parse_preempt
228
+
229
+ ##
230
+ # parse_enable scans the nodes configurations for the given
231
+ # virtual router id and extracts the enable value.
232
+ #
233
+ # @api private
234
+ #
235
+ # @param [String] :config The interface config.
236
+ # @param [String] :vrid The virtual router id.
237
+ #
238
+ # @return [Hash<'enable', Boolean>]
239
+ def parse_enable(config, vrid)
240
+ match = config.scan(/^\s+vrrp #{vrid} shutdown$/)
241
+ if match.empty?
242
+ value = true
243
+ else
244
+ value = false
245
+ end
246
+ { enable: value }
247
+ end
248
+ private :parse_enable
249
+
250
+ ##
251
+ # parse_secondary_ip scans the nodes configurations for the given
252
+ # virtual router id and extracts the secondary_ip value.
253
+ #
254
+ # @api private
255
+ #
256
+ # @param [String] :config The interface config.
257
+ # @param [String] :vrid The virtual router id.
258
+ #
259
+ # @return [nil, Hash<'secondary_ip', Array<Strings>>] Returns an empty
260
+ # array if the value is not set.
261
+ def parse_secondary_ip(config, vrid)
262
+ regex = "vrrp #{vrid} ip"
263
+ matches = config.scan(/^\s+#{regex} (\d+\.\d+\.\d+\.\d+) secondary$/)
264
+ response = []
265
+ matches.each do |ip|
266
+ response << ip[0]
267
+ end
268
+ { secondary_ip: response }
269
+ end
270
+ private :parse_secondary_ip
271
+
272
+ ##
273
+ # parse_description scans the nodes configurations for the given
274
+ # virtual router id and extracts the description.
275
+ #
276
+ # @api private
277
+ #
278
+ # @param [String] :config The interface config.
279
+ # @param [String] :vrid The virtual router id.
280
+ #
281
+ # @return [nil, Hash<'secondary_ip', String>] Returns nil if the
282
+ # value is not set.
283
+ def parse_description(config, vrid)
284
+ match = config.scan(/^\s+vrrp #{vrid} description\s+(.*)\s*$/)
285
+ if match.empty?
286
+ value = nil
287
+ else
288
+ value = match[0][0]
289
+ end
290
+ { description: value }
291
+ end
292
+ private :parse_description
293
+
294
+ ##
295
+ # parse_track scans the nodes configurations for the given
296
+ # virtual router id and extracts the track entries.
297
+ #
298
+ # @api private
299
+ #
300
+ # @param [String] :config The interface config.
301
+ # @param [String] :vrid The virtual router id.
302
+ #
303
+ # @return [Hash<'track', Array<Hashes>] Returns an empty array if the
304
+ # value is not set. An example array of hashes follows:
305
+ # { name: 'Ethernet3', action: 'decrement', amount: 33 },
306
+ # { name: 'Ethernet2', action: 'decrement', amount: 22 },
307
+ # { name: 'Ethernet2', action: 'shutdown' }
308
+ def parse_track(config, vrid)
309
+ pre = "vrrp #{vrid} track "
310
+ matches = \
311
+ config.scan(/^\s+#{pre}(\S+) (decrement|shutdown)\s*(?:(\d+$|$))/)
312
+ response = []
313
+ matches.each do |name, action, amount|
314
+ hsh = { name: name, action: action }
315
+ hsh[:amount] = amount.to_i if action == 'decrement'
316
+ response << hsh
317
+ end
318
+ { track: response }
319
+ end
320
+ private :parse_track
321
+
322
+ ##
323
+ # parse_ip_version scans the nodes configurations for the given
324
+ # virtual router id and extracts the IP version.
325
+ #
326
+ # @api private
327
+ #
328
+ # @param [String] :config The interface config.
329
+ # @param [String] :vrid The virtual router id.
330
+ #
331
+ # @return [Hash<'ip_version', Integer>] Returns nil if the
332
+ # value is not set.
333
+ def parse_ip_version(config, vrid)
334
+ match = config.scan(/^\s+vrrp #{vrid} ip version (\d+)$/)
335
+ if match.empty?
336
+ fail 'Did not get a default value for ip version'
337
+ else
338
+ value = match[0][0].to_i
339
+ end
340
+ { ip_version: value }
341
+ end
342
+ private :parse_ip_version
343
+
344
+ ##
345
+ # parse_mac_addr_adv_interval scans the nodes configurations for the
346
+ # given virtual router id and extracts the mac address advertisement
347
+ # interval.
348
+ #
349
+ # @api private
350
+ #
351
+ # @param [String] :config The interface config.
352
+ # @param [String] :vrid The virtual router id.
353
+ #
354
+ # @return [Hash<'mac_addr_adv_interval', Integer>] Returns nil if the
355
+ # value is not set.
356
+ def parse_mac_addr_adv_interval(config, vrid)
357
+ regex = "vrrp #{vrid} mac-address advertisement-interval"
358
+ match = config.scan(/^\s+#{regex} (\d+)$/)
359
+ if match.empty?
360
+ fail 'Did not get a default value for mac address ' \
361
+ 'advertisement interval'
362
+ else
363
+ value = match[0][0].to_i
364
+ end
365
+ { mac_addr_adv_interval: value }
366
+ end
367
+ private :parse_mac_addr_adv_interval
368
+
369
+ ##
370
+ # parse_preempt_delay_min scans the nodes configurations for the given
371
+ # virtual router id and extracts the preempt delay minimum value..
372
+ #
373
+ # @api private
374
+ #
375
+ # @param [String] :config The interface config.
376
+ # @param [String] :vrid The virtual router id.
377
+ #
378
+ # @return [Hash<'preempt_delay_min', Integer>] Returns nil if the
379
+ # value is not set.
380
+ def parse_preempt_delay_min(config, vrid)
381
+ match = config.scan(/^\s+vrrp #{vrid} preempt delay minimum (\d+)$/)
382
+ if match.empty?
383
+ fail 'Did not get a default value for preempt delay minimum'
384
+ else
385
+ value = match[0][0].to_i
386
+ end
387
+ { preempt_delay_min: value }
388
+ end
389
+ private :parse_preempt_delay_min
390
+
391
+ ##
392
+ # parse_preempt_delay_reload scans the nodes configurations for the
393
+ # given virtual router id and extracts the preempt delay reload value.
394
+ #
395
+ # @api private
396
+ #
397
+ # @param [String] :config The interface config.
398
+ # @param [String] :vrid The virtual router id.
399
+ #
400
+ # @return [Hash<'preempt_delay_reload', Integer>] Returns nil if the
401
+ # value is not set.
402
+ def parse_preempt_delay_reload(config, vrid)
403
+ match = config.scan(/^\s+vrrp #{vrid} preempt delay reload (\d+)$/)
404
+ if match.empty?
405
+ fail 'Did not get a default value for preempt delay reload'
406
+ else
407
+ value = match[0][0].to_i
408
+ end
409
+ { preempt_delay_reload: value }
410
+ end
411
+ private :parse_preempt_delay_reload
412
+
413
+ ##
414
+ # parse_delay_reload scans the nodes configurations for the given
415
+ # virtual router id and extracts the delay reload value.
416
+ #
417
+ # @api private
418
+ #
419
+ # @param [String] :config The interface config.
420
+ # @param [String] :vrid The virtual router id.
421
+ #
422
+ # @return [Hash<'delay_reload', Integer>] Returns empty hash if the
423
+ # value is not set.
424
+ def parse_delay_reload(config, vrid)
425
+ match = config.scan(/^\s+vrrp #{vrid} delay reload (\d+)$/)
426
+ if match.empty?
427
+ fail 'Did not get a default value for delay reload'
428
+ else
429
+ value = match[0][0].to_i
430
+ end
431
+ { delay_reload: value }
432
+ end
433
+ private :parse_delay_reload
434
+
435
+ ##
436
+ # create will create a new virtual router ID resource for the interface
437
+ # in the nodes current. If the create method is called and the virtual
438
+ # router ID already exists for the interface, this method will still
439
+ # return true. Create takes optional parameters, but at least one
440
+ # parameter needs to be set or the command will fail.
441
+ #
442
+ # @eos_version 4.13.7M
443
+ #
444
+ # @commands
445
+ # interface <name>
446
+ # vrrp <vrid> ...
447
+ #
448
+ # @param [String] :name The layer 3 interface name.
449
+ #
450
+ # @param [String] :vrid The virtual router id.
451
+ #
452
+ # @param [hash] :opts Optional keyword arguments
453
+ #
454
+ # @option :opts [Boolean] :enable Enable the virtual router.
455
+ #
456
+ # @option :opts [String] :primary_ip The primary IPv4 address.
457
+ #
458
+ # @option :opts [Integer] :priority The priority setting for a virtual
459
+ # router.
460
+ #
461
+ # @option :opts [String] :description Associates a text string to a
462
+ # virtual router.
463
+ #
464
+ # @option :opts [Array<String>] :secondary_ip The secondary IPv4
465
+ # address to the specified virtual router.
466
+ #
467
+ # @option :opts [Integer] :ip_version Configures the VRRP version for
468
+ # the VRRP router.
469
+ #
470
+ # @option :opts [Integer] :timers_advertise The interval between
471
+ # successive advertisement messages that the switch sends to routers
472
+ # in the specified virtual router ID.
473
+ #
474
+ # @option :opts [Integer] :mac_addr_adv_interval Specifies interval in
475
+ # seconds between advertisement packets sent to VRRP group members.
476
+ #
477
+ # @option :opts [Boolean] :preempt A virtual router preempt mode
478
+ # setting. When preempt mode is enabled, if the switch has a higher
479
+ # priority it will preempt the current master virtual router. When
480
+ # preempt mode is disabled, the switch can become the master virtual
481
+ # router only when a master virtual router is not present on the
482
+ # subnet, regardless of priority settings.
483
+ #
484
+ # @option :opts [Integer] :preempt_delay_min Interval in seconds between
485
+ # VRRP preempt event and takeover. Minimum delays takeover when VRRP
486
+ # is fully implemented.
487
+ #
488
+ # @option :opts [Integer] :preempt_delay_reload Interval in seconds
489
+ # between VRRP preempt event and takeover. Reload delays takeover
490
+ # after initialization following a switch reload.
491
+ #
492
+ # @option :opts [Integer] :delay_reload Delay between system reboot and
493
+ # VRRP initialization.
494
+ #
495
+ # @option :opts [Array<Hash>] :track The track hash contains the
496
+ # name of an interface to track, the action to take on state-change
497
+ # of the tracked interface, and the amount to decrement the priority.
498
+ #
499
+ # @return [Boolean] returns true if the command completed successfully
500
+ def create(name, vrid, opts = {})
501
+ fail ArgumentError, 'create has no options set' if opts.empty?
502
+ cmds = []
503
+ if opts.key?(:enable)
504
+ if opts[:enable]
505
+ cmds << "no vrrp #{vrid} shutdown"
506
+ else
507
+ cmds << "vrrp #{vrid} shutdown"
508
+ end
509
+ end
510
+ cmds << "vrrp #{vrid} ip #{opts[:primary_ip]}" if opts.key?(:primary_ip)
511
+ if opts.key?(:priority)
512
+ cmds << "vrrp #{vrid} priority #{opts[:priority]}"
513
+ end
514
+ if opts.key?(:description)
515
+ cmds << "vrrp #{vrid} description #{opts[:description]}"
516
+ end
517
+ if opts.key?(:secondary_ip)
518
+ cmds += build_secondary_ip_cmd(name, vrid, opts[:secondary_ip])
519
+ end
520
+ if opts.key?(:ip_version)
521
+ cmds << "vrrp #{vrid} ip version #{opts[:ip_version]}"
522
+ end
523
+ if opts.key?(:timers_advertise)
524
+ cmds << "vrrp #{vrid} timers advertise #{opts[:timers_advertise]}"
525
+ end
526
+ if opts.key?(:mac_addr_adv_interval)
527
+ val = opts[:mac_addr_adv_interval]
528
+ cmds << "vrrp #{vrid} mac-address advertisement-interval #{val}"
529
+ end
530
+ if opts.key?(:preempt)
531
+ if opts[:preempt]
532
+ cmds << "vrrp #{vrid} preempt"
533
+ else
534
+ cmds << "no vrrp #{vrid} preempt"
535
+ end
536
+ end
537
+ if opts.key?(:preempt_delay_min)
538
+ val = opts[:preempt_delay_min]
539
+ cmds << "vrrp #{vrid} preempt delay minimum #{val}"
540
+ end
541
+ if opts.key?(:preempt_delay_reload)
542
+ val = opts[:preempt_delay_reload]
543
+ cmds << "vrrp #{vrid} preempt delay reload #{val}"
544
+ end
545
+ if opts.key?(:delay_reload)
546
+ cmds << "vrrp #{vrid} delay reload #{opts[:delay_reload]}"
547
+ end
548
+ cmds += build_tracks_cmd(name, vrid, opts[:track]) if opts.key?(:track)
549
+ configure_interface(name, cmds)
550
+ end
551
+
552
+ ##
553
+ # delete will delete the virtual router ID on the interface from the
554
+ # nodes current running configuration. If the delete method is called
555
+ # and the virtual router id does not exist on the interface, this
556
+ # method will succeed.
557
+ #
558
+ # @eos_version 4.13.7M
559
+ #
560
+ # @commands
561
+ # interface <name>
562
+ # no vrrp <vrid>
563
+ #
564
+ # @param [String] :name The layer 3 interface name.
565
+ # @param [Integer] :vrid The virtual router ID.
566
+ #
567
+ # @return [Boolean] returns true if the command completed successfully
568
+ def delete(name, vrid)
569
+ configure_interface(name, "no vrrp #{vrid}")
570
+ end
571
+
572
+ ##
573
+ # default will default the virtual router ID on the interface from the
574
+ # nodes current running configuration. This command has the same effect
575
+ # as deleting the virtual router id from the interface in the nodes
576
+ # running configuration. If the default method is called and the
577
+ # virtual router id does not exist on the interface, this method will
578
+ # succeed.
579
+ #
580
+ # @eos_version 4.13.7M
581
+ #
582
+ # @commands
583
+ # interface <name>
584
+ # default vrrp <vrid>
585
+ #
586
+ # @param [String] :name The layer 3 interface name.
587
+ # @param [Integer] :vrid The virtual router ID.
588
+ #
589
+ # @return [Boolean] returns true if the command complete successfully
590
+ def default(name, vrid)
591
+ configure_interface(name, "default vrrp #{vrid}")
592
+ end
593
+
594
+ ##
595
+ # set_shutdown enables and disables the virtual router.
596
+ #
597
+ # @commands
598
+ # interface <name>
599
+ # {no | default} vrrp <vrid> shutdown
600
+ #
601
+ # @param [String] :name The layer 3 interface name.
602
+ # @param [Integer] :vrid The virtual router ID.
603
+ # @param [hash] :opts Optional keyword arguments
604
+ #
605
+ # @option :opts [Boolean] :enable If enable is true then the virtual
606
+ # router is administratively enabled for the interface and if enable
607
+ # is false then the virtual router is administratively disabled
608
+ # for the interface. Default is true.
609
+ #
610
+ # @option :opts [Boolean] :default Configure shutdown using
611
+ # the default keyword.
612
+ #
613
+ # @return [Boolean] returns true if the command complete successfully
614
+ def set_shutdown(name, vrid, opts = {})
615
+ fail 'set_shutdown has the value option set' if opts[:value]
616
+ # Shutdown semantics are opposite of enable semantics so invert enable
617
+ enable = opts.fetch(:enable, true)
618
+ opts.merge!(enable: !enable)
619
+ cmd = "vrrp #{vrid} shutdown"
620
+ configure_interface(name, command_builder(cmd, opts))
621
+ end
622
+
623
+ ##
624
+ # set_primary_ip sets the primary IP address for the virtual router.
625
+ #
626
+ # @commands
627
+ # interface <name>
628
+ # {no | default} vrrp <vrid> ip <A.B.C.D>
629
+ #
630
+ # @param [String] :name The layer 3 interface name.
631
+ # @param [Integer] :vrid The virtual router ID.
632
+ # @param [hash] :opts Optional keyword arguments
633
+ #
634
+ # @option :opts [String] :value The primary IPv4 address.
635
+ #
636
+ # @option :opts [Boolean] :enable If false then the command is
637
+ # negated. Default is true.
638
+ #
639
+ # @option :opts [Boolean] :default Configure the primary IP address using
640
+ # the default keyword.
641
+ #
642
+ # @return [Boolean] returns true if the command complete successfully
643
+ def set_primary_ip(name, vrid, opts = {})
644
+ cmd = "vrrp #{vrid} ip"
645
+ configure_interface(name, command_builder(cmd, opts))
646
+ end
647
+
648
+ ##
649
+ # set_priority sets the priority for a virtual router.
650
+ #
651
+ # @commands
652
+ # interface <name>
653
+ # {no | default} vrrp <vrid> priority <priority>
654
+ #
655
+ # @param [String] :name The layer 3 interface name.
656
+ # @param [Integer] :vrid The virtual router ID.
657
+ # @param [hash] :opts Optional keyword arguments
658
+ #
659
+ # @option :opts [String] :value The priority value.
660
+ #
661
+ # @option :opts [Boolean] :enable If false then the command is
662
+ # negated. Default is true.
663
+ #
664
+ # @option :opts [Boolean] :default Configure the priority using
665
+ # the default keyword.
666
+ #
667
+ # @return [Boolean] returns true if the command complete successfully
668
+ def set_priority(name, vrid, opts = {})
669
+ cmd = "vrrp #{vrid} priority"
670
+ configure_interface(name, command_builder(cmd, opts))
671
+ end
672
+
673
+ ##
674
+ # set_description sets the description for a virtual router.
675
+ #
676
+ # @commands
677
+ # interface <name>
678
+ # {no | default} vrrp <vrid> description <description>
679
+ #
680
+ # @param [String] :name The layer 3 interface name.
681
+ # @param [Integer] :vrid The virtual router ID.
682
+ # @param [hash] :opts Optional keyword arguments
683
+ #
684
+ # @option :opts [String] :value The description value.
685
+ #
686
+ # @option :opts [Boolean] :enable If false then the command is
687
+ # negated. Default is true.
688
+ #
689
+ # @option :opts [Boolean] :default Configure the description using
690
+ # the default keyword.
691
+ #
692
+ # @return [Boolean] returns true if the command complete successfully
693
+ def set_description(name, vrid, opts = {})
694
+ cmd = "vrrp #{vrid} description"
695
+ configure_interface(name, command_builder(cmd, opts))
696
+ end
697
+
698
+ ##
699
+ # build_secondary_ip_cmd builds the array of commands required
700
+ # to update the secondary IP addresses. This method allows the
701
+ # create methods to leverage the code in the setter.
702
+ #
703
+ # @api private
704
+ #
705
+ # @param [String] :name The layer 3 interface name.
706
+ #
707
+ # @param [Integer] :vrid The virtual router ID.
708
+ #
709
+ # @param [Array<String>] :ip_addrs Array of secondary IPv4 address.
710
+ # An empty array will remove all secondary IPv4 addresses set for
711
+ # the virtual router on the specified layer 3 interface.
712
+ #
713
+ # @return [Array<String>] Returns the array of commands. The
714
+ # array could be empty.
715
+ def build_secondary_ip_cmd(name, vrid, ip_addrs)
716
+ ip_addrs = Set.new ip_addrs
717
+
718
+ # Get the current secondary IP address set for the virtual router
719
+ # A return of nil means that nothing has been configured for
720
+ # the virtual router.
721
+ vrrp = get(name)
722
+ vrrp = [] if vrrp.nil?
723
+
724
+ if vrrp.key?(vrid)
725
+ current_addrs = Set.new vrrp[vrid][:secondary_ip]
726
+ else
727
+ current_addrs = Set.new []
728
+ end
729
+
730
+ cmds = []
731
+ # Add commands to delete any secondary IP addresses that are
732
+ # currently set for the virtual router but not in ip_addrs.
733
+ current_addrs.difference(ip_addrs).each do |addr|
734
+ cmds << "no vrrp #{vrid} ip #{addr} secondary"
735
+ end
736
+
737
+ # Add commands to add any secondary IP addresses that are
738
+ # not currently set for the virtual router but are in ip_addrs.
739
+ ip_addrs.difference(current_addrs).each do |addr|
740
+ cmds << "vrrp #{vrid} ip #{addr} secondary"
741
+ end
742
+ cmds
743
+ end
744
+ private :build_secondary_ip_cmd
745
+
746
+ # set_secondary_ips configures the set of secondary IP addresses
747
+ # associated with the virtual router. The ip_addrs value passed
748
+ # should be an array of IP Addresses. This method will remove
749
+ # secondary IP addresses that are currently set for the virtual
750
+ # router but not included in the ip_addrs array value passed in.
751
+ # The method will then add secondary IP addresses that are not
752
+ # currently set for the virtual router but are included in the
753
+ # ip_addrs array value passed in.
754
+ #
755
+ # @commands
756
+ # interface <name>
757
+ # {no} vrrp <vrid> ip <A.B.C.D> secondary
758
+ #
759
+ # @param [String] :name The layer 3 interface name.
760
+ #
761
+ # @param [Integer] :vrid The virtual router ID.
762
+ #
763
+ # @param [Array<String>] :ip_addrs Array of secondary IPv4 address.
764
+ # An empty array will remove all secondary IPv4 addresses set for
765
+ # the virtual router on the specified layer 3 interface.
766
+ #
767
+ # @return [Boolean] returns true if the command complete successfully
768
+ def set_secondary_ip(name, vrid, ip_addrs)
769
+ cmds = build_secondary_ip_cmd(name, vrid, ip_addrs)
770
+ return true if cmds.empty?
771
+ configure_interface(name, cmds)
772
+ end
773
+
774
+ ##
775
+ # set_ip_version sets the VRRP version for a virtual router.
776
+ #
777
+ # @commands
778
+ # interface <name>
779
+ # {no | default} vrrp <vrid> ip version <version>
780
+ #
781
+ # @param [String] :name The layer 3 interface name.
782
+ # @param [Integer] :vrid The virtual router ID.
783
+ # @param [hash] :opts Optional keyword arguments
784
+ #
785
+ # @option :opts [String] :value The VRRP version.
786
+ #
787
+ # @option :opts [Boolean] :enable If false then the command is
788
+ # negated. Default is true.
789
+ #
790
+ # @option :opts [Boolean] :default Configure the VRRP version using
791
+ # the default keyword.
792
+ #
793
+ # @return [Boolean] returns true if the command complete successfully
794
+ def set_ip_version(name, vrid, opts = {})
795
+ cmd = "vrrp #{vrid} ip version"
796
+ configure_interface(name, command_builder(cmd, opts))
797
+ end
798
+
799
+ ##
800
+ # set_timers_advertise sets the interval between successive
801
+ # advertisement messages that the switch sends to routers in the
802
+ # specified virtual router ID.
803
+ #
804
+ # @commands
805
+ # interface <name>
806
+ # {no | default} vrrp <vrid> timers advertise <secs>
807
+ #
808
+ # @param [String] :name The layer 3 interface name.
809
+ # @param [Integer] :vrid The virtual router ID.
810
+ # @param [hash] :opts Optional keyword arguments
811
+ #
812
+ # @option :opts [String] :value The timer value in seconds.
813
+ #
814
+ # @option :opts [Boolean] :enable If false then the command is
815
+ # negated. Default is true.
816
+ #
817
+ # @option :opts [Boolean] :default Configure the timer advertise value
818
+ # using the default keyword.
819
+ #
820
+ # @return [Boolean] returns true if the command complete successfully
821
+ def set_timers_advertise(name, vrid, opts = {})
822
+ cmd = "vrrp #{vrid} timers advertise"
823
+ configure_interface(name, command_builder(cmd, opts))
824
+ end
825
+
826
+ ##
827
+ # set_mac_addr_adv_interval sets the interval in seconds between
828
+ # advertisement packets sent to VRRP group members for the
829
+ # specified virtual router ID.
830
+ #
831
+ # @commands
832
+ # interface <name>
833
+ # {no | default} vrrp <vrid> mac-address advertisement-interval <secs>
834
+ #
835
+ # @param [String] :name The layer 3 interface name.
836
+ # @param [Integer] :vrid The virtual router ID.
837
+ # @param [hash] :opts Optional keyword arguments
838
+ #
839
+ # @option :opts [String] :value The mac address advertisement interval
840
+ # value in seconds.
841
+ #
842
+ # @option :opts [Boolean] :enable If false then the command is
843
+ # negated. Default is true.
844
+ #
845
+ # @option :opts [Boolean] :default Configure the timer advertise value
846
+ # using the default keyword.
847
+ #
848
+ # @return [Boolean] returns true if the command complete successfully
849
+ def set_mac_addr_adv_interval(name, vrid, opts = {})
850
+ cmd = "vrrp #{vrid} mac-address advertisement-interval"
851
+ configure_interface(name, command_builder(cmd, opts))
852
+ end
853
+
854
+ ##
855
+ # set_preempt sets the virtual router's preempt mode setting. When
856
+ # preempt mode is enabled, if the switch has a higher priority it
857
+ # will preempt the current master virtual router. When preempt mode
858
+ # is disabled, the switch can become the master virtual router only
859
+ # when a master virtual router is not present on the subnet,
860
+ # regardless of priority settings.
861
+ #
862
+ # @commands
863
+ # interface <name>
864
+ # {no | default} vrrp <vrid> preempt
865
+ #
866
+ # @param [String] :name The layer 3 interface name.
867
+ # @param [Integer] :vrid The virtual router ID.
868
+ # @param [hash] :opts Optional keyword arguments
869
+ #
870
+ # @option :opts [Boolean] :enable If enable is true then the virtual
871
+ # router preempt mode is administratively enabled for the interface
872
+ # and if enable is false then the virtual router preempt mode is
873
+ # administratively disabled for the interface. Default is true.
874
+ #
875
+ # @option :opts [Boolean] :default Configure the timer advertise value
876
+ # using the default keyword.
877
+ #
878
+ # @return [Boolean] returns true if the command complete successfully
879
+ def set_preempt(name, vrid, opts = {})
880
+ fail 'set_preempt has the value option set' if opts[:value]
881
+ cmd = "vrrp #{vrid} preempt"
882
+ configure_interface(name, command_builder(cmd, opts))
883
+ end
884
+
885
+ ##
886
+ # set_preempt_delay_min sets the minimum time in seconds for the
887
+ # virtual router to wait before taking over the active role.
888
+ #
889
+ # @commands
890
+ # interface <name>
891
+ # {no | default} vrrp <vrid> preempt delay minimum <secs>
892
+ #
893
+ # @param [String] :name The layer 3 interface name.
894
+ # @param [Integer] :vrid The virtual router ID.
895
+ # @param [hash] :opts Optional keyword arguments
896
+ #
897
+ # @option :opts [String] :value The preempt delay minimum value.
898
+ #
899
+ # @option :opts [Boolean] :enable If false then the command is
900
+ # negated. Default is true.
901
+ #
902
+ # @option :opts [Boolean] :default Configure the preempt delay minimum
903
+ # value using the default keyword.
904
+ #
905
+ # @return [Boolean] returns true if the command complete successfully
906
+ def set_preempt_delay_min(name, vrid, opts = {})
907
+ cmd = "vrrp #{vrid} preempt delay minimum"
908
+ configure_interface(name, command_builder(cmd, opts))
909
+ end
910
+
911
+ ##
912
+ # set_preempt_delay_reload sets the preemption delay after a reload
913
+ # only. This delay period applies only to the first interface-up
914
+ # event after the virtual router has reloaded.
915
+ #
916
+ # @commands
917
+ # interface <name>
918
+ # {no | default} vrrp <vrid> preempt delay reload <secs>
919
+ #
920
+ # @param [String] :name The layer 3 interface name.
921
+ # @param [Integer] :vrid The virtual router ID.
922
+ # @param [hash] :opts Optional keyword arguments
923
+ #
924
+ # @option :opts [String] :value The preempt delay reload value.
925
+ #
926
+ # @option :opts [Boolean] :enable If false then the command is
927
+ # negated. Default is true.
928
+ #
929
+ # @option :opts [Boolean] :default Configure the preempt delay reload
930
+ # value using the default keyword.
931
+ #
932
+ # @return [Boolean] returns true if the command complete successfully
933
+ def set_preempt_delay_reload(name, vrid, opts = {})
934
+ cmd = "vrrp #{vrid} preempt delay reload"
935
+ configure_interface(name, command_builder(cmd, opts))
936
+ end
937
+
938
+ ##
939
+ # set_delay_reload sets the delay between system reboot and VRRP
940
+ # initialization for the virtual router.
941
+ #
942
+ # @commands
943
+ # interface <name>
944
+ # {no | default} vrrp <vrid> delay reload <secs>
945
+ #
946
+ # @param [String] :name The layer 3 interface name.
947
+ # @param [Integer] :vrid The virtual router ID.
948
+ # @param [hash] :opts Optional keyword arguments
949
+ #
950
+ # @option :opts [String] :value The delay reload value.
951
+ #
952
+ # @option :opts [Boolean] :enable If false then the command is
953
+ # negated. Default is true.
954
+ #
955
+ # @option :opts [Boolean] :default Configure the delay reload
956
+ # value using the default keyword.
957
+ #
958
+ # @return [Boolean] returns true if the command complete successfully
959
+ def set_delay_reload(name, vrid, opts = {})
960
+ cmd = "vrrp #{vrid} delay reload"
961
+ configure_interface(name, command_builder(cmd, opts))
962
+ end
963
+
964
+ ##
965
+ # build_tracks_cmd builds the array of commands required
966
+ # to update the tracks. This method allows the
967
+ # create methods to leverage the code in the setter.
968
+ #
969
+ # @api private
970
+ #
971
+ # @param [String] :name The layer 3 interface name.
972
+ #
973
+ # @param [Integer] :vrid The virtual router ID.
974
+ #
975
+ # @param [Array<Hash>] :tracks Array of a hash of track information.
976
+ # Hash format: { name: 'Eth2', action: 'decrement', amount: 33 },
977
+ # The name and action key are required. The amount key should only
978
+ # be specified if the action is shutdown. The valid actions are
979
+ # 'decrement' and 'shutdown'. An empty array will remove all tracks
980
+ # set for the virtual router on the specified layer 3 interface.
981
+ #
982
+ # @return [Array<String>] Returns the array of commands. The
983
+ # array could be empty.
984
+ def build_tracks_cmd(name, vrid, tracks)
985
+ # Validate the track hash
986
+ valid_keys = [:name, :action, :amount]
987
+ # rubocop:disable Style/Next
988
+ tracks.each do |track|
989
+ track.keys do |key|
990
+ unless valid_keys.include?(key)
991
+ fail ArgumentError, 'Key: #{key} invalid in track hash'
992
+ end
993
+ end
994
+ unless track.key?(:name) && track.key?(:action)
995
+ fail ArgumentError, 'Must specify :name and :action in track hash'
996
+ end
997
+ unless track[:action] == 'decrement' || track[:action] == 'shutdown'
998
+ fail ArgumentError, "Action must be 'decrement' or 'shutdown'"
999
+ end
1000
+ if track.key?(:amount) && track[:action] != 'decrement'
1001
+ fail ArgumentError, "Action must be 'decrement' to set amount"
1002
+ end
1003
+ if track.key?(:amount)
1004
+ track[:amount] = track[:amount].to_i
1005
+ if track[:amount] < 0
1006
+ fail ArgumentError, 'Amount must be greater than zero'
1007
+ end
1008
+ end
1009
+ end
1010
+
1011
+ tracks = Set.new tracks
1012
+
1013
+ # Get the current tracks set for the virtual router
1014
+ # A return of nil means that nothing has been configured for
1015
+ # the virtual router.
1016
+ vrrp = get(name)
1017
+ vrrp = [] if vrrp.nil?
1018
+
1019
+ if vrrp.key?(vrid)
1020
+ current_tracks = Set.new vrrp[vrid][:track]
1021
+ else
1022
+ current_tracks = Set.new []
1023
+ end
1024
+
1025
+ cmds = []
1026
+ # Add commands to delete any tracks that are
1027
+ # currently set for the virtual router but not in tracks.
1028
+ current_tracks.difference(tracks).each do |tk|
1029
+ cmds << "no vrrp #{vrid} track #{tk[:name]} #{tk[:action]}"
1030
+ end
1031
+
1032
+ # Add commands to add any tracks that are
1033
+ # not currently set for the virtual router but are in tracks.
1034
+ tracks.difference(current_tracks).each do |tk|
1035
+ cmd = "vrrp #{vrid} track #{tk[:name]} #{tk[:action]}"
1036
+ cmd << " #{tk[:amount]}" if tk.key?(:amount)
1037
+ cmds << cmd
1038
+ end
1039
+ cmds
1040
+ end
1041
+ private :build_tracks_cmd
1042
+
1043
+ # set_tracks configures the set of track settings associated with
1044
+ # the virtual router. The tracks value passed should be an array of
1045
+ # hashes, each hash containing a track entry. This method will remove
1046
+ # tracks that are currently set for the virtual router but not included
1047
+ # in the tracks array value passed in. The method will then add
1048
+ # tracks that are not currently set for the virtual router but are
1049
+ # included in the tracks array value passed in.
1050
+ #
1051
+ # @commands
1052
+ # interface <name>
1053
+ # {no} vrrp <vrid> track <name> <action> [<amount>]
1054
+ #
1055
+ # @param [String] :name The layer 3 interface name.
1056
+ #
1057
+ # @param [Integer] :vrid The virtual router ID.
1058
+ #
1059
+ # @param [Array<Hash>] :tracks Array of a hash of track information.
1060
+ # Hash format: { name: 'Eth2', action: 'decrement', amount: 33 },
1061
+ # An empty array will remove all tracks set for
1062
+ # the virtual router on the specified layer 3 interface.
1063
+ #
1064
+ # @return [Boolean] returns true if the command complete successfully
1065
+ def set_tracks(name, vrid, tracks)
1066
+ cmds = build_tracks_cmd(name, vrid, tracks)
1067
+ return true if cmds.empty?
1068
+ configure_interface(name, cmds)
1069
+ end
1070
+ end
1071
+ end
1072
+ end