puppet_x_eos_eapi 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +24 -0
- data/LICENSE.txt +202 -0
- data/README.md +87 -0
- data/Rakefile +1 -0
- data/lib/puppet_x/eos/autoload.rb +57 -0
- data/lib/puppet_x/eos/eapi.rb +259 -0
- data/lib/puppet_x/eos/module_base.rb +37 -0
- data/lib/puppet_x/eos/modules/daemon.rb +109 -0
- data/lib/puppet_x/eos/modules/extension.rb +167 -0
- data/lib/puppet_x/eos/modules/interface.rb +180 -0
- data/lib/puppet_x/eos/modules/ipinterface.rb +133 -0
- data/lib/puppet_x/eos/modules/mlag.rb +268 -0
- data/lib/puppet_x/eos/modules/ntp.rb +129 -0
- data/lib/puppet_x/eos/modules/ospf.rb +129 -0
- data/lib/puppet_x/eos/modules/portchannel.rb +277 -0
- data/lib/puppet_x/eos/modules/radius.rb +367 -0
- data/lib/puppet_x/eos/modules/snmp.rb +177 -0
- data/lib/puppet_x/eos/modules/switchport.rb +255 -0
- data/lib/puppet_x/eos/modules/system.rb +138 -0
- data/lib/puppet_x/eos/modules/tacacs.rb +302 -0
- data/lib/puppet_x/eos/modules/vlan.rb +179 -0
- data/lib/puppet_x/eos/modules/vxlan.rb +132 -0
- data/lib/puppet_x/eos/provider.rb +71 -0
- data/lib/puppet_x/eos/version.rb +41 -0
- data/lib/puppet_x/net_dev/eos_api.rb +1011 -0
- data/lib/puppet_x/net_dev/eos_api/common_methods.rb +27 -0
- data/lib/puppet_x/net_dev/eos_api/snmp_methods.rb +647 -0
- data/lib/puppet_x/net_dev/eos_api/version.rb +8 -0
- data/lib/puppet_x_eos_eapi.rb +4 -0
- data/puppet_x_eos_eapi.gemspec +31 -0
- data/spec/fixtures/fixture_all_portchannel_modes.json +8 -0
- data/spec/fixtures/fixture_all_portchannels_detailed.json +15 -0
- data/spec/fixtures/fixture_create_vlan_error.json +17 -0
- data/spec/fixtures/fixture_create_vlan_success.json +12 -0
- data/spec/fixtures/fixture_eapi_conf.yaml +4 -0
- data/spec/fixtures/fixture_enable_configure_vlan_3111_name_foo.json +14 -0
- data/spec/fixtures/fixture_enable_configure_vlan_foo_name_bar.json +19 -0
- data/spec/fixtures/fixture_get_snmp_communities_non_existent_acl.yaml +2 -0
- data/spec/fixtures/fixture_get_snmp_location_westeros.json +5 -0
- data/spec/fixtures/fixture_portchannel_min_links_1.json +8 -0
- data/spec/fixtures/fixture_portchannel_min_links_2.json +8 -0
- data/spec/fixtures/fixture_running_config.yaml +1 -0
- data/spec/fixtures/fixture_running_configuration_radius_configured.yaml +30 -0
- data/spec/fixtures/fixture_running_configuration_radius_default.yaml +29 -0
- data/spec/fixtures/fixture_running_configuration_radius_server_groups.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_radius_servers.yaml +34 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_configured.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_default.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_groups.yaml +1 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_groups_3.yaml +43 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_servers.yaml +41 -0
- data/spec/fixtures/fixture_s4_show_etherchannel_detailed.json +9 -0
- data/spec/fixtures/fixture_show_flowcontrol_et1.json +5 -0
- data/spec/fixtures/fixture_show_interfaces.json +297 -0
- data/spec/fixtures/fixture_show_interfaces_switchport_format_text.json +9 -0
- data/spec/fixtures/fixture_show_port_channel_summary_2_lags.json +9 -0
- data/spec/fixtures/fixture_show_port_channel_summary_static.json +9 -0
- data/spec/fixtures/fixture_show_snmp_community.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_contact_empty.json +5 -0
- data/spec/fixtures/fixture_show_snmp_contact_name.json +5 -0
- data/spec/fixtures/fixture_show_snmp_disabled.json +5 -0
- data/spec/fixtures/fixture_show_snmp_enabled.json +5 -0
- data/spec/fixtures/fixture_show_snmp_host.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_host_duplicates.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_host_more_duplicates.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_location_empty.json +5 -0
- data/spec/fixtures/fixture_show_snmp_trap.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_user.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_user_raw_text.yaml +1 -0
- data/spec/fixtures/fixture_show_vlan.json +37 -0
- data/spec/fixtures/fixture_show_vlan_3110.json +18 -0
- data/spec/fixtures/fixture_show_vlan_4000.json +18 -0
- data/spec/fixtures/fixture_snmp_host_opts.yaml +11 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/fixtures.rb +104 -0
- data/spec/unit/puppet_x/eos/eapi_spec.rb +182 -0
- data/spec/unit/puppet_x/eos/module_base_spec.rb +26 -0
- data/spec/unit/puppet_x/eos/modules/daemon_spec.rb +110 -0
- data/spec/unit/puppet_x/eos/modules/extension_spec.rb +197 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/daemon_getall.json +3 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/extension_getall.json +28 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/hostname.json +6 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/interface_getall.json +509 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ipinterface_getall.json +56 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get.json +21 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get_interfaces.json +18 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ntp_get.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ospf_instance_getall.json +58 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_get.json +54 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getlacpmode.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getmembers.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_po1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/snmp_get.json +14 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get_et1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_getall_interfaces.json +230 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_list.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_name.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_hostname.json +6 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_name_servers.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/vlan_getall.json +123 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/vxlan_get.json +24 -0
- data/spec/unit/puppet_x/eos/modules/interface_spec.rb +281 -0
- data/spec/unit/puppet_x/eos/modules/ipinterface_spec.rb +143 -0
- data/spec/unit/puppet_x/eos/modules/mlag_spec.rb +349 -0
- data/spec/unit/puppet_x/eos/modules/ntp_spec.rb +136 -0
- data/spec/unit/puppet_x/eos/modules/ospf_spec.rb +143 -0
- data/spec/unit/puppet_x/eos/modules/portchannel_spec.rb +357 -0
- data/spec/unit/puppet_x/eos/modules/radius_spec.rb +509 -0
- data/spec/unit/puppet_x/eos/modules/snmp_spec.rb +202 -0
- data/spec/unit/puppet_x/eos/modules/switchport_get_et1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/switchport_spec.rb +307 -0
- data/spec/unit/puppet_x/eos/modules/system_spec.rb +170 -0
- data/spec/unit/puppet_x/eos/modules/tacacs_spec.rb +448 -0
- data/spec/unit/puppet_x/eos/modules/vlan_spec.rb +244 -0
- data/spec/unit/puppet_x/eos/modules/vxlan_spec.rb +189 -0
- data/spec/unit/puppet_x/eos/provider_spec.rb +35 -0
- data/spec/unit/puppet_x/net_dev/eos_api/common_methods_spec.rb +34 -0
- data/spec/unit/puppet_x/net_dev/eos_api/snmp_methods_spec.rb +842 -0
- data/spec/unit/puppet_x/net_dev/eos_api_spec.rb +1000 -0
- metadata +369 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module PuppetX
|
4
|
+
module NetDev
|
5
|
+
##
|
6
|
+
class EosApi
|
7
|
+
##
|
8
|
+
# CommonMethods implements common methods, such as returning the running
|
9
|
+
# config. This separation makes it easier to provide documentation and
|
10
|
+
# introspect where methods come from given an api instance.
|
11
|
+
module CommonMethods
|
12
|
+
##
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# @return [String] the text of the running configuration
|
17
|
+
def running_config
|
18
|
+
prefix = %w(enable)
|
19
|
+
cmd = 'show running-config'
|
20
|
+
msg = 'show running configuration'
|
21
|
+
result = eapi_action([*prefix, cmd], msg, format: 'text')
|
22
|
+
result.last['output']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,647 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module PuppetX
|
4
|
+
module NetDev
|
5
|
+
##
|
6
|
+
class EosApi
|
7
|
+
##
|
8
|
+
# SnmpMethods encapsulate the SNMP specific EOS API methods. This
|
9
|
+
# separation makes it easier to provide documentation and introspect
|
10
|
+
# where methods come from given an api instance.
|
11
|
+
module SnmpMethods
|
12
|
+
##
|
13
|
+
# snmp_attributes retrieves the current state of the SNMP service on
|
14
|
+
# the device and returns data suitable for a provider instance.
|
15
|
+
#
|
16
|
+
# @return [Hash<Symbol,String>]
|
17
|
+
def snmp_attributes
|
18
|
+
rval = { name: 'settings', ensure: :present }
|
19
|
+
rval.merge!(snmp_location)
|
20
|
+
rval.merge!(snmp_enable)
|
21
|
+
rval.merge!(snmp_contact)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# snmp_location obtains the configured SNMP location string from
|
26
|
+
# the device.
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
#
|
30
|
+
# @return [Hash<Symbol,String>]
|
31
|
+
def snmp_location
|
32
|
+
cmd = 'show snmp location'
|
33
|
+
result = eapi_action(cmd, 'get snmp location')
|
34
|
+
location = result.first['location']
|
35
|
+
{ location: location }
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# snmp_enable returns :true if SNMP is enabled on the device or :false
|
40
|
+
# otherwise as a Hash suitable for merge into `snmp_attributes`.
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
# @return [Hash<Symbol,Symbol>] e.g. `{ enable: :true }`
|
45
|
+
def snmp_enable
|
46
|
+
cmd = 'show snmp'
|
47
|
+
result = eapi_action(cmd, 'get snmp status', format: 'text')
|
48
|
+
text = result.first['output']
|
49
|
+
enable = parse_snmp_enable(text)
|
50
|
+
{ enable: enable }
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# parse_snmp_enable parses the text output of the `show snmp` command
|
55
|
+
# an returns :true or :false for the enabled state.
|
56
|
+
#
|
57
|
+
# @param [String] text The text of the snmp output, e.g. for a disabled
|
58
|
+
# SNMP service:
|
59
|
+
#
|
60
|
+
# SNMP agent enabled in VRFs: default
|
61
|
+
# SNMP agent disabled: no communities or users configured
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
#
|
65
|
+
# @return [Symbol] :true or :false
|
66
|
+
def parse_snmp_enable(text)
|
67
|
+
disabled_regexp = /SNMP agent disabled:/m
|
68
|
+
enabled_regexp = /SNMP packets input/m
|
69
|
+
|
70
|
+
disabled_mdata = disabled_regexp.match(text)
|
71
|
+
return :false if disabled_mdata
|
72
|
+
|
73
|
+
enabled_mdata = enabled_regexp.match(text)
|
74
|
+
return :true if enabled_mdata
|
75
|
+
|
76
|
+
fail ArgumentError, 'could not parse text for SNMP enabled state'
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# snmp_contact returns the snmp contact string configured on the device.
|
81
|
+
#
|
82
|
+
# @api private
|
83
|
+
#
|
84
|
+
# @return [Hash<Symbol,Symbol>] e.g. `{ contact: 'Jane Doe' }`
|
85
|
+
def snmp_contact
|
86
|
+
cmd = 'show snmp contact'
|
87
|
+
result = eapi_action(cmd, 'get snmp contact')
|
88
|
+
contact = result.first['contact']
|
89
|
+
{ contact: contact }
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# snmp_enable= disables or enables SNMP
|
94
|
+
#
|
95
|
+
# @param [Boolean] state enable SNMP if true, disable if false.
|
96
|
+
#
|
97
|
+
# @api public
|
98
|
+
def snmp_enable=(state)
|
99
|
+
cmd = %w(enable configure)
|
100
|
+
case state
|
101
|
+
when true
|
102
|
+
cmd << 'snmp-server community public ro'
|
103
|
+
when false
|
104
|
+
cmd << 'no snmp-server'
|
105
|
+
else
|
106
|
+
fail ArgumentError, "invalid state #{state.inspect}"
|
107
|
+
end
|
108
|
+
|
109
|
+
eapi_action(cmd, 'configure snmp') && true || false
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# snmp_contact= updates the SNMP contact on the target device.
|
114
|
+
#
|
115
|
+
# @param [String] contact The contact name, e.g. 'Jane Doe'
|
116
|
+
#
|
117
|
+
# @api public
|
118
|
+
#
|
119
|
+
# @return [Boolean] true or false
|
120
|
+
def snmp_contact=(contact)
|
121
|
+
cmd = %w(enable configure)
|
122
|
+
cmd << "snmp-server contact #{contact}"
|
123
|
+
eapi_action(cmd, 'set snmp contact') && true || false
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# snmp_location= updates the SNMP location on the target device.
|
128
|
+
#
|
129
|
+
# @param [String] location The location, e.g. 'Planet Earth'
|
130
|
+
#
|
131
|
+
# @api public
|
132
|
+
#
|
133
|
+
# @return [Boolean] true or false
|
134
|
+
def snmp_location=(location)
|
135
|
+
cmd = %w(enable configure)
|
136
|
+
cmd << "snmp-server location #{location}"
|
137
|
+
eapi_action(cmd, 'set snmp location') && true || false
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# snmp_communities retrieves all of the SNMP community strings defined
|
142
|
+
# on the target device and returns an Array of Hash objects suitable
|
143
|
+
# for use as a resource hash to the provider's initializer method.
|
144
|
+
#
|
145
|
+
# @param [String] buf Describe the string parameter here
|
146
|
+
#
|
147
|
+
# @api public
|
148
|
+
#
|
149
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
150
|
+
def snmp_communities
|
151
|
+
cmd = 'show snmp community'
|
152
|
+
result = eapi_action(cmd, 'get snmp communities', format: 'text')
|
153
|
+
text = result.first['output']
|
154
|
+
parse_snmp_communities(text)
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# parse_snmp_communities takes the text output from the `show snmp
|
159
|
+
# community` EAPI command and parses the text into structured data
|
160
|
+
# suitable for use as a resource hash to the provider initializer
|
161
|
+
# method. An example of the output looks like:
|
162
|
+
#
|
163
|
+
# ```
|
164
|
+
# Community name: jeff
|
165
|
+
# Community access: read-write
|
166
|
+
# Access list: stest1
|
167
|
+
#
|
168
|
+
# Community name: jeff2
|
169
|
+
# Community access: read-write
|
170
|
+
# Access list: stest2 (non-existent)
|
171
|
+
#
|
172
|
+
# Community name: private
|
173
|
+
# Community access: read-write
|
174
|
+
#
|
175
|
+
# Community name: public
|
176
|
+
# Community access: read-only
|
177
|
+
# ```
|
178
|
+
#
|
179
|
+
# @param [String] text The text to parse
|
180
|
+
#
|
181
|
+
# @api private
|
182
|
+
#
|
183
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
184
|
+
def parse_snmp_communities(text)
|
185
|
+
blocks = text.split("\n\n")
|
186
|
+
# (?:\s*\(.*?\)|\n|$) deals with trailing data after the value. e.g.
|
187
|
+
# an ACL might come back as `Access list: stest2 (non-existent)`
|
188
|
+
regexp = / (\w+): (\w.*?)(?:\s*\(.*?\)|\n|$)/
|
189
|
+
communities = blocks.map { |l| l.scan(regexp) }
|
190
|
+
communities.map do |pairs|
|
191
|
+
pairs.each_with_object({}) do |(key, val), resource_hash|
|
192
|
+
resource_hash.merge!(map_snmp_keys(key, val))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# map_snmp_keys maps the keys and values parsed from the show snmp
|
199
|
+
# community raw text output into resource attributes and values.
|
200
|
+
#
|
201
|
+
# @api private
|
202
|
+
def map_snmp_keys(key, val)
|
203
|
+
case key
|
204
|
+
when 'name' then { name: val }
|
205
|
+
when 'list' then { acl: val }
|
206
|
+
when 'access'
|
207
|
+
group = case val
|
208
|
+
when 'read-write'; then 'rw'
|
209
|
+
when 'read-only'; then 'ro'
|
210
|
+
end
|
211
|
+
{ group: group }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
private :map_snmp_keys
|
215
|
+
|
216
|
+
##
|
217
|
+
# snmp_community_set creates or updates an snmp community on the target
|
218
|
+
# device given a hash of attributes from the resource model.
|
219
|
+
#
|
220
|
+
# @option opts [String] :name ('public') The community name
|
221
|
+
#
|
222
|
+
# @option opts [Symbol] :group (:ro) :ro or :rw for read-only or
|
223
|
+
# read-write access control for the community name.
|
224
|
+
#
|
225
|
+
# @option opts [String] :acl ('stest1') The standard ACL name defined on
|
226
|
+
# the switch. This ACL is defined using the `ip access-list standard
|
227
|
+
# stest1` command.
|
228
|
+
#
|
229
|
+
# @api public
|
230
|
+
#
|
231
|
+
# @return [Boolean] true if the resource was successfully created
|
232
|
+
def snmp_community_set(opts)
|
233
|
+
prefix = %w(enable configure)
|
234
|
+
cmd = "snmp-server community #{opts[:name]}"
|
235
|
+
cmd << " #{opts[:group]}" if opts[:group]
|
236
|
+
cmd << " #{opts[:acl]}" if opts[:acl]
|
237
|
+
eapi_action([*prefix, cmd], 'define snmp community') && true || false
|
238
|
+
end
|
239
|
+
|
240
|
+
##
|
241
|
+
# snmp_community_destroy deletes an SNMP community from the target
|
242
|
+
# device. given a hash of attributes from the resource model.
|
243
|
+
#
|
244
|
+
# @option opts [String] :name ('public') The community name
|
245
|
+
#
|
246
|
+
# @api public
|
247
|
+
#
|
248
|
+
# @return [Boolean] true if the resource was successfully created
|
249
|
+
def snmp_community_destroy(opts)
|
250
|
+
prefix = %w(enable configure)
|
251
|
+
cmd = "no snmp-server community #{opts[:name]}"
|
252
|
+
result = eapi_action([*prefix, cmd], 'destroy snmp community')
|
253
|
+
result && true || false
|
254
|
+
end
|
255
|
+
|
256
|
+
##
|
257
|
+
# snmp_notifications returns an Array of resource hashes suitable for
|
258
|
+
# initializing new provider resources.
|
259
|
+
#
|
260
|
+
# @api public
|
261
|
+
#
|
262
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
263
|
+
def snmp_notifications
|
264
|
+
cmd = 'show snmp trap'
|
265
|
+
result = eapi_action(cmd, 'get snmp traps', format: 'text')
|
266
|
+
text = result.first['output']
|
267
|
+
parse_snmp_traps(text)
|
268
|
+
end
|
269
|
+
|
270
|
+
##
|
271
|
+
# parse_snmp_traps takes the raw text output of the `show snmp trap`
|
272
|
+
# command and parses the data into hases suitable for new provider
|
273
|
+
# instances.
|
274
|
+
#
|
275
|
+
# @param [String] text The raw text to process.
|
276
|
+
#
|
277
|
+
# @api private
|
278
|
+
#
|
279
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
280
|
+
def parse_snmp_traps(text)
|
281
|
+
regexp = /(\w+)\s+([-_\w]+)\s+(\w+).*$/
|
282
|
+
triples = text.scan(regexp)
|
283
|
+
triples.shift # Header
|
284
|
+
triples.map do |triple|
|
285
|
+
{
|
286
|
+
name: format('%s %s', *triple),
|
287
|
+
enable: /yes/xi.match(triple[2]) ? :true : :false
|
288
|
+
}
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# snmp_notification_set configures a SNMP trap notification on the
|
294
|
+
# target device.
|
295
|
+
#
|
296
|
+
# @option opts [String] :name ('snmp link-down') The trap name with the
|
297
|
+
# type name as a prefix separated by a space. The special name 'all'
|
298
|
+
# will enable or disable all notifications.
|
299
|
+
#
|
300
|
+
# @option opts [Symbol] :enable (:true) :true to enable the
|
301
|
+
# notification, :false to disable the notification.
|
302
|
+
#
|
303
|
+
# @api public
|
304
|
+
#
|
305
|
+
# @return [Boolean] true if successful
|
306
|
+
def snmp_notification_set(opts)
|
307
|
+
prefix = %w(enable configure)
|
308
|
+
pre = opts[:enable] == :true ? '' : 'no '
|
309
|
+
suffix = opts[:name] == 'all' ? '' : " #{opts[:name]}"
|
310
|
+
cmd = pre << 'snmp-server enable traps' << suffix
|
311
|
+
result = eapi_action([*prefix, cmd], 'set snmp trap')
|
312
|
+
result && true || false
|
313
|
+
end
|
314
|
+
|
315
|
+
##
|
316
|
+
# snmp_notification_receivers obtains a list of all the snmp
|
317
|
+
# notification receivers and returns them as an Array of resource
|
318
|
+
# hashes suitable for the provider's new class method. This command
|
319
|
+
# maps the `show snmp host` command to an array of resource hashes.
|
320
|
+
#
|
321
|
+
# @api public
|
322
|
+
#
|
323
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
324
|
+
def snmp_notification_receivers
|
325
|
+
cmd = 'show snmp host'
|
326
|
+
msg = 'get snmp notification hosts'
|
327
|
+
result = eapi_action(cmd, msg, format: 'text')
|
328
|
+
text = result.first['output']
|
329
|
+
parse_snmp_hosts(text)
|
330
|
+
end
|
331
|
+
|
332
|
+
##
|
333
|
+
# parse_snmp_hosts parses the raw text from the `show snmp host`
|
334
|
+
# command and returns an Array of resource hashes.
|
335
|
+
#
|
336
|
+
# rubocop:disable Metrics/MethodLength
|
337
|
+
#
|
338
|
+
# @param [String] text The text of the `show snmp host` output, e.g.
|
339
|
+
# for three hosts:
|
340
|
+
#
|
341
|
+
# ```
|
342
|
+
# Notification host: 127.0.0.1 udp-port: 162 type: trap
|
343
|
+
# user: public security model: v3 noauth
|
344
|
+
#
|
345
|
+
# Notification host: 127.0.0.1 udp-port: 162 type: trap
|
346
|
+
# user: smtpuser security model: v3 auth
|
347
|
+
#
|
348
|
+
# Notification host: 127.0.0.2 udp-port: 162 type: trap
|
349
|
+
# user: private security model: v2c
|
350
|
+
#
|
351
|
+
# Notification host: 127.0.0.3 udp-port: 162 type: trap
|
352
|
+
# user: public security model: v1
|
353
|
+
#
|
354
|
+
# Notification host: 127.0.0.4 udp-port: 10162 type: inform
|
355
|
+
# user: private security model: v2c
|
356
|
+
#
|
357
|
+
# Notification host: 127.0.0.4 udp-port: 162 type: trap
|
358
|
+
# user: priv@te security model: v1
|
359
|
+
#
|
360
|
+
# Notification host: 127.0.0.4 udp-port: 162 type: trap
|
361
|
+
# user: public security model: v1
|
362
|
+
#
|
363
|
+
# Notification host: 127.0.0.4 udp-port: 20162 type: trap
|
364
|
+
# user: private security model: v1
|
365
|
+
#
|
366
|
+
# ```
|
367
|
+
#
|
368
|
+
# @api private
|
369
|
+
#
|
370
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
371
|
+
def parse_snmp_hosts(text)
|
372
|
+
re = /host: ([^\s]+)\s+.*?port: (\d+)\s+type: (\w+)\s*user: (.*?)\s+security model: (.*?)\n/m # rubocop:disable Metrics/LineLength
|
373
|
+
text.scan(re).map do |(host, port, type, username, auth)|
|
374
|
+
resource_hash = { name: host, ensure: :present, port: port.to_i }
|
375
|
+
sec_match = /^v3 (\w+)/.match(auth)
|
376
|
+
resource_hash[:security] = sec_match[1] if sec_match
|
377
|
+
ver_match = /^(v\d)/.match(auth) # first 2 characters
|
378
|
+
resource_hash[:version] = ver_match[1] if ver_match
|
379
|
+
resource_hash[:type] = /trap/.match(type) ? :traps : :informs
|
380
|
+
resource_hash[:username] = username
|
381
|
+
resource_hash
|
382
|
+
end
|
383
|
+
end
|
384
|
+
# rubocop:enable Metrics/MethodLength
|
385
|
+
|
386
|
+
##
|
387
|
+
# snmp_notification_receiver_set takes a resource hash and configures a
|
388
|
+
# SNMP notification host on the target device. In practice this method
|
389
|
+
# usually creates a resource because nearly all of the properties can
|
390
|
+
# vary and are components of a resource identifier.
|
391
|
+
#
|
392
|
+
# @option opts [String] :name ('127.0.0.1') The hostname or ip address
|
393
|
+
# of the snmp notification receiver host.
|
394
|
+
#
|
395
|
+
# @option opts [String] :username ('public') The SNMP username, or
|
396
|
+
# community, to use for authentication.
|
397
|
+
#
|
398
|
+
# @option opts [Fixnum] :port (162) The UDP port of the receiver.
|
399
|
+
#
|
400
|
+
# @option opts [Symbol] :version (:v3) The version, :v1, :v2, or :v3
|
401
|
+
#
|
402
|
+
# @option opts [Symbol] :type (:traps) The notification type, :traps or
|
403
|
+
# :informs.
|
404
|
+
#
|
405
|
+
# @option opts [Symbol] :security (:auth) The security mode, :auth,
|
406
|
+
# :noauth, or :priv
|
407
|
+
#
|
408
|
+
# @api public
|
409
|
+
#
|
410
|
+
# @return [Boolean]
|
411
|
+
def snmp_notification_receiver_set(opts = {})
|
412
|
+
prefix = %w(enable configure)
|
413
|
+
cmd = snmp_notification_receiver_cmd(opts)
|
414
|
+
result = eapi_action([*prefix, cmd], 'set snmp host')
|
415
|
+
result ? true : false
|
416
|
+
end
|
417
|
+
|
418
|
+
##
|
419
|
+
# snmp_notification_receiver_cmd builds a command given a resource
|
420
|
+
# hash.
|
421
|
+
#
|
422
|
+
# @return [String]
|
423
|
+
def snmp_notification_receiver_cmd(opts = {})
|
424
|
+
host = opts[:name].split(':').first
|
425
|
+
version = /\d+/.match(opts[:version]).to_s
|
426
|
+
version.sub!('2', '2c')
|
427
|
+
cmd = "snmp-server host #{host}"
|
428
|
+
cmd << " #{opts[:type] || :traps}"
|
429
|
+
cmd << " version #{version}"
|
430
|
+
cmd << " #{opts[:security] || :noauth}" if version == '3'
|
431
|
+
cmd << " #{opts[:username]}"
|
432
|
+
cmd << " udp-port #{opts[:port]}"
|
433
|
+
cmd
|
434
|
+
end
|
435
|
+
private :snmp_notification_receiver_cmd
|
436
|
+
|
437
|
+
##
|
438
|
+
# snmp_notification_receiver_remove removes an snmp-server host from
|
439
|
+
# the target device.
|
440
|
+
#
|
441
|
+
# @option opts [String] :name ('127.0.0.1') The hostname or ip address
|
442
|
+
# of the snmp notification receiver host.
|
443
|
+
#
|
444
|
+
# @option opts [String] :username ('public') The SNMP username, or
|
445
|
+
# community, to use for authentication.
|
446
|
+
#
|
447
|
+
# @option opts [Fixnum] :port (162) The UDP port of the receiver.
|
448
|
+
#
|
449
|
+
# @option opts [Symbol] :version (:v3) The version, :v1, :v2, or :v3
|
450
|
+
#
|
451
|
+
# @option opts [Symbol] :type (:traps) The notification type, :traps or
|
452
|
+
# :informs.
|
453
|
+
#
|
454
|
+
# @option opts [Symbol] :security (:auth) The security mode, :auth,
|
455
|
+
# :noauth, or :priv
|
456
|
+
#
|
457
|
+
# @api public
|
458
|
+
#
|
459
|
+
# @return [Boolean]
|
460
|
+
def snmp_notification_receiver_remove(opts = {})
|
461
|
+
prefix = %w(enable configure)
|
462
|
+
cmd = 'no ' << snmp_notification_receiver_cmd(opts)
|
463
|
+
result = eapi_action([*prefix, cmd], 'remove snmp host')
|
464
|
+
result ? true : false
|
465
|
+
end
|
466
|
+
|
467
|
+
##
|
468
|
+
# snmp_users retrieves all of the SNMP users defined on the target
|
469
|
+
# device and returns an Array of Hash objects suitable for use as a
|
470
|
+
# resource hash to the provider's initializer method.
|
471
|
+
#
|
472
|
+
# @api public
|
473
|
+
#
|
474
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
475
|
+
def snmp_users
|
476
|
+
cmd = 'show snmp user'
|
477
|
+
result = eapi_action(cmd, 'get snmp users', format: 'text')
|
478
|
+
text = result.first['output']
|
479
|
+
users = parse_snmp_users(text)
|
480
|
+
text = running_config
|
481
|
+
users.each do |h|
|
482
|
+
cmd = "snmp-server user #{h[:name]} #{h[:roles]} #{h[:version]}"
|
483
|
+
password = snmp_user_password_hash(text, cmd)[:auth]
|
484
|
+
h[:password] = password if password
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
##
|
489
|
+
# parse_snmp_users takes the text output from the `show snmp user` EAPI
|
490
|
+
# command and parses the text into structured data suitable for use as
|
491
|
+
# a resource has to the provider initializer method.
|
492
|
+
#
|
493
|
+
# ```
|
494
|
+
#
|
495
|
+
# User name : jeff
|
496
|
+
# Security model : v3
|
497
|
+
# Engine ID : f5717f00420008177800
|
498
|
+
# Authentication : SHA
|
499
|
+
# Privacy : AES-128
|
500
|
+
# Group : developers
|
501
|
+
#
|
502
|
+
# User name : nigel
|
503
|
+
# Security model : v2c
|
504
|
+
# Group : sysops (not configured)
|
505
|
+
#
|
506
|
+
# User name : nigel
|
507
|
+
# Security model : v3
|
508
|
+
# Engine ID : f5717f00420008177800
|
509
|
+
# Authentication : SHA
|
510
|
+
# Privacy : AES-128
|
511
|
+
# Group : sysops
|
512
|
+
# ```
|
513
|
+
#
|
514
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
515
|
+
# rubocop:disable Metrics/MethodLength
|
516
|
+
#
|
517
|
+
# @param [String] text The text to parse
|
518
|
+
#
|
519
|
+
# @api private
|
520
|
+
#
|
521
|
+
# @return [Array<Hash<Symbol,Object>>] Array of resource hashes.
|
522
|
+
def parse_snmp_users(text)
|
523
|
+
text.split("\n\n").map do |user_s|
|
524
|
+
user_s.scan(/^(\w+).*?: (.*)/).each_with_object({}) do |(h, v), m|
|
525
|
+
key = SNMP_USER_PARAM[h.downcase.intern] || h.downcase.intern
|
526
|
+
m[key] = case key
|
527
|
+
when :privacy then /AES/.match(v) ? :aes128 : :des
|
528
|
+
when :version then v.sub('v2c', 'v2').intern
|
529
|
+
when :auth then v.downcase.intern
|
530
|
+
when :roles then v.sub(/ \(.*?\)/, '')
|
531
|
+
else v.downcase
|
532
|
+
end
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
# rubocop:enable Metrics/MethodLength
|
537
|
+
|
538
|
+
# Map SNMP headings from `show snmp user` to snmp_user parameter names
|
539
|
+
SNMP_USER_PARAM = {
|
540
|
+
user: :name,
|
541
|
+
engine: :engine_id,
|
542
|
+
security: :version,
|
543
|
+
authentication: :auth,
|
544
|
+
privacy: :privacy,
|
545
|
+
group: :roles
|
546
|
+
}
|
547
|
+
|
548
|
+
##
|
549
|
+
# snmp_user_set creates or updates an SNMP user account on the target
|
550
|
+
# device.
|
551
|
+
#
|
552
|
+
# rubocop:disable Metrics/MethodLength
|
553
|
+
#
|
554
|
+
# @option opts [String] :name ('johndoe') The username
|
555
|
+
#
|
556
|
+
# @option opts [Array] :roles (['developers']) The group, as an Array,
|
557
|
+
# this user is associated with.
|
558
|
+
#
|
559
|
+
# @option opts [Symbol] :version (:v2) The snmp version for this user
|
560
|
+
# account.
|
561
|
+
#
|
562
|
+
# @option opts [Symbol] :auth (:sha) The authentication digest method
|
563
|
+
#
|
564
|
+
# @option opts [Symbol] :privacy (:aes) The encryption scheme for
|
565
|
+
# privacy.
|
566
|
+
#
|
567
|
+
# @option opts [String] :password ('abc123') The password to
|
568
|
+
# configure for authentication and privacy.
|
569
|
+
#
|
570
|
+
# @api public
|
571
|
+
#
|
572
|
+
# @return [Hash<Symbol,Object>] Updated properties, e.g. the password
|
573
|
+
# hash which is idempotent.
|
574
|
+
def snmp_user_set(opts = {})
|
575
|
+
prefix = %w(enable configure)
|
576
|
+
group = [*opts[:roles]].first
|
577
|
+
fail ArgumentError, 'at least one role is required' unless group
|
578
|
+
version = opts[:version].to_s.sub('v2', 'v2c')
|
579
|
+
cmd = user_cmd = "snmp-server user #{opts[:name]} #{group} #{version}"
|
580
|
+
if opts[:password] && version == 'v3'
|
581
|
+
privacy = opts[:privacy].to_s.scan(/aes|des/).first
|
582
|
+
fail ArgumentError,
|
583
|
+
'privacy is required when managing passwords' unless privacy
|
584
|
+
cmd += " auth #{opts[:auth] || 'sha'} #{opts[:password]} "\
|
585
|
+
"priv #{privacy} #{opts[:password]}"
|
586
|
+
end
|
587
|
+
eapi_action([*prefix, cmd], 'configure snmp user')
|
588
|
+
hash = snmp_user_password_hash(running_config, user_cmd)
|
589
|
+
{ password: hash[:auth] }
|
590
|
+
end
|
591
|
+
# rubocop:enable Metrics/MethodLength
|
592
|
+
|
593
|
+
##
|
594
|
+
# snmp_user_destroy removes an SNMP user from the target device
|
595
|
+
#
|
596
|
+
# @option opts [String] :name ('johndoe') The username
|
597
|
+
#
|
598
|
+
# @option opts [Array] :roles (['developers']) The group, as an Array,
|
599
|
+
# this user is associated with.
|
600
|
+
#
|
601
|
+
# @option opts [Symbol] :version (:v2) The snmp version for this user
|
602
|
+
# account.
|
603
|
+
#
|
604
|
+
# @option opts [Symbol] :auth (:sha) The authentication digest method
|
605
|
+
#
|
606
|
+
# @option opts [Symbol] :privacy (:aes) The encryption scheme for
|
607
|
+
# privacy.
|
608
|
+
#
|
609
|
+
# @option opts [String] :password ('abc123') The password to
|
610
|
+
# configure for authentication and privacy.
|
611
|
+
#
|
612
|
+
# @api public
|
613
|
+
#
|
614
|
+
# @return [Hash<Symbol,Object>] Updated properties, e.g. the password
|
615
|
+
# hash which is idempotent.
|
616
|
+
#
|
617
|
+
# @return [String]
|
618
|
+
def snmp_user_destroy(opts = {})
|
619
|
+
prefix = %w(enable configure)
|
620
|
+
group = [*opts[:roles]].first
|
621
|
+
version = opts[:version].to_s.sub('v2', 'v2c')
|
622
|
+
cmd = "no snmp-server user #{opts[:name]} #{group} #{version}"
|
623
|
+
eapi_action([*prefix, cmd], 'remove snmp user')
|
624
|
+
{}
|
625
|
+
end
|
626
|
+
|
627
|
+
##
|
628
|
+
# snmp_user_password obtains the password hash from the device in order
|
629
|
+
# to provide an idempotent configuration value.
|
630
|
+
#
|
631
|
+
# @param [String] running_config The text of the current running
|
632
|
+
# configuration.
|
633
|
+
#
|
634
|
+
# @param [String] user_cmd The prefix of the command that identifies
|
635
|
+
# the user in the running-config. e.g. ('snmp-server user jeff
|
636
|
+
# developers v3')
|
637
|
+
#
|
638
|
+
# @return [Hash<Symbol,String>] The hashes for :auth and :privacy
|
639
|
+
def snmp_user_password_hash(running_config, user_cmd)
|
640
|
+
regexp = /#{user_cmd} .*?auth \w+\s+(.*?)\s+priv \w+\s+(.*?)\s/
|
641
|
+
(auth_hash, priv_hash) = running_config.scan(regexp).first
|
642
|
+
{ auth: auth_hash, privacy: priv_hash }
|
643
|
+
end
|
644
|
+
end
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|