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,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ # CommonMethods should be mixed into the EosApi class
6
+ describe PuppetX::NetDev::EosApi do
7
+ let(:api) { described_class.new }
8
+ let(:prefix) { %w(enable configure) }
9
+
10
+ describe '#running_config' do
11
+ subject { api.running_config }
12
+
13
+ before :each do
14
+ allow(api).to receive(:eapi_action)
15
+ .with(['enable', 'show running-config'],
16
+ 'show running configuration',
17
+ format: 'text')
18
+ .and_return([{ 'output' => 'empty' }])
19
+ end
20
+
21
+ it 'calls eapi_action to obtain the running configuration' do
22
+ expect(api).to receive(:eapi_action)
23
+ .with(['enable', 'show running-config'],
24
+ 'show running configuration',
25
+ format: 'text')
26
+ .and_return([{ 'output' => 'empty' }])
27
+ subject
28
+ end
29
+
30
+ it 'returns the output key from the EAPI' do
31
+ expect(subject).to eq('empty')
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,842 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ # SnmpMethods should be mixed into the EosApi class
6
+ describe PuppetX::NetDev::EosApi do
7
+ let(:api) { described_class.new }
8
+ let(:prefix) { %w(enable configure) }
9
+
10
+ describe '#snmp_attributes' do
11
+ subject { api.snmp_attributes }
12
+ before :each do
13
+ allow(api).to receive(:snmp_location).and_return(location: 'foo')
14
+ allow(api).to receive(:snmp_enable).and_return(enable: :true)
15
+ allow(api).to receive(:snmp_contact).and_return(contact: 'Jane Doe')
16
+ end
17
+
18
+ it { is_expected.to have_key :ensure }
19
+ it { is_expected.to have_key :name }
20
+ it { is_expected.to have_key :enable }
21
+ it { is_expected.to have_key :contact }
22
+ it { is_expected.to have_key :location }
23
+
24
+ it 'merges attributes into a single hash' do
25
+ expect(subject).to eq(ensure: :present,
26
+ name: 'settings',
27
+ enable: :true,
28
+ contact: 'Jane Doe',
29
+ location: 'foo')
30
+ end
31
+ end
32
+
33
+ describe '#snmp_location' do
34
+ subject { api.snmp_location }
35
+
36
+ context 'when location is empty on the device' do
37
+ before :each do
38
+ allow(api).to receive(:eapi_action)
39
+ .with('show snmp location', 'get snmp location')
40
+ .and_return(fixture(:show_snmp_location_empty))
41
+ end
42
+
43
+ it { is_expected.to be_a Hash }
44
+ it { is_expected.to have_key :location }
45
+ it ':location is empty' do
46
+ expect(subject[:location]).to be_empty
47
+ end
48
+ end
49
+
50
+ context 'when location is present on the device' do
51
+ before :each do
52
+ allow(api).to receive(:eapi_action)
53
+ .with('show snmp location', 'get snmp location')
54
+ .and_return(fixture(:get_snmp_location_westeros))
55
+ end
56
+
57
+ it { is_expected.to be_a Hash }
58
+ it { is_expected.to have_key :location }
59
+ it ':location is empty' do
60
+ expect(subject[:location]).to eq 'Westeros'
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#snmp_enable' do
66
+ subject { api.snmp_enable }
67
+
68
+ context 'when SNMP is disabled' do
69
+ before :each do
70
+ allow(api).to receive(:eapi_action)
71
+ .with('show snmp', 'get snmp status', format: 'text')
72
+ .and_return(fixture(:show_snmp_disabled))
73
+ end
74
+
75
+ it { is_expected.to be_a Hash }
76
+ it { is_expected.to have_key :enable }
77
+ it ':enable => :false' do
78
+ expect(subject[:enable]).to eq(:false)
79
+ end
80
+ end
81
+
82
+ context 'when SNMP is enabled' do
83
+ before :each do
84
+ allow(api).to receive(:eapi_action)
85
+ .with('show snmp', 'get snmp status', format: 'text')
86
+ .and_return(fixture(:show_snmp_enabled))
87
+ end
88
+
89
+ it { is_expected.to be_a Hash }
90
+ it { is_expected.to have_key :enable }
91
+ it ':enable => :true' do
92
+ expect(subject[:enable]).to eq(:true)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '#parse_snmp_enable(text)' do
98
+ context 'when garbage is provided' do
99
+ subject { api.parse_snmp_enable('garbage') }
100
+
101
+ it 'throws an ArgumentError' do
102
+ expect { subject }.to raise_error ArgumentError, /could not parse/
103
+ end
104
+ end
105
+
106
+ context 'when text contains "SNMP agent disabled:"' do
107
+ subject { api.parse_snmp_enable('SNMP agent disabled:') }
108
+ it { is_expected.to eq(:false) }
109
+ end
110
+
111
+ context 'when text contains "SNMP packets input"' do
112
+ subject { api.parse_snmp_enable('SNMP packets input') }
113
+ it { is_expected.to eq(:true) }
114
+ end
115
+ end
116
+
117
+ describe '#snmp_contact' do
118
+ subject { api.snmp_contact }
119
+
120
+ context 'when the contact is empty' do
121
+ before :each do
122
+ allow(api).to receive(:eapi_action)
123
+ .with('show snmp contact', 'get snmp contact')
124
+ .and_return(fixture(:show_snmp_contact_empty))
125
+ end
126
+
127
+ it { is_expected.to be_a Hash }
128
+ it { is_expected.to have_key :contact }
129
+ it ':contact => ""' do
130
+ expect(subject[:contact]).to eq('')
131
+ end
132
+ end
133
+
134
+ context 'when the contact is "Jane Doe"' do
135
+ before :each do
136
+ allow(api).to receive(:eapi_action)
137
+ .with('show snmp contact', 'get snmp contact')
138
+ .and_return(fixture(:show_snmp_contact_name))
139
+ end
140
+
141
+ it { is_expected.to be_a Hash }
142
+ it { is_expected.to have_key :contact }
143
+ it ':contact => "Jane Doe"' do
144
+ expect(subject[:contact]).to eq('Jane Doe')
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#snmp_enable=(state)' do
150
+ context 'when state is true' do
151
+ subject { api.snmp_enable = true }
152
+ let(:expected) { 'snmp-server community public ro' }
153
+
154
+ it 'enables snmp by setting a public community string to ro' do
155
+ expect(api).to receive(:eapi_action)
156
+ .with(['enable', 'configure', expected], 'configure snmp')
157
+ .and_return([{}, {}, {}])
158
+ subject
159
+ end
160
+ end
161
+
162
+ context 'when state is false' do
163
+ subject { api.snmp_enable = false }
164
+ let(:expected) { 'no snmp-server' }
165
+
166
+ it 'disables snmp by setting no snmp-server' do
167
+ expect(api).to receive(:eapi_action)
168
+ .with(['enable', 'configure', expected], 'configure snmp')
169
+ .and_return([{}, {}, {}])
170
+ subject
171
+ end
172
+ end
173
+
174
+ context 'when state is :garbage' do
175
+ subject { api.snmp_enable = :garbage }
176
+ it { expect { subject }.to raise_error ArgumentError, /invalid state/ }
177
+ end
178
+ end
179
+
180
+ describe '#snmp_contact=(contact)' do
181
+ subject { api.snmp_contact = 'Jane Doe' }
182
+ it 'sets the contact using snmp-server contact Jane Doe' do
183
+ expect(api).to receive(:eapi_action)
184
+ .with(['enable', 'configure', 'snmp-server contact Jane Doe'],
185
+ 'set snmp contact')
186
+ .and_return([{}, {}, {}])
187
+ subject
188
+ end
189
+ end
190
+
191
+ describe '#snmp_location=(location)' do
192
+ subject { api.snmp_location = 'Planet Earth' }
193
+ it 'sets the contact using snmp-server location Planet Earth' do
194
+ expect(api).to receive(:eapi_action)
195
+ .with(['enable', 'configure', 'snmp-server location Planet Earth'],
196
+ 'set snmp location')
197
+ .and_return([{}, {}, {}])
198
+ subject
199
+ end
200
+ end
201
+
202
+ describe '#snmp_communities' do
203
+ subject { api.snmp_communities }
204
+
205
+ before :each do
206
+ allow(api).to receive(:eapi_action)
207
+ .with('show snmp community', 'get snmp communities', format: 'text')
208
+ .and_return(example_api_response)
209
+ end
210
+
211
+ describe 'structure of the return object' do
212
+ let(:example_api_response) { fixture(:show_snmp_community) }
213
+
214
+ it { is_expected.to be_an Array }
215
+ it 'is expected to have 3 results' do
216
+ expect(subject.size).to eq(3)
217
+ end
218
+ end
219
+
220
+ context 'when there is a community with an existing acl' do
221
+ let(:example_api_response) { fixture(:show_snmp_community) }
222
+
223
+ it { is_expected.to include(name: 'jeff', group: 'rw', acl: 'stest1') }
224
+ it { is_expected.to include(name: 'public', group: 'ro') }
225
+ it { is_expected.to include(name: 'private', group: 'rw') }
226
+ end
227
+
228
+ context 'when there is a community with a non-existent acl' do
229
+ let(:example_api_response) do
230
+ fixture(:get_snmp_communities_non_existent_acl)
231
+ end
232
+
233
+ it 'has 6 results' do
234
+ expect(subject.size).to eq(6)
235
+ end
236
+ it { is_expected.to include(name: 'jeff', group: 'rw', acl: 'stest1') }
237
+ it { is_expected.to include(name: 'jeff2', group: 'ro', acl: 'stest1') }
238
+ it { is_expected.to include(name: 'jeff3', group: 'ro', acl: 'stest1') }
239
+ it 'parses "Access list: stest2 (non-existent)" as stest2' do
240
+ expect(subject).to include(name: 'jeff4', group: 'ro', acl: 'stest2')
241
+ end
242
+ it { is_expected.to include(name: 'public', group: 'ro') }
243
+ it { is_expected.to include(name: 'private', group: 'rw') }
244
+ end
245
+ end
246
+
247
+ describe '#snmp_community_set' do
248
+ subject { api.snmp_community_set(resource_hash) }
249
+
250
+ context 'when the api call succeeds' do
251
+ before :each do
252
+ allow(api).to receive(:eapi_action).and_return(true)
253
+ end
254
+
255
+ let :resource_hash do
256
+ { name: 'public', group: :ro, acl: 'stest1' }
257
+ end
258
+
259
+ it { is_expected.to eq(true) }
260
+ end
261
+
262
+ context 'group and acl are both set' do
263
+ let :resource_hash do
264
+ { name: 'public', group: :ro, acl: 'stest1' }
265
+ end
266
+
267
+ it 'sets the group and the acl in that positional order' do
268
+ expected = 'snmp-server community public ro stest1'
269
+ expect(api).to receive(:eapi_action)
270
+ .with([*prefix, expected], 'define snmp community')
271
+ subject
272
+ end
273
+ end
274
+
275
+ context 'group is set, acl is not set' do
276
+ let :resource_hash do
277
+ { name: 'public', group: :ro }
278
+ end
279
+
280
+ it 'sets the group and not the acl' do
281
+ expected = 'snmp-server community public ro'
282
+ expect(api).to receive(:eapi_action)
283
+ .with([*prefix, expected], 'define snmp community')
284
+ subject
285
+ end
286
+ end
287
+
288
+ context 'group is not set, acl is set' do
289
+ let :resource_hash do
290
+ { name: 'public', acl: 'stest1' }
291
+ end
292
+
293
+ it 'sets the acl and not the group' do
294
+ expected = 'snmp-server community public stest1'
295
+ expect(api).to receive(:eapi_action)
296
+ .with([*prefix, expected], 'define snmp community')
297
+ subject
298
+ end
299
+ end
300
+ end
301
+
302
+ describe '#snmp_community_destroy' do
303
+ subject { api.snmp_community_destroy(resource_hash) }
304
+
305
+ let :resource_hash do
306
+ { name: 'public' }
307
+ end
308
+
309
+ context 'when the api call succeeds' do
310
+ before :each do
311
+ allow(api).to receive(:eapi_action).and_return(true)
312
+ end
313
+
314
+ it { is_expected.to eq(true) }
315
+ end
316
+
317
+ describe 'expected REST API call' do
318
+ it 'calls eapi_action with []' do
319
+ expected = [[*prefix, 'no snmp-server community public'],
320
+ 'destroy snmp community']
321
+ expect(api).to receive(:eapi_action)
322
+ .with(*expected)
323
+ .and_return(true)
324
+
325
+ subject
326
+ end
327
+ end
328
+ end
329
+
330
+ describe '#snmp_notifications' do
331
+ subject { api.snmp_notifications }
332
+
333
+ before :each do
334
+ allow(api).to receive(:eapi_action)
335
+ .with('show snmp trap', 'get snmp traps', format: 'text')
336
+ .and_return(fixture(:show_snmp_trap))
337
+ end
338
+
339
+ it { is_expected.to be_an Array }
340
+ it 'parses 23 resources' do
341
+ expect(subject.size).to eq(23)
342
+ end
343
+ describe 'disabled notifications' do
344
+ it 'includes msdp backward-transition' do
345
+ expect(subject).to include(name: 'msdp backward-transition',
346
+ enable: :false)
347
+ end
348
+ it { is_expected.to include(name: 'pim neighbor-loss', enable: :false) }
349
+ end
350
+ end
351
+
352
+ describe '#snmp_notification_set' do
353
+ subject { api.snmp_notification_set(resource_hash) }
354
+
355
+ before :each do
356
+ allow(api).to receive(:eapi_action).and_return([{}, {}, {}])
357
+ end
358
+
359
+ context 'when :enable => :true' do
360
+ let :resource_hash do
361
+ { name: 'snmp link-down', enable: :true }
362
+ end
363
+
364
+ it { is_expected.to eq(true) }
365
+
366
+ it 'executes "snmp-server enable traps snmp link-down"' do
367
+ expect(api).to receive(:eapi_action)
368
+ .with([*prefix, 'snmp-server enable traps snmp link-down'],
369
+ 'set snmp trap')
370
+ subject
371
+ end
372
+ end
373
+
374
+ context 'when :enable => :false' do
375
+ let :resource_hash do
376
+ { name: 'snmp link-down', enable: :false }
377
+ end
378
+
379
+ it { is_expected.to eq(true) }
380
+
381
+ it 'executes "no snmp-server enable traps snmp link-down"' do
382
+ expect(api).to receive(:eapi_action)
383
+ .with([*prefix, 'no snmp-server enable traps snmp link-down'],
384
+ 'set snmp trap')
385
+ subject
386
+ end
387
+ end
388
+
389
+ context 'when :name => "all" (manage all traps)' do
390
+ context 'when :enable => :true' do
391
+ let :resource_hash do
392
+ { name: 'all', enable: :true }
393
+ end
394
+
395
+ it { is_expected.to eq(true) }
396
+
397
+ it 'executes "snmp-server enable traps"' do
398
+ expect(api).to receive(:eapi_action)
399
+ .with([*prefix, 'snmp-server enable traps'],
400
+ 'set snmp trap')
401
+ subject
402
+ end
403
+ end
404
+
405
+ context 'when :enable => :false' do
406
+ let :resource_hash do
407
+ { name: 'all', enable: :false }
408
+ end
409
+
410
+ it { is_expected.to eq(true) }
411
+
412
+ it 'executes "no snmp-server enable traps"' do
413
+ expect(api).to receive(:eapi_action)
414
+ .with([*prefix, 'no snmp-server enable traps'],
415
+ 'set snmp trap')
416
+ subject
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ describe '#snmp_notification_receivers' do
423
+ subject { api.snmp_notification_receivers }
424
+
425
+ context 'when there are no duplicate hosts' do
426
+ before :each do
427
+ allow(api).to receive(:eapi_action)
428
+ .with('show snmp host', 'get snmp notification hosts', format: 'text')
429
+ .and_return(fixture(:show_snmp_host))
430
+ end
431
+
432
+ it { is_expected.to be_an Array }
433
+ it 'has 4 elements' do
434
+ expect(subject.size).to eq(4)
435
+ end
436
+ it 'includes 127.0.0.1' do
437
+ expect(subject).to include(name: '127.0.0.1',
438
+ ensure: :present,
439
+ port: 162,
440
+ type: :traps,
441
+ username: 'public',
442
+ version: 'v3',
443
+ security: 'noauth')
444
+ end
445
+ it 'includes 127.0.0.2' do
446
+ expect(subject).to include(name: '127.0.0.2',
447
+ ensure: :present,
448
+ port: 162,
449
+ type: :traps,
450
+ version: 'v2',
451
+ username: 'private')
452
+ end
453
+ it 'includes 127.0.0.3' do
454
+ expect(subject).to include(name: '127.0.0.3',
455
+ ensure: :present,
456
+ port: 162,
457
+ type: :traps,
458
+ version: 'v1',
459
+ username: 'public')
460
+ end
461
+ it 'includes 127.0.0.4' do
462
+ expect(subject).to include(name: '127.0.0.4',
463
+ ensure: :present,
464
+ port: 10_162,
465
+ type: :informs,
466
+ version: 'v2',
467
+ username: 'private')
468
+ end
469
+ end
470
+
471
+ context 'when there are duplicate hosts' do
472
+ before :each do
473
+ allow(api).to receive(:eapi_action)
474
+ .with('show snmp host', 'get snmp notification hosts', format: 'text')
475
+ .and_return(fixture(:show_snmp_host_duplicates))
476
+ end
477
+
478
+ it { is_expected.to be_an Array }
479
+ it 'has 5 elements' do
480
+ expect(subject.size).to eq(5)
481
+ end
482
+ it 'includes 127.0.0.4 port 20162' do
483
+ expect(subject).to include(name: '127.0.0.4',
484
+ ensure: :present,
485
+ port: 20_162,
486
+ type: :traps,
487
+ version: 'v1',
488
+ username: 'private')
489
+ end
490
+ end
491
+
492
+ context 'when there are many duplicate hosts' do
493
+ before :each do
494
+ allow(api).to receive(:eapi_action)
495
+ .with('show snmp host', 'get snmp notification hosts', format: 'text')
496
+ .and_return(fixture(:show_snmp_host_more_duplicates))
497
+ end
498
+
499
+ it { is_expected.to be_an Array }
500
+ it 'has 8 elements' do
501
+ expect(subject.size).to eq(8)
502
+ end
503
+ it 'includes 127.0.0.4 port 20162' do
504
+ expect(subject).to include(name: '127.0.0.4',
505
+ ensure: :present,
506
+ port: 20_162,
507
+ type: :traps,
508
+ version: 'v1',
509
+ username: 'private')
510
+ end
511
+ %w(priv@te public).each do |username|
512
+ it "includes 127.0.0.4 port 162 (varies by community #{username})" do
513
+ expect(subject).to include(name: '127.0.0.4',
514
+ ensure: :present,
515
+ port: 162,
516
+ type: :traps,
517
+ version: 'v1',
518
+ username: username)
519
+ end
520
+ end
521
+ end
522
+ end
523
+
524
+ describe '#snmp_notification_receiver_set' do
525
+ subject { api.snmp_notification_receiver_set(resource_hash) }
526
+
527
+ context 'when traps v3 noauth' do
528
+ let(:resource_override) { {} }
529
+ let :resource_hash do
530
+ {
531
+ ensure: :present,
532
+ name: '127.0.0.1',
533
+ port: 162,
534
+ type: :informs,
535
+ version: :v3,
536
+ username: 'snmpuser',
537
+ security: :auth
538
+ }.merge(resource_override)
539
+ end
540
+
541
+ let :expected do
542
+ 'snmp-server host 127.0.0.1 informs version 3 '\
543
+ 'auth snmpuser udp-port 162'
544
+ end
545
+
546
+ it 'configures snmp-server host ... on the target device' do
547
+ expect(api).to receive(:eapi_action)
548
+ .with(['enable', 'configure', expected], 'set snmp host')
549
+ subject
550
+ end
551
+
552
+ context 'when :name contains colons' do
553
+ let :resource_override do
554
+ { name: '127.0.0.1:snmpuser:162' }
555
+ end
556
+
557
+ it 'uses the first component for name' do
558
+ expect(api).to receive(:eapi_action)
559
+ .with(['enable', 'configure', expected], 'set snmp host')
560
+ subject
561
+ end
562
+ end
563
+
564
+ context 'when :version is :v3' do
565
+ it 'sets security after version (not username)' do
566
+ expect(api).to receive(:eapi_action)
567
+ .with(['enable', 'configure', /3 auth snmpuser/], 'set snmp host')
568
+ subject
569
+ end
570
+ end
571
+
572
+ context 'when :version is :v2c' do
573
+ let :resource_override do
574
+ { version: :v2c, username: 'public' }
575
+ end
576
+
577
+ it 'sets the version to "2c"' do
578
+ expect(api).to receive(:eapi_action)
579
+ .with(['enable', 'configure', /version 2c/], 'set snmp host')
580
+ subject
581
+ end
582
+
583
+ it 'sets community after version (not security)' do
584
+ expect(api).to receive(:eapi_action)
585
+ .with(['enable', 'configure', /2c public/], 'set snmp host')
586
+ subject
587
+ end
588
+ end
589
+
590
+ context 'when :type is nil' do
591
+ let :resource_override do
592
+ { type: nil }
593
+ end
594
+
595
+ it 'sets the type to traps' do
596
+ expect(api).to receive(:eapi_action)
597
+ .with(['enable', 'configure', /127.0.0.1 traps /], 'set snmp host')
598
+ subject
599
+ end
600
+ end
601
+ end
602
+ end
603
+
604
+ describe '#snmp_notification_receiver_cmd' do
605
+ subject { api.send(:snmp_notification_receiver_cmd, opts) }
606
+
607
+ context 'when :version is :v2' do
608
+ let(:opts) { fixture(:snmp_host_opts) }
609
+ it { is_expected.to match(/version 2c/) }
610
+ it { is_expected.not_to match(/version 2 /) }
611
+ end
612
+ end
613
+
614
+ describe '#parse_snmp_users' do
615
+ let(:text) { fixture(:show_snmp_user_raw_text) }
616
+ subject { api.parse_snmp_users(text) }
617
+
618
+ it 'returns 3 elements' do
619
+ expect(subject.size).to eq(3)
620
+ end
621
+
622
+ it 'includes nigel v2' do
623
+ expected = { name: 'nigel', version: :v2, roles: 'sysops' }
624
+ expect(subject).to include(expected)
625
+ end
626
+
627
+ it 'include nigel v3' do
628
+ expected = {
629
+ name: 'nigel',
630
+ version: :v3,
631
+ roles: 'sysops',
632
+ engine_id: 'f5717f00420008177800',
633
+ auth: :sha,
634
+ privacy: :aes128
635
+ }
636
+ expect(subject).to include(expected)
637
+ end
638
+
639
+ it 'includes jeff v3' do
640
+ expected = {
641
+ name: 'jeff',
642
+ version: :v3,
643
+ roles: 'developers',
644
+ engine_id: 'f5717f00420008177800',
645
+ auth: :sha,
646
+ privacy: :aes128
647
+ }
648
+ expect(subject).to include(expected)
649
+ end
650
+ end
651
+
652
+ describe '#snmp_users' do
653
+ subject { api.snmp_users }
654
+ before :each do
655
+ allow(api).to receive(:eapi_action)
656
+ .with('show snmp user', 'get snmp users', format: 'text')
657
+ .and_return(fixture(:show_snmp_user))
658
+ allow(api).to receive(:running_config)
659
+ .and_return(fixture(:running_config))
660
+ end
661
+
662
+ it 'returns 3 items' do
663
+ expect(subject.size).to eq(3)
664
+ end
665
+
666
+ it 'includes nigel v2' do
667
+ expected = { name: 'nigel', version: :v2, roles: 'sysops' }
668
+ expect(subject).to include(expected)
669
+ end
670
+
671
+ it 'include nigel v3' do
672
+ expected = {
673
+ name: 'nigel',
674
+ version: :v3,
675
+ roles: 'sysops',
676
+ engine_id: 'f5717f00420008177800',
677
+ auth: :sha,
678
+ privacy: :aes128
679
+ }
680
+ expect(subject).to include(expected)
681
+ end
682
+
683
+ it 'includes jeff v3' do
684
+ expected = {
685
+ name: 'jeff',
686
+ version: :v3,
687
+ roles: 'developers',
688
+ engine_id: 'f5717f00420008177800',
689
+ password: '78093f54260696d982f92ccc2d420b285151c3c8',
690
+ auth: :sha,
691
+ privacy: :aes128
692
+ }
693
+ expect(subject).to include(expected)
694
+ end
695
+ end
696
+
697
+ describe '#snmp_user_set' do
698
+ subject { api.snmp_user_set(opts) }
699
+
700
+ let :opts do
701
+ {
702
+ name: 'jeff',
703
+ roles: ['developers'],
704
+ version: :v3,
705
+ auth: :sha,
706
+ privacy: :aes,
707
+ password: 'abc123'
708
+ }.merge(opts_override)
709
+ end
710
+
711
+ let(:opts_override) { Hash.new }
712
+
713
+ before :each do
714
+ allow(api).to receive(:eapi_action)
715
+ allow(api).to receive(:running_config)
716
+ .and_return(fixture(:running_config))
717
+ end
718
+
719
+ it { is_expected.to be_a Hash }
720
+ it 'returns the hash value of the cleartext password' do
721
+ expected = '78093f54260696d982f92ccc2d420b285151c3c8'
722
+ expect(subject).to include(password: expected)
723
+ end
724
+
725
+ context 'when there is more than one role' do
726
+ let(:opts_override) do
727
+ { roles: %w(sysops developers) }
728
+ end
729
+
730
+ it 'uses only the first specified role' do
731
+ expected = ['enable', 'configure',
732
+ 'snmp-server user jeff sysops v3 auth '\
733
+ 'sha abc123 priv aes abc123']
734
+ expect(api).to receive(:eapi_action)
735
+ .with(expected, 'configure snmp user')
736
+ subject
737
+ end
738
+ end
739
+
740
+ context 'then the version is :v2' do
741
+ let(:opts_override) do
742
+ { version: :v2 }
743
+ end
744
+
745
+ it 'uses v2c when configuring the device' do
746
+ expected = ['enable', 'configure',
747
+ 'snmp-server user jeff developers v2c']
748
+ expect(api).to receive(:eapi_action)
749
+ .with(expected, 'configure snmp user')
750
+ subject
751
+ end
752
+ end
753
+
754
+ context 'then the version is not v3 (v1)' do
755
+ let(:opts_override) do
756
+ { version: :v1 }
757
+ end
758
+
759
+ it 'ignores auth and privacy options' do
760
+ expected = ['enable', 'configure',
761
+ 'snmp-server user jeff developers v1']
762
+ expect(api).to receive(:eapi_action)
763
+ .with(expected, 'configure snmp user')
764
+ subject
765
+ end
766
+ end
767
+
768
+ context 'with name, version, roles, and password but not privacy' do
769
+ let(:opts) do
770
+ { name: 'jeff', version: :v3, roles: %w(sysops), password: 'abc123' }
771
+ end
772
+
773
+ it 'throws an ArgumentError that privacy is required' do
774
+ expect { subject }.to raise_error ArgumentError, /privacy is required/
775
+ end
776
+ end
777
+
778
+ context 'with name, version and nothing else' do
779
+ let(:opts) do
780
+ { name: 'jeff', version: :v2 }
781
+ end
782
+
783
+ it 'throws an ArgumentError that role is required' do
784
+ expect { subject }.to raise_error ArgumentError, /role is required/
785
+ end
786
+ end
787
+ end
788
+
789
+ describe '#snmp_user_destroy' do
790
+ subject { api.snmp_user_destroy(opts) }
791
+
792
+ let :opts do
793
+ {
794
+ name: 'jeff',
795
+ roles: ['sysops'],
796
+ version: :v3
797
+ }.merge(opts_override)
798
+ end
799
+
800
+ let(:opts_override) { Hash.new }
801
+
802
+ [:v1, :v3].each do |version|
803
+ context "when version #{version.inspect}" do
804
+ let :opts_override do
805
+ { version: version }
806
+ end
807
+
808
+ it "uses #{version} in the `no snmp-user ...` API call" do
809
+ prefix = %w(enable configure)
810
+ expected = [*prefix, "no snmp-server user jeff sysops #{version}"]
811
+ expect(api).to receive(:eapi_action)
812
+ .with(expected, 'remove snmp user')
813
+ subject
814
+ end
815
+ end
816
+ end
817
+
818
+ context 'when version :v2' do
819
+ let :opts_override do
820
+ { version: :v2 }
821
+ end
822
+
823
+ it 'uses v2c in the `no snmp-user ...` API call' do
824
+ expected = [*prefix, 'no snmp-server user jeff sysops v2c']
825
+ expect(api).to receive(:eapi_action).with(expected, 'remove snmp user')
826
+ subject
827
+ end
828
+ end
829
+
830
+ context 'when multiple roles are specified' do
831
+ let :opts_override do
832
+ { roles: %w(one two three) }
833
+ end
834
+
835
+ it 'uses the the first role' do
836
+ expected = [*prefix, 'no snmp-server user jeff one v3']
837
+ expect(api).to receive(:eapi_action).with(expected, 'remove snmp user')
838
+ subject
839
+ end
840
+ end
841
+ end
842
+ end