puppet_x_eos_eapi 0.2.0

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