puppet_x_eos_eapi 0.2.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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +24 -0
  4. data/LICENSE.txt +202 -0
  5. data/README.md +87 -0
  6. data/Rakefile +1 -0
  7. data/lib/puppet_x/eos/autoload.rb +57 -0
  8. data/lib/puppet_x/eos/eapi.rb +259 -0
  9. data/lib/puppet_x/eos/module_base.rb +37 -0
  10. data/lib/puppet_x/eos/modules/daemon.rb +109 -0
  11. data/lib/puppet_x/eos/modules/extension.rb +167 -0
  12. data/lib/puppet_x/eos/modules/interface.rb +180 -0
  13. data/lib/puppet_x/eos/modules/ipinterface.rb +133 -0
  14. data/lib/puppet_x/eos/modules/mlag.rb +268 -0
  15. data/lib/puppet_x/eos/modules/ntp.rb +129 -0
  16. data/lib/puppet_x/eos/modules/ospf.rb +129 -0
  17. data/lib/puppet_x/eos/modules/portchannel.rb +277 -0
  18. data/lib/puppet_x/eos/modules/radius.rb +367 -0
  19. data/lib/puppet_x/eos/modules/snmp.rb +177 -0
  20. data/lib/puppet_x/eos/modules/switchport.rb +255 -0
  21. data/lib/puppet_x/eos/modules/system.rb +138 -0
  22. data/lib/puppet_x/eos/modules/tacacs.rb +302 -0
  23. data/lib/puppet_x/eos/modules/vlan.rb +179 -0
  24. data/lib/puppet_x/eos/modules/vxlan.rb +132 -0
  25. data/lib/puppet_x/eos/provider.rb +71 -0
  26. data/lib/puppet_x/eos/version.rb +41 -0
  27. data/lib/puppet_x/net_dev/eos_api.rb +1011 -0
  28. data/lib/puppet_x/net_dev/eos_api/common_methods.rb +27 -0
  29. data/lib/puppet_x/net_dev/eos_api/snmp_methods.rb +647 -0
  30. data/lib/puppet_x/net_dev/eos_api/version.rb +8 -0
  31. data/lib/puppet_x_eos_eapi.rb +4 -0
  32. data/puppet_x_eos_eapi.gemspec +31 -0
  33. data/spec/fixtures/fixture_all_portchannel_modes.json +8 -0
  34. data/spec/fixtures/fixture_all_portchannels_detailed.json +15 -0
  35. data/spec/fixtures/fixture_create_vlan_error.json +17 -0
  36. data/spec/fixtures/fixture_create_vlan_success.json +12 -0
  37. data/spec/fixtures/fixture_eapi_conf.yaml +4 -0
  38. data/spec/fixtures/fixture_enable_configure_vlan_3111_name_foo.json +14 -0
  39. data/spec/fixtures/fixture_enable_configure_vlan_foo_name_bar.json +19 -0
  40. data/spec/fixtures/fixture_get_snmp_communities_non_existent_acl.yaml +2 -0
  41. data/spec/fixtures/fixture_get_snmp_location_westeros.json +5 -0
  42. data/spec/fixtures/fixture_portchannel_min_links_1.json +8 -0
  43. data/spec/fixtures/fixture_portchannel_min_links_2.json +8 -0
  44. data/spec/fixtures/fixture_running_config.yaml +1 -0
  45. data/spec/fixtures/fixture_running_configuration_radius_configured.yaml +30 -0
  46. data/spec/fixtures/fixture_running_configuration_radius_default.yaml +29 -0
  47. data/spec/fixtures/fixture_running_configuration_radius_server_groups.yaml +38 -0
  48. data/spec/fixtures/fixture_running_configuration_radius_servers.yaml +34 -0
  49. data/spec/fixtures/fixture_running_configuration_tacacs_configured.yaml +38 -0
  50. data/spec/fixtures/fixture_running_configuration_tacacs_default.yaml +38 -0
  51. data/spec/fixtures/fixture_running_configuration_tacacs_groups.yaml +1 -0
  52. data/spec/fixtures/fixture_running_configuration_tacacs_groups_3.yaml +43 -0
  53. data/spec/fixtures/fixture_running_configuration_tacacs_servers.yaml +41 -0
  54. data/spec/fixtures/fixture_s4_show_etherchannel_detailed.json +9 -0
  55. data/spec/fixtures/fixture_show_flowcontrol_et1.json +5 -0
  56. data/spec/fixtures/fixture_show_interfaces.json +297 -0
  57. data/spec/fixtures/fixture_show_interfaces_switchport_format_text.json +9 -0
  58. data/spec/fixtures/fixture_show_port_channel_summary_2_lags.json +9 -0
  59. data/spec/fixtures/fixture_show_port_channel_summary_static.json +9 -0
  60. data/spec/fixtures/fixture_show_snmp_community.yaml +2 -0
  61. data/spec/fixtures/fixture_show_snmp_contact_empty.json +5 -0
  62. data/spec/fixtures/fixture_show_snmp_contact_name.json +5 -0
  63. data/spec/fixtures/fixture_show_snmp_disabled.json +5 -0
  64. data/spec/fixtures/fixture_show_snmp_enabled.json +5 -0
  65. data/spec/fixtures/fixture_show_snmp_host.yaml +2 -0
  66. data/spec/fixtures/fixture_show_snmp_host_duplicates.yaml +2 -0
  67. data/spec/fixtures/fixture_show_snmp_host_more_duplicates.yaml +2 -0
  68. data/spec/fixtures/fixture_show_snmp_location_empty.json +5 -0
  69. data/spec/fixtures/fixture_show_snmp_trap.yaml +2 -0
  70. data/spec/fixtures/fixture_show_snmp_user.yaml +2 -0
  71. data/spec/fixtures/fixture_show_snmp_user_raw_text.yaml +1 -0
  72. data/spec/fixtures/fixture_show_vlan.json +37 -0
  73. data/spec/fixtures/fixture_show_vlan_3110.json +18 -0
  74. data/spec/fixtures/fixture_show_vlan_4000.json +18 -0
  75. data/spec/fixtures/fixture_snmp_host_opts.yaml +11 -0
  76. data/spec/spec_helper.rb +21 -0
  77. data/spec/support/fixtures.rb +104 -0
  78. data/spec/unit/puppet_x/eos/eapi_spec.rb +182 -0
  79. data/spec/unit/puppet_x/eos/module_base_spec.rb +26 -0
  80. data/spec/unit/puppet_x/eos/modules/daemon_spec.rb +110 -0
  81. data/spec/unit/puppet_x/eos/modules/extension_spec.rb +197 -0
  82. data/spec/unit/puppet_x/eos/modules/fixtures/daemon_getall.json +3 -0
  83. data/spec/unit/puppet_x/eos/modules/fixtures/extension_getall.json +28 -0
  84. data/spec/unit/puppet_x/eos/modules/fixtures/hostname.json +6 -0
  85. data/spec/unit/puppet_x/eos/modules/fixtures/interface_getall.json +509 -0
  86. data/spec/unit/puppet_x/eos/modules/fixtures/ipinterface_getall.json +56 -0
  87. data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get.json +21 -0
  88. data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get_interfaces.json +18 -0
  89. data/spec/unit/puppet_x/eos/modules/fixtures/ntp_get.json +5 -0
  90. data/spec/unit/puppet_x/eos/modules/fixtures/ospf_instance_getall.json +58 -0
  91. data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_get.json +54 -0
  92. data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getlacpmode.json +5 -0
  93. data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getmembers.json +5 -0
  94. data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_po1.json +7 -0
  95. data/spec/unit/puppet_x/eos/modules/fixtures/snmp_get.json +14 -0
  96. data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get.json +5 -0
  97. data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get_et1.json +7 -0
  98. data/spec/unit/puppet_x/eos/modules/fixtures/switchport_getall_interfaces.json +230 -0
  99. data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_list.json +5 -0
  100. data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_name.json +5 -0
  101. data/spec/unit/puppet_x/eos/modules/fixtures/system_hostname.json +6 -0
  102. data/spec/unit/puppet_x/eos/modules/fixtures/system_name_servers.json +5 -0
  103. data/spec/unit/puppet_x/eos/modules/fixtures/vlan_getall.json +123 -0
  104. data/spec/unit/puppet_x/eos/modules/fixtures/vxlan_get.json +24 -0
  105. data/spec/unit/puppet_x/eos/modules/interface_spec.rb +281 -0
  106. data/spec/unit/puppet_x/eos/modules/ipinterface_spec.rb +143 -0
  107. data/spec/unit/puppet_x/eos/modules/mlag_spec.rb +349 -0
  108. data/spec/unit/puppet_x/eos/modules/ntp_spec.rb +136 -0
  109. data/spec/unit/puppet_x/eos/modules/ospf_spec.rb +143 -0
  110. data/spec/unit/puppet_x/eos/modules/portchannel_spec.rb +357 -0
  111. data/spec/unit/puppet_x/eos/modules/radius_spec.rb +509 -0
  112. data/spec/unit/puppet_x/eos/modules/snmp_spec.rb +202 -0
  113. data/spec/unit/puppet_x/eos/modules/switchport_get_et1.json +7 -0
  114. data/spec/unit/puppet_x/eos/modules/switchport_spec.rb +307 -0
  115. data/spec/unit/puppet_x/eos/modules/system_spec.rb +170 -0
  116. data/spec/unit/puppet_x/eos/modules/tacacs_spec.rb +448 -0
  117. data/spec/unit/puppet_x/eos/modules/vlan_spec.rb +244 -0
  118. data/spec/unit/puppet_x/eos/modules/vxlan_spec.rb +189 -0
  119. data/spec/unit/puppet_x/eos/provider_spec.rb +35 -0
  120. data/spec/unit/puppet_x/net_dev/eos_api/common_methods_spec.rb +34 -0
  121. data/spec/unit/puppet_x/net_dev/eos_api/snmp_methods_spec.rb +842 -0
  122. data/spec/unit/puppet_x/net_dev/eos_api_spec.rb +1000 -0
  123. metadata +369 -0
@@ -0,0 +1,129 @@
1
+ #
2
+ # Copyright (c) 2014, 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
+
33
+ ##
34
+ # PuppetX is the toplevel namespace for working with Arista EOS nodes
35
+ module PuppetX
36
+ ##
37
+ # Eos is module namesapce for working with the EOS command API
38
+ module Eos
39
+ ##
40
+ # The Ospf class provides a base class instance for working with
41
+ # instances of OSPF
42
+ #
43
+ class Ospf
44
+ ##
45
+ # Initialize instance of Ospf
46
+ #
47
+ # @param [PuppetX::Eos::Eapi] api An instance of Eapi
48
+ #
49
+ # @return [PuppetX::Eos::Ospf]
50
+ def initialize(api)
51
+ @api = api
52
+ end
53
+
54
+ ##
55
+ # Returns the base interface hash representing physical and logical
56
+ # interfaces in EOS using eAPI
57
+ #
58
+ # Example
59
+ # {
60
+ # "instances": {
61
+ # "1": { "router_id": <String> }
62
+ # }
63
+ # }
64
+ #
65
+ # @return [Hash] returns an Hash
66
+ def getall
67
+ result = @api.enable('show ip ospf')
68
+ instances = {}
69
+ result[0]['vrfs']['default']['instList'].map do |inst, attrs|
70
+ instances[inst] = { router_id: attrs['routerId'] }
71
+ end
72
+ { instances: instances }
73
+ end
74
+
75
+ ##
76
+ # Creates a new instance of OSPF routing
77
+ #
78
+ # @param [String] inst The instance id to create
79
+ #
80
+ # @return [Boolean] True if the commands succeed otherwise False
81
+ def create(inst)
82
+ @api.config("router ospf #{inst}") == [{}]
83
+ end
84
+
85
+ ##
86
+ # Deletes an instance of OSPF routing
87
+ #
88
+ # @param [String] inst The instance id to delete
89
+ #
90
+ # @return [Boolean] True if the commands succeed otherwise False
91
+ def delete(inst)
92
+ @api.config("no router ospf #{inst}") == [{}]
93
+ end
94
+
95
+ ##
96
+ # Defaults an instance of OSPF routing
97
+ #
98
+ # @param [String] inst The instance id to delete
99
+ #
100
+ # @return [Boolean] True if the commands succeed otherwise False
101
+ def default(inst)
102
+ @api.config("default router ospf #{inst}") == [{}]
103
+ end
104
+
105
+ ##
106
+ # Configures the OSPF process router-id
107
+ #
108
+ # @param [String] inst The instance of ospf to configure
109
+ # @param [Hash] opts The configuration parameters
110
+ # @option opts [string] :value The value to set the router-id to
111
+ # @option opts [Boolean] :default The value should be set to default
112
+ #
113
+ # @return [Boolean] True if the commands succeed otherwise False
114
+ def set_router_id(inst, opts = {})
115
+ value = opts[:value] || false
116
+ default = opts[:default] || false
117
+
118
+ cmds = ["router ospf #{inst}"]
119
+ case default
120
+ when true
121
+ cmds << 'default router-id'
122
+ when false
123
+ cmds << (value ? "router-id #{value}" : 'no router-id')
124
+ end
125
+ @api.config(cmds) == [{}, {}]
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,277 @@
1
+ #
2
+ # Copyright (c) 2014, 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
+
33
+ ##
34
+ # PuppetX is the toplevel namespace for working with Arista EOS nodes
35
+ module PuppetX
36
+ ##
37
+ # Eos is module namesapce for working with the EOS command API
38
+ module Eos
39
+ ##
40
+ # The Portchannel class provides a base class instance for working with
41
+ # logical link aggregation interfaces.
42
+ #
43
+ class Portchannel
44
+ ##
45
+ # Initialize innstance of Portchannel
46
+ #
47
+ # @param [PuppetX::Eos::Eapi] api An instance of Eapi
48
+ #
49
+ # @return [PuppetX::Eos::Interface]
50
+ def initialize(api)
51
+ @api = api
52
+ end
53
+
54
+ ##
55
+ # Retrievess a port channel interface from the running-configuration
56
+ #
57
+ # Example
58
+ # {
59
+ # "name": <String>,
60
+ # "lacp_mode": [active, passive, off],
61
+ # "members": [Array],
62
+ # "lacp_fallback": [static, individual],
63
+ # "lacp_timeout": <0-900>
64
+ # }
65
+ #
66
+ # @param [String] name The name of the port-channel interface
67
+ #
68
+ # @return [Hash] A hash of the port channel attributes and properties
69
+ def get(name)
70
+ members = get_members name
71
+ result = @api.enable("show interfaces #{name}")
72
+ interface = result.first['interfaces']
73
+
74
+ attr_hash = { name: name }
75
+ attr_hash[:members] = members
76
+ attr_hash[:lacp_mode] = get_lacp_mode members
77
+ attr_hash[:lacp_fallback] = get_lacp_fallback interface
78
+ attr_hash[:lacp_timeout] = interface['fallbackTimeout']
79
+ attr_hash
80
+ end
81
+
82
+ ##
83
+ # Retrieves all logical port-channel interfaces from the running config
84
+ #
85
+ # @return [Array] an array of port-channel attribute hashes
86
+ def getall
87
+ result = @api.enable('show interfaces')
88
+ interfaces = result.first['interfaces']
89
+ resp = []
90
+ interfaces.each do |key, value|
91
+ resp << get(key) if value['hardware'] == 'portChannel'
92
+ end
93
+ resp
94
+ end
95
+
96
+ ##
97
+ # Retreives the member interfaces for the specified channel group
98
+ #
99
+ # @param [String] name The name of the port-channel interface to return
100
+ # members for
101
+ #
102
+ # @return [Array] An array of interface names that are members of the
103
+ # specified channel group id
104
+ def get_members(name)
105
+ result = @api.enable("show #{name} all-ports",
106
+ format: 'text')
107
+ result.first['output'].scan(/Ethernet\d+/)
108
+ end
109
+
110
+ ##
111
+ # Create a logical port-channel interface
112
+ #
113
+ # @param [String] name The name of the interface to create
114
+ #
115
+ # @return [Boolean] True if the create succeeds otherwise False
116
+ def create(name)
117
+ @api.config("interface #{name}") == [{}]
118
+ end
119
+
120
+ ##
121
+ # Deletes a logical port-channel interface
122
+ #
123
+ # @param [String] name The name of the interface to create
124
+ #
125
+ # @return [Boolean] True if the create succeeds otherwise False
126
+ def delete(name)
127
+ @api.config("no interface #{name}") == [{}]
128
+ end
129
+
130
+ ##
131
+ # Defaults a logical port-channel interface
132
+ #
133
+ # @param [String] name The name of the interface to create
134
+ #
135
+ # @return [Boolean] True if the create succeeds otherwise False
136
+ def default(name)
137
+ @api.config("default interface #{name}") == [{}]
138
+ end
139
+
140
+ ##
141
+ # Adds a new member interface to the channel group
142
+ #
143
+ # @param [String] name The name of the port channel to add the interface
144
+ # @param [String] member The name of the interface to add
145
+ #
146
+ # @return [Boolean] True if the create succeeds otherwise False
147
+ def add_member(name, member)
148
+ id = name.match(/\d+/)
149
+ @api.config(["interface #{member}",
150
+ "channel-group #{id} mode on"]) == [{}, {}]
151
+ end
152
+
153
+ ##
154
+ # Removes a member interface from the channel group
155
+ #
156
+ # @param [String] name The name of the port-channel to add the interface
157
+ # @param [String] member The name of the interface to remove
158
+ #
159
+ # @return [Boolean] True if the create succeeds otherwise False
160
+ def remove_member(_name, member)
161
+ @api.config(["interface #{member}", 'no channel-group']) == [{}, {}]
162
+ end
163
+
164
+ ##
165
+ # Configures the member interfaces for the port-channel interface
166
+ #
167
+ # @param [String] name The name of the port-channel to assign the
168
+ # the members to
169
+ # @param [Array] members The array of members to add to the port-channel
170
+ def set_members(name, members)
171
+ current = get_members(name)
172
+ (current - members).each do |member|
173
+ remove_member(name, member)
174
+ end
175
+ (members - current).each do |member|
176
+ add_member(name, member)
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Configures the lacp mode for the interface
182
+ #
183
+ # @param [String] name The name of the port-channel interface
184
+ # @param [String] mode The LACP mode to configure
185
+ #
186
+ # @return [Boolean] True if the create succeeds otherwise False
187
+ def set_lacp_mode(name, mode)
188
+ id = name.match(/\d+/)
189
+ members = get_members name
190
+
191
+ commands = []
192
+ config = []
193
+
194
+ members.each do |member|
195
+ commands << "interface #{member}" << 'no channel-group'
196
+ config << "interface #{member}" << "channel-group #{id} mode #{mode}"
197
+ end
198
+
199
+ config.unshift(*commands)
200
+ result = @api.config(config)
201
+ config.size == result.size
202
+ end
203
+
204
+ ##
205
+ # Configures the lacp fallback value
206
+ #
207
+ # @param [String] name The name of the interface to configure
208
+ # @param [Hash] opts The configuration parameters for the interface
209
+ # @option opts [string] :value The value to set the value to
210
+ # @option opts [Boolean] :default The value should be set to default
211
+ #
212
+ # @return [Boolean] True if the commands succeed otherwise False
213
+ def set_lacp_fallback(name, opts = {})
214
+ value = opts[:value]
215
+ default = opts[:default] || false
216
+
217
+ cmds = ["interface #{name}"]
218
+ case default
219
+ when true
220
+ cmds << 'default port-channel lacp fallback'
221
+ when false
222
+ cmds << (value.nil? ? "no port-channel lacp fallback #{value}" : \
223
+ "port-channel lacp fallback #{value}")
224
+ end
225
+ @api.config(cmds) == [{}, {}]
226
+ end
227
+
228
+ ##
229
+ # Configures the lacp fallback timeout value
230
+ #
231
+ # @param [String] name The name of the interface to configure
232
+ # @param [Hash] opts The configuration parameters for the interface
233
+ # @option opts [string] :value The value to set the timeout to
234
+ # @option opts [Boolean] :default The value should be set to default
235
+ #
236
+ # @return [Boolean] True if the commands succeed otherwise False
237
+ def set_lacp_timeout(name, opts = {})
238
+ value = opts[:value]
239
+ default = opts[:default] || false
240
+
241
+ cmds = ["interface #{name}"]
242
+ case default
243
+ when true
244
+ cmds << 'default port-channel lacp fallback timeout'
245
+ when false
246
+ cmds << (value.nil? ? 'no port-channel lacp fallback timeout' : \
247
+ "port-channel lacp fallback timeout #{value}")
248
+ end
249
+ @api.config(cmds) == [{}, {}]
250
+ end
251
+
252
+ private
253
+
254
+ def get_lacp_mode(members)
255
+ return '' if members.empty?
256
+ name = members.first
257
+ result = @api.enable("show running-config interfaces #{name}",
258
+ format: 'text')
259
+ m = /channel-group\s\d+\smode\s(?<lacp>.*)/
260
+ .match(result.first['output'])
261
+ m['lacp']
262
+ end
263
+
264
+ def get_lacp_fallback(attr_hash)
265
+ if attr_hash['fallbackEnabled']
266
+ case attr_hash['fallbackEnabledType']
267
+ when 'fallbackStatic'
268
+ fallback = 'static'
269
+ when 'fallbackIndividual'
270
+ fallback = 'individual'
271
+ end
272
+ end
273
+ fallback || ''
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,367 @@
1
+ # encoding: utf-8
2
+
3
+ module PuppetX
4
+ ##
5
+ # Eos is module namesapce for working with the EOS command API
6
+ module Eos
7
+ ##
8
+ # Radius provides instance methods to retrieve and set radius configuration
9
+ # values.
10
+ class Radius < ModuleBase
11
+ DEFAULT_AUTH_PORT = 1812
12
+ DEFAULT_ACCT_PORT = 1813
13
+
14
+ # Regular expression to extract a radius server's attributes from the
15
+ # running-configuration text. The explicit [ ] spaces enable line
16
+ # wrappping and indentation with the /x flag.
17
+ SERVER_REGEXP = /radius-server[ ]host[ ](.*?)
18
+ (?:[ ]auth-port[ ](\d+))?
19
+ (?:[ ]acct-port[ ](\d+))?
20
+ (?:[ ]timeout[ ](\d+))?
21
+ (?:[ ]deadtime[ ](\d+))?
22
+ (?:[ ]retransmit[ ](\d+))?
23
+ (?:[ ]key[ ](\d+)[ ](\w+))?\s/x
24
+
25
+ GROUP_MEMBER_REGEXP = /server[ ](.*?)
26
+ (?:[ ]auth-port[ ](\d+))?
27
+ (?:[ ]acct-port[ ](\d+))?\s/x
28
+
29
+ # Regular expression to extract a radius server's attributes from the
30
+ # running-configuration text. The explicit [ ] spaces enable line
31
+ # wrappping and indentation with the /x flag.
32
+ SERVER_GROUP_REGEXP = /aaa group server radius (.*)/
33
+
34
+ ##
35
+ # getall Returns an Array with a single resource Hash describing the
36
+ # current state of the global radius configuration on the target device.
37
+ # This method is intended to be used by a provider's instances class
38
+ # method.
39
+ #
40
+ # The resource hash returned contains the following information:
41
+ # * name: ('settings')
42
+ # * enable: (true | false) if radius functionality is enabled. This is
43
+ # always true for EOS.
44
+ # * key: (String) the key either in plaintext or hashed format
45
+ # * key_format: (Integer) e.g. 0 or 7
46
+ # * timeout: (Integer) seconds before the timeout period ends
47
+ # * retransmit_count: (Integer), e.g. 3, attempts after first timeout
48
+ # expiry.
49
+ #
50
+ # @api public
51
+ #
52
+ # @return [Array<Hash>] Single element Array of resource hashes
53
+ def getall
54
+ config = running_configuration
55
+ rsrc_hsh = radius_global_defaults
56
+ rsrc_hsh.merge!(parse_global_key(config))
57
+ rsrc_hsh.merge!(parse_global_timeout(config))
58
+ rsrc_hsh.merge!(parse_global_retransmit(config))
59
+ [rsrc_hsh]
60
+ end
61
+
62
+ ##
63
+ # servers returns an Array of radius server resource hashes. Each hash
64
+ # describes the current state of the radius server and is suitable for
65
+ # use in initializing a radius_server provider.
66
+ #
67
+ # The resource hash returned contains the following information:
68
+ # * hostname: hostname or ip address
69
+ # * key: (String) the key either in plaintext or hashed format
70
+ # * key_format: (Fixnum) e.g. 0 or 7
71
+ # * timeout: (Fixnum) seconds before the timeout period ends
72
+ # * retransmit_count: (Integer), e.g. 3, attempts after first timeout
73
+ # expiry.
74
+ # * group: (String) Server group associated with this server.
75
+ # * deadtime: (Fixnum) number of minutes to ignore an unresponsive
76
+ # server.
77
+ # * acct_port: (Fixnum) Port number to use for accounting.
78
+ # * accounting_only: (Boolean) Enable this server for accounting only.
79
+ # * auth_port: (Fixnum) Port number to use for authentication
80
+ #
81
+ # @api public
82
+ #
83
+ # @return [Array<Hash<Symbol,Object>>] Array of resource hashes
84
+ def servers
85
+ config = running_configuration
86
+ tuples = config.scan(SERVER_REGEXP)
87
+ tuples.map do |(host, authp, acctp, tout, dead, tries, keyfm, key)|
88
+ hsh = { auth_port: DEFAULT_AUTH_PORT, acct_port: DEFAULT_ACCT_PORT }
89
+ hsh[:hostname] = host if host
90
+ hsh[:auth_port] = authp.to_i if authp
91
+ hsh[:acct_port] = acctp.to_i if acctp
92
+ hsh[:timeout] = tout.to_i if tout
93
+ hsh[:retransmit_count] = tries.to_i if tries
94
+ hsh[:deadtime] = dead.to_i if dead
95
+ hsh[:key_format] = keyfm.to_i if keyfm
96
+ hsh[:key] = key if key
97
+ hsh
98
+ end
99
+ end
100
+
101
+ ##
102
+ # server_groups retrieves a list of radius server groups from the target
103
+ # device.
104
+ #
105
+ # @api public
106
+ #
107
+ # @return [Array<Hash<Symbol,Object>>] Array of resource hashes
108
+ def server_groups
109
+ config = running_configuration
110
+ tuples = config.scan(SERVER_GROUP_REGEXP)
111
+ tuples.map do |(name)|
112
+ { name: name, servers: parse_group_servers(config, name) }
113
+ end
114
+ end
115
+
116
+ ##
117
+ # parse_group_servers parses the list of servers associated with a radius
118
+ # server group given a group name and a running configuration text.
119
+ #
120
+ # @param [String] config The running configuration text.
121
+ #
122
+ # @param [String] name The name of the server group to parse.
123
+ #
124
+ # @api private
125
+ #
126
+ # @return [Array<Hash<Symbol,Object>] Array of server attributes
127
+ def parse_group_servers(config, name)
128
+ regexp = /aaa group server radius #{name}(.*?)!/m
129
+ mdata = regexp.match(config)
130
+ if mdata
131
+ tuples = mdata[1].scan(GROUP_MEMBER_REGEXP)
132
+ tuples.collect do |(hostname, auth_port, acct_port)|
133
+ {
134
+ hostname: hostname,
135
+ auth_port: auth_port ? auth_port.to_i : DEFAULT_AUTH_PORT,
136
+ acct_port: acct_port ? acct_port.to_i : DEFAULT_ACCT_PORT
137
+ }
138
+ end
139
+ else
140
+ Array.new
141
+ end
142
+ end
143
+
144
+ ##
145
+ # update_server_group updates a radius server group given an Array of
146
+ # server attributes and the name of the server group. The update happens
147
+ # by first deleting the existing group if it exists then creating it
148
+ # again with all of the specified servers.
149
+ #
150
+ # @param [String] name The name of the server group to update
151
+ #
152
+ # @param [Array<Hash<Symbol,Object>>] servers The array of servers to
153
+ # associate with the server group. This hash should have at least the
154
+ # :hostname key.
155
+ #
156
+ # @api public
157
+ #
158
+ # @return [Boolean] true if no errors
159
+ def update_server_group(opts = {})
160
+ cmd = "aaa group server radius #{opts[:name]}"
161
+ api.config("no #{cmd}")
162
+ cmds = [cmd]
163
+ opts[:servers].each do |hsh|
164
+ server = "server #{hsh[:hostname]}"
165
+ server << " auth-port #{hsh[:auth_port] || DEFAULT_AUTH_PORT}"
166
+ server << " acct-port #{hsh[:acct_port] || DEFAULT_ACCT_PORT}"
167
+ cmds << server
168
+ end
169
+ result = api.config(cmds)
170
+ !result.find { |r| r != {} }
171
+ end
172
+
173
+ ##
174
+ # remove_server_group removes a radius server group by name. This API
175
+ # call maps to the `no aaa group server radius <name>` command.
176
+ #
177
+ # @option opts [String] :name ('RAD-SV2') The name of the radius server
178
+ # group to remove.
179
+ #
180
+ # @api public
181
+ #
182
+ # @return [Boolean] true if no errors
183
+ def remove_server_group(opts = {})
184
+ result = api.config("no aaa group server radius #{opts[:name]}")
185
+ result == [{}]
186
+ end
187
+
188
+ ##
189
+ # update_server configures a radius server resource on the target device.
190
+ # This API method maps to the `radius server host` command, e.g.
191
+ # `radius-server host 10.11.12.13 auth-port 1024 acct-port 2048 timeout
192
+ # 30 retransmit 5 key 7 011204070A5955`
193
+ #
194
+ # @api public
195
+ #
196
+ # @return [Boolean] true if there are no errors
197
+ def update_server(opts = {})
198
+ retransmit = opts[:retransmit_count]
199
+ key_format = opts[:key_format] || 7
200
+ cmd = "radius-server host #{opts[:hostname]}"
201
+ cmd << " auth-port #{opts[:auth_port]}" if opts[:auth_port]
202
+ cmd << " acct-port #{opts[:acct_port]}" if opts[:acct_port]
203
+ cmd << " timeout #{opts[:timeout]}" if opts[:timeout]
204
+ cmd << " deadtime #{opts[:deadtime]}" if opts[:deadtime]
205
+ cmd << " retransmit #{retransmit}" if retransmit
206
+ cmd << " key #{key_format} #{opts[:key]}" if opts[:key]
207
+ result = api.config(cmd)
208
+ result == [{}]
209
+ end
210
+
211
+ ##
212
+ # remove_server removes the SNMP server identified by the hostname,
213
+ # auth_port, and acct_port attributes.
214
+ #
215
+ # @api public
216
+ #
217
+ # @return [Boolean] true if no errors
218
+ def remove_server(opts = {})
219
+ cmd = "no radius-server host #{opts[:hostname]}"
220
+ cmd << " auth-port #{opts[:auth_port]}" if opts[:auth_port]
221
+ cmd << " acct-port #{opts[:acct_port]}" if opts[:acct_port]
222
+ result = api.config(cmd)
223
+ result == [{}]
224
+ end
225
+
226
+ ##
227
+ # radius_global_defaults returns the default values for the radius_global
228
+ # resource. This is in a single method to keep the information in one
229
+ # place. If a value is explicitly configured to be the same as a default
230
+ # value it will not show up in the running configuration and as a result
231
+ # will not be parsed out by the parse instance methods. This method
232
+ # exposes the default values.
233
+ #
234
+ # @return [Array<Hash>] Single element Array of resource hashes
235
+ def radius_global_defaults
236
+ {
237
+ name: 'settings',
238
+ enable: true,
239
+ timeout: 5,
240
+ retransmit_count: 3
241
+ }
242
+ end
243
+ private :radius_global_defaults
244
+
245
+ ##
246
+ # parse_global_key takes a running configuration as a string and
247
+ # parses out the radius global key and global key format if it exists in
248
+ # the configuration. An empty Hash is returned if there is no global key
249
+ # configured. The intent of the Hash is to be merged into a property
250
+ # hash.
251
+ #
252
+ # @param [String] config The running configuration as a single string.
253
+ #
254
+ # @api private
255
+ #
256
+ # @return [Hash<Symbol,Object>] resource hash attributes
257
+ def parse_global_key(config)
258
+ rsrc_hsh = {}
259
+ (key_format, key) = config.scan(/radius-server key (\d+) (\w+)/).first
260
+ rsrc_hsh[:key_format] = key_format.to_i if key_format
261
+ rsrc_hsh[:key] = key if key
262
+ rsrc_hsh
263
+ end
264
+ private :parse_global_key
265
+
266
+ ##
267
+ # parse_global_timeout takes a running configuration as a string
268
+ # and parses out the radius global timeout if it exists in the
269
+ # configuration. An empty Hash is returned if there is no global timeout
270
+ # value configured. The intent of the Hash is to be merged into a
271
+ # property hash.
272
+ #
273
+ # @param [String] config The running configuration as a single string.
274
+ #
275
+ # @api private
276
+ #
277
+ # @return [Hash<Symbol,Object>] resource hash attributes
278
+ def parse_global_timeout(config)
279
+ rsrc_hsh = {}
280
+ timeout = config.scan(/radius-server timeout (\d+)/).first
281
+ # EOS default is 5 (does not show up in the running config)
282
+ rsrc_hsh[:timeout] = timeout.first.to_i if timeout
283
+ rsrc_hsh
284
+ end
285
+ private :parse_global_timeout
286
+
287
+ ##
288
+ # parse_global_retransmit takes a running configuration as a string and
289
+ # parses out the radius global retransmit count value if it exists in the
290
+ # configuration. An empty Hash is returned if there is no global timeout
291
+ # value configured. The intent of the Hash is to be merged into a
292
+ # property hash.
293
+ #
294
+ # @param [String] config The running configuration as a single string.
295
+ #
296
+ # @api private
297
+ #
298
+ # @return [Hash<Symbol,Object>] resource hash attributes
299
+ def parse_global_retransmit(config)
300
+ rsrc_hsh = {}
301
+ count = config.scan(/radius-server retransmit (\d+)/).first
302
+ # EOS default is 3 (does not show up in the running config)
303
+ rsrc_hsh[:retransmit_count] = count.first.to_i if count
304
+ rsrc_hsh
305
+ end
306
+ private :parse_global_retransmit
307
+
308
+ ##
309
+ # set_global_key configures the radius default key. This method maps to
310
+ # the `radius-server key` EOS configuration command, e.g. `radius-server
311
+ # key 7 070E234F1F5B4A`.
312
+ #
313
+ # @option opts [String] :key ('070E234F1F5B4A') The key value
314
+ #
315
+ # @option opts [Fixnum] :key_format (7) The key format, 0 for plaintext
316
+ # and 7 for a hashed value. 7 will be assumed if this option is not
317
+ # provided.
318
+ #
319
+ # @api public
320
+ #
321
+ # @return [Boolean] true if no errors
322
+ def set_global_key(opts = {})
323
+ format = opts[:key_format] || 7
324
+ key = opts[:key]
325
+ fail ArgumentError, 'key option is required' unless key
326
+ result = api.config("radius-server key #{format} #{key}")
327
+ result == [{}]
328
+ end
329
+
330
+ ##
331
+ # set_timeout configures the radius default timeout. This method maps to
332
+ # the `radius-server timeout` setting.
333
+ #
334
+ # @option opts [Fixnum] :timeout (50) The timeout in seconds to
335
+ # configure.
336
+ #
337
+ # @api public
338
+ #
339
+ # @return [Boolean] true if no errors
340
+ def set_timeout(opts = {})
341
+ timeout = opts[:timeout]
342
+ fail ArgumentError, 'timeout option is required' unless timeout
343
+ result = api.config("radius-server timeout #{timeout}")
344
+ result == [{}]
345
+ end
346
+
347
+ ##
348
+ # set_retransmit_count configures the radius default retransmit count.
349
+ # This method maps to the `radius-server retransmit` configuration
350
+ # command.
351
+ #
352
+ # @option opts [Fixnum] :retransmit_count (4) The number of times to
353
+ # retry an unresponsive server after the first timeout period.
354
+ #
355
+ # @api public
356
+ #
357
+ # @return [Boolean] true if no errors
358
+ def set_retransmit_count(opts = {})
359
+ retransmit_count = opts[:retransmit_count]
360
+ fail ArgumentError,
361
+ 'retransmit_count option is required' unless retransmit_count
362
+ result = api.config("radius-server retransmit #{retransmit_count}")
363
+ result == [{}]
364
+ end
365
+ end
366
+ end
367
+ end