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.
- 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,1000 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe PuppetX::NetDev::EosApi do
|
|
6
|
+
let(:api) { described_class.new }
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# api_response returns a JSON fixture that presents a representative REST API
|
|
10
|
+
# response. The method is memoized to reduce filesystem operations.
|
|
11
|
+
#
|
|
12
|
+
# @param [Symbol] key The fixture to load, e.g. :foo will load
|
|
13
|
+
# 'fixture_foo.json'
|
|
14
|
+
def api_response(key)
|
|
15
|
+
memo = Fixtures[key]
|
|
16
|
+
return memo if memo
|
|
17
|
+
file = File.join(File.dirname(__FILE__), "fixture_#{key}.json")
|
|
18
|
+
Fixtures[key] = JSON.load(File.read(file))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context 'initializing the API instance' do
|
|
22
|
+
[:address, :port, :username, :password].each do |option|
|
|
23
|
+
it "initializes with #{option}" do
|
|
24
|
+
api = described_class.new(option => 'foo')
|
|
25
|
+
expect(api.send(option)).to eq('foo')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'address defaults to unix:///var/run/command-api.sock' do
|
|
30
|
+
expect(subject.address).to eq('unix:///var/run/command-api.sock')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe '#vlan(id)' do
|
|
35
|
+
context 'when the vlan exists' do
|
|
36
|
+
subject { api.vlan(3110) }
|
|
37
|
+
|
|
38
|
+
before do
|
|
39
|
+
allow(api).to receive(:eapi_call)
|
|
40
|
+
.with('show vlan 3110', {})
|
|
41
|
+
.and_return(fixture(:show_vlan_3110))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'has only one key' do
|
|
45
|
+
expect(subject.size).to eq 1
|
|
46
|
+
end
|
|
47
|
+
it { is_expected.to be_a_kind_of Hash }
|
|
48
|
+
it { is_expected.to have_key '3110' }
|
|
49
|
+
it { is_expected.not_to have_key 'results' }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'when the vlan does not exist' do
|
|
53
|
+
subject { api.vlan(4000) }
|
|
54
|
+
|
|
55
|
+
before do
|
|
56
|
+
allow(api).to receive(:eapi_call)
|
|
57
|
+
.with('show vlan 4000', {})
|
|
58
|
+
.and_return(fixture(:show_vlan_4000))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'raises Puppet:Error with message "could not list vlans"' do
|
|
62
|
+
expect { subject }.to raise_error PuppetX::NetDev::ApiError,
|
|
63
|
+
/could not list vlans/
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#vlan_create(id)' do
|
|
69
|
+
subject { api.vlan_create(3100) }
|
|
70
|
+
|
|
71
|
+
context 'when eAPI reports no errors' do
|
|
72
|
+
it 'accepts 3100 without error' do
|
|
73
|
+
allow(api).to receive(:eapi_call)
|
|
74
|
+
.with(['enable', 'configure', 'vlan 3100'], {})
|
|
75
|
+
.and_return(fixture(:create_vlan_success))
|
|
76
|
+
subject
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'when eAPI reports errors' do
|
|
81
|
+
before do
|
|
82
|
+
allow(api).to receive(:eapi_call)
|
|
83
|
+
.with(['enable', 'configure', 'vlan 3100'], {})
|
|
84
|
+
.and_return(fixture(:create_vlan_error))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'raises PuppetX::NetDev::ApiError on eAPI errors' do
|
|
88
|
+
expect { subject }.to raise_error PuppetX::NetDev::ApiError,
|
|
89
|
+
/could not create vlan 3100/
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe '#http' do
|
|
95
|
+
it 'Returns a NetX::HTTPUnix instance' do
|
|
96
|
+
expect(subject.send(:http)).to be_a_kind_of NetX::HTTPUnix
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'Attempts to open address unix:///dev/null as a socket' do
|
|
100
|
+
socket_http = described_class.new(address: 'unix:///dev/null').send(:http)
|
|
101
|
+
expect { socket_http.get('/') }.to raise_error Errno::ENOTSOCK
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#format_command' do
|
|
106
|
+
context 'with a single command' do
|
|
107
|
+
subject do
|
|
108
|
+
json = described_class.new.send(:format_command, 'list vlan')
|
|
109
|
+
JSON.parse(json)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'accepts "list vlan" as a single command and returns JSON' do
|
|
113
|
+
expect(subject['params']['cmds']).to eq(['list vlan'])
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'generates an ID when an explicit id is not given' do
|
|
117
|
+
expect(subject['id']).not_to be_empty
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'uses the explicitly provided ID' do
|
|
121
|
+
json = described_class.new.send(:format_command,
|
|
122
|
+
'list vlan',
|
|
123
|
+
id: 'ID:X')
|
|
124
|
+
expect(JSON.parse(json)['id']).to eq 'ID:X'
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
describe '#eapi_request' do
|
|
130
|
+
subject { api.send(:eapi_request, '{}') }
|
|
131
|
+
|
|
132
|
+
let :mock_http_post do
|
|
133
|
+
post = double(Net::HTTP::Post)
|
|
134
|
+
allow(post).to receive(:basic_auth)
|
|
135
|
+
post
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
before :each do
|
|
139
|
+
allow(Net::HTTP::Post).to receive(:new)
|
|
140
|
+
.with('/command-api/')
|
|
141
|
+
.and_return(mock_http_post)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it 'sets the body of the post to the string provided' do
|
|
145
|
+
allow(mock_http_post).to receive(:body=).with('{}')
|
|
146
|
+
subject
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe '#eapi_call' do
|
|
151
|
+
subject { api.send(:eapi_call, 'show vlan') }
|
|
152
|
+
|
|
153
|
+
before :each do
|
|
154
|
+
mock_response = double(Net::HTTPOK)
|
|
155
|
+
allow(mock_response).to receive(:body)
|
|
156
|
+
.and_return(fixture(:show_vlan, format: :json))
|
|
157
|
+
|
|
158
|
+
mock_http = double(NetX::HTTPUnix)
|
|
159
|
+
allow(mock_http).to receive(:request)
|
|
160
|
+
.and_return(mock_response)
|
|
161
|
+
|
|
162
|
+
allow(api).to receive(:http).and_return(mock_http)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'decodes the JSON response from the switch' do
|
|
166
|
+
expect(subject).to eq(fixture(:show_vlan))
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe '#eapi_action' do
|
|
171
|
+
before :each do
|
|
172
|
+
mock_response = double(Net::HTTPOK)
|
|
173
|
+
allow(mock_response).to receive(:body)
|
|
174
|
+
.and_return(api_response_body)
|
|
175
|
+
|
|
176
|
+
mock_http = double(NetX::HTTPUnix)
|
|
177
|
+
allow(mock_http).to receive(:request)
|
|
178
|
+
.and_return(mock_response)
|
|
179
|
+
|
|
180
|
+
allow(api).to receive(:http).and_return(mock_http)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
context 'format: "json" (default)' do
|
|
184
|
+
let :api_response_body do
|
|
185
|
+
fixture(:show_vlan, format: :json)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it 'accepts a human readable description as argument 2' do
|
|
189
|
+
expect(api.send(:eapi_action, 'show vlan', 'show all vlans'))
|
|
190
|
+
.to eq(fixture(:show_vlan)['result'])
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context 'format: "text"' do
|
|
195
|
+
let :api_response_body do
|
|
196
|
+
fixture(:show_interfaces_switchport_format_text, format: :json)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'accepts a keyword argument of :format => "text"' do
|
|
200
|
+
args = ['show interfaces switchport', 'desc', { format: 'text' }]
|
|
201
|
+
expect(api.send(:eapi_action, *args))
|
|
202
|
+
.to eq(JSON.parse(api_response_body)['result'])
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
describe '#all_vlans' do
|
|
208
|
+
subject { api.all_vlans }
|
|
209
|
+
|
|
210
|
+
before do
|
|
211
|
+
allow(api).to receive(:eapi_call)
|
|
212
|
+
.with('show vlan', {})
|
|
213
|
+
.and_return(fixture(:show_vlan))
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it { is_expected.to be_a_kind_of Hash }
|
|
217
|
+
it { is_expected.to have_key '1' }
|
|
218
|
+
it { is_expected.to have_key '3110' }
|
|
219
|
+
it { is_expected.not_to have_key 'results' }
|
|
220
|
+
|
|
221
|
+
describe '#all_vlans["1"]' do
|
|
222
|
+
subject { api.all_vlans['1'] }
|
|
223
|
+
|
|
224
|
+
%w(status name interfaces dynamic).each do |k|
|
|
225
|
+
it { is_expected.to have_key k }
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
describe '#all_interfaces' do
|
|
231
|
+
subject { api.all_interfaces }
|
|
232
|
+
|
|
233
|
+
before do
|
|
234
|
+
allow(api).to receive(:eapi_call)
|
|
235
|
+
.with('show interfaces', {})
|
|
236
|
+
.and_return(fixture(:show_interfaces))
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it { is_expected.not_to have_key 'results' }
|
|
240
|
+
it { is_expected.to have_key 'Management1' }
|
|
241
|
+
|
|
242
|
+
describe '#all_interfaces["Management1"]' do
|
|
243
|
+
subject { api.all_interfaces['Management1'] }
|
|
244
|
+
|
|
245
|
+
it { is_expected.to be_a_kind_of Hash }
|
|
246
|
+
it 'has an mtu of 1500' do
|
|
247
|
+
expect(subject['mtu']).to eq 1500
|
|
248
|
+
end
|
|
249
|
+
it 'is hardware ethernet' do
|
|
250
|
+
expect(subject['hardware']).to eq 'ethernet'
|
|
251
|
+
end
|
|
252
|
+
it 'has duplex of duplexFull' do
|
|
253
|
+
expect(subject['duplex']).to eq 'duplexFull'
|
|
254
|
+
end
|
|
255
|
+
it 'has bandwidth of 1000000000' do
|
|
256
|
+
expect(subject['bandwidth']).to eq 1_000_000_000
|
|
257
|
+
end
|
|
258
|
+
it 'has an interfaceAddress key with Array value' do
|
|
259
|
+
expect(subject['interfaceAddress']).to be_an Array
|
|
260
|
+
end
|
|
261
|
+
it 'has a physicalAddress of 00:42:00:6e:00:96' do
|
|
262
|
+
expect(subject['physicalAddress']).to eq '00:42:00:6e:00:96'
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
describe '#all_portchannels_detailed' do
|
|
268
|
+
subject { api.all_portchannels_detailed }
|
|
269
|
+
|
|
270
|
+
before :each do
|
|
271
|
+
allow(api).to receive(:eapi_call)
|
|
272
|
+
.with('show etherchannel detailed', format: 'text')
|
|
273
|
+
.and_return(fixture(:s4_show_etherchannel_detailed))
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it { is_expected.not_to have_key 'results' }
|
|
277
|
+
it { is_expected.to have_key 'Port-Channel3' }
|
|
278
|
+
it { is_expected.to have_key 'Port-Channel4' }
|
|
279
|
+
|
|
280
|
+
describe 'nested attribute hash' do
|
|
281
|
+
subject { api.all_portchannels_detailed['Port-Channel3'] }
|
|
282
|
+
it { is_expected.to have_key 'name' }
|
|
283
|
+
it { is_expected.to have_key 'ports' }
|
|
284
|
+
|
|
285
|
+
describe 'ports array' do
|
|
286
|
+
subject { api.all_portchannels_detailed['Port-Channel3']['ports'] }
|
|
287
|
+
it { is_expected.to include('Ethernet1') }
|
|
288
|
+
it { is_expected.to include('Ethernet2') }
|
|
289
|
+
it 'sorts the list of member ports' do
|
|
290
|
+
expect(subject).to eq(subject.sort)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe '#all_portchannel_modes' do
|
|
297
|
+
subject { api.all_portchannel_modes }
|
|
298
|
+
|
|
299
|
+
context 'with two LAGS, one LACP passive, one active' do
|
|
300
|
+
before :each do
|
|
301
|
+
allow(api).to receive(:eapi_call)
|
|
302
|
+
.with('show port-channel summary', format: 'text')
|
|
303
|
+
.and_return(fixture(:show_port_channel_summary_2_lags))
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
let :expected_results do
|
|
307
|
+
{
|
|
308
|
+
'Port-Channel3' => { 'mode' => :passive },
|
|
309
|
+
'Port-Channel4' => { 'mode' => :active }
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it { is_expected.to have_key 'Port-Channel3' }
|
|
314
|
+
it { is_expected.to have_key 'Port-Channel4' }
|
|
315
|
+
it { is_expected.to eq expected_results }
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
context 'with one LAG in LACP static mode' do
|
|
319
|
+
before :each do
|
|
320
|
+
allow(api).to receive(:eapi_call)
|
|
321
|
+
.with('show port-channel summary', format: 'text')
|
|
322
|
+
.and_return(fixture(:show_port_channel_summary_static))
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
let :expected_results do
|
|
326
|
+
{
|
|
327
|
+
'Port-Channel4' => { 'mode' => :active },
|
|
328
|
+
'Port-Channel9' => { 'mode' => :disabled }
|
|
329
|
+
}
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it { is_expected.to have_key 'Port-Channel4' }
|
|
333
|
+
it { is_expected.to have_key 'Port-Channel9' }
|
|
334
|
+
it { is_expected.to eq expected_results }
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
describe '#all_portchannels' do
|
|
339
|
+
subject { api.all_portchannels }
|
|
340
|
+
|
|
341
|
+
before :each do
|
|
342
|
+
allow(api).to receive(:all_portchannels_detailed)
|
|
343
|
+
.and_return(fixture(:all_portchannels_detailed))
|
|
344
|
+
allow(api).to receive(:all_portchannel_modes)
|
|
345
|
+
.and_return(fixture(:all_portchannel_modes))
|
|
346
|
+
allow(api).to receive(:portchannel_min_links).and_return(2)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
it { is_expected.to be_a Hash }
|
|
350
|
+
it { is_expected.to have_key 'Port-Channel4' }
|
|
351
|
+
it { is_expected.to have_key 'Port-Channel9' }
|
|
352
|
+
it 'has a mode attribute for each port channel' do
|
|
353
|
+
modes = subject.values.map { |v| v['mode'] }
|
|
354
|
+
expect(modes).to eq %w(active active)
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
describe '#channel_group_destroy' do
|
|
359
|
+
let :port_channels_detailed do
|
|
360
|
+
data = {
|
|
361
|
+
'name' => 'Port-Channel9',
|
|
362
|
+
'ports' => %w(Ethernet1 Ethernet2)
|
|
363
|
+
}
|
|
364
|
+
{ 'Port-Channel9' => data }
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
before :each do
|
|
368
|
+
allow(api).to receive(:all_portchannels_detailed)
|
|
369
|
+
.and_return(port_channels_detailed)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it 'removes interfaces from the channel group' do
|
|
373
|
+
expect(api).to receive(:interface_unset_channel_group).with('Ethernet1')
|
|
374
|
+
expect(api).to receive(:interface_unset_channel_group).with('Ethernet2')
|
|
375
|
+
api.channel_group_destroy('Port-Channel9')
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it 'raises ArgumentError when the channel group is unknown' do
|
|
379
|
+
expect { api.channel_group_destroy('Port-Channel1') }
|
|
380
|
+
.to raise_error ArgumentError, /Port-Channel1 is not in \[.*?\]/
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
describe '#interface_unset_channel_group' do
|
|
385
|
+
it 'sets no channel-group using the API' do
|
|
386
|
+
cmd = %w(enable configure) << 'interface Ethernet1'
|
|
387
|
+
cmd << 'no channel-group'
|
|
388
|
+
|
|
389
|
+
expect(api).to receive(:eapi_action)
|
|
390
|
+
.with(cmd, 'remove Ethernet1 from channel group')
|
|
391
|
+
|
|
392
|
+
api.interface_unset_channel_group('Ethernet1')
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
describe '#port_channel_destroy' do
|
|
397
|
+
it 'removes the channel interface' do
|
|
398
|
+
cmd = %w(enable configure) << 'no interface Port-Channel1'
|
|
399
|
+
|
|
400
|
+
expect(api).to receive(:eapi_action)
|
|
401
|
+
.with(cmd, 'remove Port-Channel1')
|
|
402
|
+
|
|
403
|
+
api.port_channel_destroy('Port-Channel1')
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
describe '#channel_group_create' do
|
|
408
|
+
context 'with Port-Channel9 and Ethernet1 LACP mode active' do
|
|
409
|
+
it 'sets the interface channel group to active' do
|
|
410
|
+
expect(api).to receive(:interface_set_channel_group)
|
|
411
|
+
.with('Ethernet1', mode: :active, group: 9)
|
|
412
|
+
|
|
413
|
+
api.channel_group_create('Port-Channel9',
|
|
414
|
+
interfaces: ['Ethernet1'],
|
|
415
|
+
mode: :active)
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
describe '#interface_set_channel_group' do
|
|
421
|
+
subject do
|
|
422
|
+
api.interface_set_channel_group('Ethernet1', group: 9, mode: mode)
|
|
423
|
+
end
|
|
424
|
+
let(:cmd_prefix) { ['enable', 'configure', 'interface Ethernet1'] }
|
|
425
|
+
let(:msg) { 'join Ethernet1 to channel group 9' }
|
|
426
|
+
|
|
427
|
+
context 'when mode is active' do
|
|
428
|
+
let(:mode) { :active }
|
|
429
|
+
let(:config_cmd) { 'channel-group 9 mode active' }
|
|
430
|
+
|
|
431
|
+
it 'configures Ethernet1 in group 9 as active' do
|
|
432
|
+
expect(api).to receive(:eapi_action)
|
|
433
|
+
.with([*cmd_prefix, config_cmd], msg)
|
|
434
|
+
|
|
435
|
+
subject
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
context 'when mode is passive' do
|
|
440
|
+
let(:mode) { :passive }
|
|
441
|
+
let(:config_cmd) { 'channel-group 9 mode passive' }
|
|
442
|
+
|
|
443
|
+
it 'configures Ethernet1 in group 9 as passive' do
|
|
444
|
+
expect(api).to receive(:eapi_action)
|
|
445
|
+
.with([*cmd_prefix, config_cmd], msg)
|
|
446
|
+
|
|
447
|
+
subject
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
context 'when mode is disabled' do
|
|
452
|
+
let(:mode) { :disabled }
|
|
453
|
+
let(:config_cmd) { 'channel-group 9 mode on' }
|
|
454
|
+
|
|
455
|
+
it 'configures Ethernet1 in group 9 as static' do
|
|
456
|
+
expect(api).to receive(:eapi_action)
|
|
457
|
+
.with([*cmd_prefix, config_cmd], msg)
|
|
458
|
+
|
|
459
|
+
subject
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
context 'when mode is invalid' do
|
|
464
|
+
it 'raises ArgumentError' do
|
|
465
|
+
expect { api.interface_set_channel_group('Ethernet1', mode: 'bad') }
|
|
466
|
+
.to raise_error ArgumentError, 'Unknown LACP mode bad'
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
describe '#uri' do
|
|
472
|
+
context 'with username and password' do
|
|
473
|
+
let :opts do
|
|
474
|
+
{ address: 'localhost', username: 'foo', password: 'bar' }
|
|
475
|
+
end
|
|
476
|
+
subject { described_class.new(opts).uri.to_s }
|
|
477
|
+
it { is_expected.to eq 'http://foo:bar@localhost' }
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
context 'without username and password' do
|
|
481
|
+
subject { described_class.new(address: 'foo.lan', port: 90).uri.to_s }
|
|
482
|
+
it { is_expected.to eq 'http://admin:puppet@foo.lan:90' }
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
describe '#set_vlan_name' do
|
|
487
|
+
context 'with valid arguments of 3111, "foo"' do
|
|
488
|
+
before do
|
|
489
|
+
allow(api).to receive(:eapi_call)
|
|
490
|
+
.with(['enable', 'configure', 'vlan 3111', 'name foo'], {})
|
|
491
|
+
.and_return(fixture(:enable_configure_vlan_3111_name_foo))
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
it 'names the vlan "foo"' do
|
|
495
|
+
api.set_vlan_name(3111, 'foo')
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
context 'with invalid arguments of "foo", "bar"' do
|
|
500
|
+
before do
|
|
501
|
+
allow(api).to receive(:eapi_call)
|
|
502
|
+
.with(['enable', 'configure', 'vlan foo', 'name bar'], {})
|
|
503
|
+
.and_return(fixture(:enable_configure_vlan_foo_name_bar))
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
it 'raises PuppetX::NetDev::ApiError' do
|
|
507
|
+
expect { api.set_vlan_name('foo', 'bar') }
|
|
508
|
+
.to raise_error PuppetX::NetDev::ApiError
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
describe '#vlan_destroy' do
|
|
514
|
+
context 'with valid arguments of 3111' do
|
|
515
|
+
let :api_response do
|
|
516
|
+
{
|
|
517
|
+
'jsonrpc' => '2.0',
|
|
518
|
+
'result' => [{}, {}, {}],
|
|
519
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
520
|
+
}
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
before do
|
|
524
|
+
allow(api).to receive(:eapi_call)
|
|
525
|
+
.with(['enable', 'configure', 'no vlan 3111'], {})
|
|
526
|
+
.and_return(api_response)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
it 'destroys the vlan without raising errors' do
|
|
530
|
+
api.vlan_destroy(3111)
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
context 'with invalid arguments of "foo"' do
|
|
535
|
+
let :api_response do
|
|
536
|
+
msg = "CLI command 3 of 3 'no vlan foo' failed: invalid command"
|
|
537
|
+
{
|
|
538
|
+
'jsonrpc' => '2.0',
|
|
539
|
+
'id' => '1cc2e684-2928-4bfe-a86c-7a1397ea05fd',
|
|
540
|
+
'error' => {
|
|
541
|
+
'data' => [
|
|
542
|
+
{},
|
|
543
|
+
{},
|
|
544
|
+
{
|
|
545
|
+
'errors' => ["Invalid input (at token 2: 'foo')"]
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
'message' => msg,
|
|
549
|
+
'code' => 1002
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
before do
|
|
555
|
+
allow(api).to receive(:eapi_call)
|
|
556
|
+
.with(['enable', 'configure', 'no vlan foo'], {})
|
|
557
|
+
.and_return(api_response)
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
it 'raises PuppetX::NetDev::ApiError' do
|
|
561
|
+
expect { api.vlan_destroy('foo') }.to raise_error PuppetX::NetDev::ApiError
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
describe '#set_vlan_state' do
|
|
567
|
+
context 'with valid arguments' do
|
|
568
|
+
let :api_response do
|
|
569
|
+
{
|
|
570
|
+
'jsonrpc' => '2.0',
|
|
571
|
+
'result' => [{}, {}, {}],
|
|
572
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
573
|
+
}
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
it 'returns the API response as a Hash when state is "active"' do
|
|
577
|
+
allow(api).to receive(:eapi_call)
|
|
578
|
+
.with(['enable', 'configure', 'vlan 3111', 'state active'], {})
|
|
579
|
+
.and_return(api_response)
|
|
580
|
+
api.set_vlan_state(3111, 'active')
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it 'returns the API response as a Hash when state is "suspend"' do
|
|
584
|
+
allow(api).to receive(:eapi_call)
|
|
585
|
+
.with(['enable', 'configure', 'vlan 3111', 'state suspend'], {})
|
|
586
|
+
.and_return(api_response)
|
|
587
|
+
api.set_vlan_state(3111, 'suspend')
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
context 'with invalid arguments' do
|
|
592
|
+
let :api_response do
|
|
593
|
+
msg = "CLI command 4 of 4 'state foo' failed: invalid command"
|
|
594
|
+
{
|
|
595
|
+
'jsonrpc' => '2.0',
|
|
596
|
+
'id' => '1ed4e6ba-89f0-45fa-aeba-f7816b4e7da3',
|
|
597
|
+
'error' => {
|
|
598
|
+
'data' => [
|
|
599
|
+
{},
|
|
600
|
+
{},
|
|
601
|
+
{
|
|
602
|
+
'errors' => ["Invalid input (at token 1: 'foo')"]
|
|
603
|
+
}
|
|
604
|
+
],
|
|
605
|
+
'message' => msg,
|
|
606
|
+
'code' => 1002
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
before do
|
|
612
|
+
allow(api).to receive(:eapi_call)
|
|
613
|
+
.with(['enable', 'configure', 'vlan 3111', 'state foo'], {})
|
|
614
|
+
.and_return(api_response)
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
it 'raises PuppetX::NetDev::ApiError' do
|
|
618
|
+
expect { api.set_vlan_state(3111, 'foo') }
|
|
619
|
+
.to raise_error PuppetX::NetDev::ApiError
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
describe '#set_interface_state' do
|
|
625
|
+
context 'with valid arguments' do
|
|
626
|
+
let :api_response do
|
|
627
|
+
{
|
|
628
|
+
'jsonrpc' => '2.0',
|
|
629
|
+
'result' => [{}, {}, {}],
|
|
630
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
631
|
+
}
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
it 'accepts "shutdown"' do
|
|
635
|
+
allow(api).to receive(:eapi_call)
|
|
636
|
+
.with(['enable', 'configure', 'interface Ethernet1', 'shutdown'], {})
|
|
637
|
+
.and_return(api_response)
|
|
638
|
+
api.set_interface_state('Ethernet1', 'shutdown')
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
it 'accepts "no shutdown"' do
|
|
642
|
+
args = ['enable', 'configure', 'interface Ethernet1', 'no shutdown']
|
|
643
|
+
allow(api).to receive(:eapi_call)
|
|
644
|
+
.with(args, {})
|
|
645
|
+
.and_return(api_response)
|
|
646
|
+
api.set_interface_state('Ethernet1', 'no shutdown')
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
context 'with invalid arguments' do
|
|
650
|
+
let :api_response do
|
|
651
|
+
{
|
|
652
|
+
'jsonrpc' => '2.0',
|
|
653
|
+
'id' => 'f449e942-940f-499e-989f-74ab4b8b9950',
|
|
654
|
+
'error' => {
|
|
655
|
+
'data' => [
|
|
656
|
+
{},
|
|
657
|
+
{},
|
|
658
|
+
{},
|
|
659
|
+
{ 'errors' => ["Invalid input (at token 0: 'garbage')"] }
|
|
660
|
+
],
|
|
661
|
+
'message' => "CLI command 4 of 4 'garbage' failed: invalid command",
|
|
662
|
+
'code' => 1002
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
it 'raises PuppetX::NetDev::ApiError on an API error' do
|
|
668
|
+
allow(api).to receive(:eapi_call)
|
|
669
|
+
.with(['enable', 'configure', 'interface Ethernet1', 'garbage'], {})
|
|
670
|
+
.and_return(api_response)
|
|
671
|
+
expect do
|
|
672
|
+
api.set_interface_state('Ethernet1', 'garbage')
|
|
673
|
+
end.to raise_error PuppetX::NetDev::ApiError
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
describe '#set_interface_description' do
|
|
679
|
+
context 'with valid arguments' do
|
|
680
|
+
let :api_response do
|
|
681
|
+
{
|
|
682
|
+
'jsonrpc' => '2.0',
|
|
683
|
+
'result' => [{}, {}, {}],
|
|
684
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
685
|
+
}
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
it 'accepts a non-empty string' do
|
|
689
|
+
preamble = ['enable', 'configure', 'interface Ethernet1']
|
|
690
|
+
allow(api).to receive(:eapi_call)
|
|
691
|
+
.with([*preamble, 'description foobar'], {})
|
|
692
|
+
.and_return(api_response)
|
|
693
|
+
api.set_interface_description('Ethernet1', 'foobar')
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
context 'with invalid arguments' do
|
|
698
|
+
let :api_response do
|
|
699
|
+
msg = "CLI command 4 of 4 'description ' failed: invalid command"
|
|
700
|
+
{
|
|
701
|
+
'jsonrpc' => '2.0',
|
|
702
|
+
'id' => 'b2c792ea-f256-42f3-a60d-5a094b29fb0b',
|
|
703
|
+
'error' => {
|
|
704
|
+
'data' => [
|
|
705
|
+
{},
|
|
706
|
+
{},
|
|
707
|
+
{},
|
|
708
|
+
{ 'errors' => ['incomplete command (at token 1: None)'] }
|
|
709
|
+
],
|
|
710
|
+
'message' => msg,
|
|
711
|
+
'code' => 1002
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
it 'raises PuppetX::NetDev::ApiError on an API error' do
|
|
717
|
+
args = ['enable', 'configure', 'interface Ethernet1', 'description ']
|
|
718
|
+
allow(api).to receive(:eapi_call)
|
|
719
|
+
.with(args, {})
|
|
720
|
+
.and_return(api_response)
|
|
721
|
+
expect do
|
|
722
|
+
api.set_interface_description('Ethernet1', '')
|
|
723
|
+
end.to raise_error PuppetX::NetDev::ApiError, /incomplete command/
|
|
724
|
+
end
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
describe '#set_interface_speed' do
|
|
729
|
+
context 'with valid arguments' do
|
|
730
|
+
let :api_response do
|
|
731
|
+
{
|
|
732
|
+
'jsonrpc' => '2.0',
|
|
733
|
+
'result' => [{}, {}, {}],
|
|
734
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
735
|
+
}
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
it 'accepts 1000full' do
|
|
739
|
+
preamble = ['enable', 'configure', 'interface Ethernet1']
|
|
740
|
+
allow(api).to receive(:eapi_call)
|
|
741
|
+
.with([*preamble, 'speed forced 1000full'], {})
|
|
742
|
+
.and_return(api_response)
|
|
743
|
+
api.set_interface_speed('Ethernet1', '1000full')
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
context 'when the API returns an error' do
|
|
748
|
+
let :api_response do
|
|
749
|
+
errors = 'Speed and duplex settings are not compatible '\
|
|
750
|
+
'with transceiver for interface Ethernet1.'
|
|
751
|
+
{
|
|
752
|
+
'jsonrpc' => '2.0',
|
|
753
|
+
'id' => 'd110e10b-a3e9-4378-8361-fd04f1f552fd',
|
|
754
|
+
'error' => {
|
|
755
|
+
'data' => [
|
|
756
|
+
{},
|
|
757
|
+
{},
|
|
758
|
+
{},
|
|
759
|
+
{ 'errors' => [*errors] }
|
|
760
|
+
],
|
|
761
|
+
'message' => "CLI command 4 of 4 'speed forced 1000full'"\
|
|
762
|
+
' failed: could not run command',
|
|
763
|
+
'code' => 1000
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
it 'raises PuppetX::NetDev::ApiError with error message from the api' do
|
|
769
|
+
preamble = ['enable', 'configure', 'interface Ethernet1']
|
|
770
|
+
allow(api).to receive(:eapi_call)
|
|
771
|
+
.with([*preamble, 'speed forced 1000full'], {})
|
|
772
|
+
.and_return(api_response)
|
|
773
|
+
expect do
|
|
774
|
+
api.set_interface_speed('Ethernet1', '1000full')
|
|
775
|
+
end.to raise_error PuppetX::NetDev::ApiError,
|
|
776
|
+
/not compatible with transceiver/
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
describe '#set_interface_mtu' do
|
|
782
|
+
context 'with valid arguments' do
|
|
783
|
+
let :api_response do
|
|
784
|
+
{
|
|
785
|
+
'jsonrpc' => '2.0',
|
|
786
|
+
'result' => [{}, {}, {}],
|
|
787
|
+
'id' => '7af750fd-9324-4f91-b4fb-cedf0c6d6a91'
|
|
788
|
+
}
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
it 'accepts 9000' do
|
|
792
|
+
preamble = ['enable', 'configure', 'interface Ethernet1']
|
|
793
|
+
allow(api).to receive(:eapi_call)
|
|
794
|
+
.with([*preamble, 'mtu 9000'], {})
|
|
795
|
+
.and_return(api_response)
|
|
796
|
+
api.set_interface_mtu('Ethernet1', 9000)
|
|
797
|
+
end
|
|
798
|
+
end
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
describe '#portchannel_min_links' do
|
|
802
|
+
subject { api.portchannel_min_links(name) }
|
|
803
|
+
|
|
804
|
+
before :each do
|
|
805
|
+
api_commands = ['enable', "show running-config interfaces #{name}"]
|
|
806
|
+
msg = 'obtain port channel min links value'
|
|
807
|
+
|
|
808
|
+
allow(api).to receive(:eapi_action)
|
|
809
|
+
.with(api_commands, msg, format: 'text')
|
|
810
|
+
.and_return(fixture(fixture_name))
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
context 'min-links 2 in runnning-config' do
|
|
814
|
+
let(:name) { 'Port-Channel4' }
|
|
815
|
+
let(:fixture_name) { :portchannel_min_links_1 }
|
|
816
|
+
|
|
817
|
+
it 'returns 2 for the number of minimum links' do
|
|
818
|
+
is_expected.to eq(2)
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
context 'min-links not in runnning-config' do
|
|
823
|
+
let(:name) { 'Port-Channel9' }
|
|
824
|
+
let(:fixture_name) { :portchannel_min_links_2 }
|
|
825
|
+
|
|
826
|
+
it 'assumes and returns 0 for the number of minimum links' do
|
|
827
|
+
is_expected.to eq(0)
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
describe '#set_portchannel_min_links' do
|
|
833
|
+
subject { api.set_portchannel_min_links(name, min_links) }
|
|
834
|
+
let(:name) { 'Port-Channel4' }
|
|
835
|
+
let(:expected) do
|
|
836
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
837
|
+
cmd << "port-channel min-links #{min_links}"
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
[0, 1, 2, 3].each do |val|
|
|
841
|
+
context "when min-links is #{val}" do
|
|
842
|
+
let(:min_links) { val }
|
|
843
|
+
|
|
844
|
+
it "calls eapi_action to set min links to #{val}" do
|
|
845
|
+
expect(api).to receive(:eapi_action)
|
|
846
|
+
.with(expected, 'set port-channel min links')
|
|
847
|
+
subject
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
end
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
describe '#parse_min_links' do
|
|
854
|
+
subject { api.parse_min_links(text) }
|
|
855
|
+
context 'when text does not contain a min-links line' do
|
|
856
|
+
let(:text) { "interface Port-Channel4\n description Office Backbone\n" }
|
|
857
|
+
it { is_expected.to eq(0) }
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
context 'when text contain min-links 2' do
|
|
861
|
+
let(:text) { "interface Port-Channel4\n port-channel min-links 2\n" }
|
|
862
|
+
it { is_expected.to eq(2) }
|
|
863
|
+
end
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
describe '#get_flowcontrol' do
|
|
867
|
+
subject { api.get_flowcontrol(name) }
|
|
868
|
+
let(:name) { 'Ethernet1' }
|
|
869
|
+
|
|
870
|
+
before :each do
|
|
871
|
+
allow(api).to receive(:eapi_action)
|
|
872
|
+
.with('show flowcontrol interface Ethernet1',
|
|
873
|
+
'get flowcontrol config',
|
|
874
|
+
format: 'text')
|
|
875
|
+
.and_return(fixture(:show_flowcontrol_et1))
|
|
876
|
+
end
|
|
877
|
+
it { is_expected.to eq(send: 'off', receive: 'off') }
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
describe '#parse_flowcontrol_single' do
|
|
881
|
+
subject { api.parse_flowcontrol_single(text) }
|
|
882
|
+
|
|
883
|
+
context 'when the input text is valid' do
|
|
884
|
+
let(:text) do
|
|
885
|
+
<<-TEXT
|
|
886
|
+
Port Send FlowControl Receive FlowControl RxPause TxPause
|
|
887
|
+
admin oper admin oper
|
|
888
|
+
---------- -------- -------- -------- -------- ------------- -------------
|
|
889
|
+
Et1 off unknown off unknown 0 0
|
|
890
|
+
TEXT
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
it { is_expected.to eq(send: 'off', receive: 'off') }
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
context 'when the input text is invalid' do
|
|
897
|
+
let(:text) { 'garbage' }
|
|
898
|
+
it 'raises ArgumentError' do
|
|
899
|
+
expect { subject }.to raise_error ArgumentError, /could not parse/
|
|
900
|
+
end
|
|
901
|
+
end
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
describe '#set_flowcontrol_send' do
|
|
905
|
+
subject { api.set_flowcontrol_send(name, value) }
|
|
906
|
+
let(:name) { 'Ethernet1' }
|
|
907
|
+
let(:value) { :on }
|
|
908
|
+
let(:expected) do
|
|
909
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
910
|
+
cmd << "flowcontrol send #{value}"
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
it 'calls eapi_action to configure the device' do
|
|
914
|
+
expect(api).to receive(:eapi_action)
|
|
915
|
+
.with(expected, 'configure flowcontrol send')
|
|
916
|
+
subject
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
describe '#set_flowcontrol_recv' do
|
|
921
|
+
subject { api.set_flowcontrol_recv(name, value) }
|
|
922
|
+
let(:name) { 'Ethernet1' }
|
|
923
|
+
let(:value) { :on }
|
|
924
|
+
let(:expected) do
|
|
925
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
926
|
+
cmd << "flowcontrol receive #{value}"
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
it 'calls eapi_action to configure the device' do
|
|
930
|
+
expect(api).to receive(:eapi_action)
|
|
931
|
+
.with(expected, 'configure flowcontrol receive')
|
|
932
|
+
subject
|
|
933
|
+
end
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
describe 'PuppetX::NetDev::EosProviderMethods' do
|
|
938
|
+
let :fake_provider do
|
|
939
|
+
klass = Class.new do
|
|
940
|
+
include PuppetX::NetDev::EosProviderMethods
|
|
941
|
+
extend PuppetX::NetDev::EosProviderMethods
|
|
942
|
+
end
|
|
943
|
+
klass.new
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
describe '#api' do
|
|
947
|
+
subject { fake_provider.api }
|
|
948
|
+
it { is_expected.to be_a PuppetX::NetDev::EosApi }
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
describe '#bandwidth_to_speed' do
|
|
952
|
+
it 'converts 10_000_000 to 10m' do
|
|
953
|
+
expect(fake_provider.bandwidth_to_speed(10_000_000)).to eq '10m'
|
|
954
|
+
end
|
|
955
|
+
it 'converts 10_000_000_000 to 10g' do
|
|
956
|
+
expect(fake_provider.bandwidth_to_speed(10_000_000_000)).to eq '10g'
|
|
957
|
+
end
|
|
958
|
+
it 'converts 1_000_000_000 to 1g' do
|
|
959
|
+
expect(fake_provider.bandwidth_to_speed(1_000_000_000)).to eq '1g'
|
|
960
|
+
end
|
|
961
|
+
it 'converts 56_000_000_000 to 56g' do
|
|
962
|
+
expect(fake_provider.bandwidth_to_speed(56_000_000_000)).to eq '56g'
|
|
963
|
+
end
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
describe '#duplex_to_value' do
|
|
967
|
+
it 'converts "duplexFull" to :full' do
|
|
968
|
+
expect(fake_provider.duplex_to_value('duplexFull')).to eq :full
|
|
969
|
+
end
|
|
970
|
+
it 'converts "duplexHalf" to :half' do
|
|
971
|
+
expect(fake_provider.duplex_to_value('duplexHalf')).to eq :half
|
|
972
|
+
end
|
|
973
|
+
it 'raises ArgumentError with unknown input' do
|
|
974
|
+
expect do
|
|
975
|
+
fake_provider.duplex_to_value('garbage')
|
|
976
|
+
end.to raise_error ArgumentError
|
|
977
|
+
end
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
describe '#interface_status_to_enable' do
|
|
981
|
+
it 'converts "connected" to :true' do
|
|
982
|
+
expect(fake_provider.interface_status_to_enable('connected')).to eq :true
|
|
983
|
+
end
|
|
984
|
+
it 'converts "disabled" to :false' do
|
|
985
|
+
expect(fake_provider.interface_status_to_enable('disabled')).to eq :false
|
|
986
|
+
end
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
describe 'interface_attributes' do
|
|
990
|
+
let :attr_hash do
|
|
991
|
+
{
|
|
992
|
+
'interfaceStatus' => 'connected',
|
|
993
|
+
'bandwidth' => 10_000_000_000,
|
|
994
|
+
'duplex' => 'duplexFull'
|
|
995
|
+
}
|
|
996
|
+
end
|
|
997
|
+
subject { fake_provider.interface_attributes(attr_hash) }
|
|
998
|
+
it { is_expected.to be_a_kind_of Hash }
|
|
999
|
+
end
|
|
1000
|
+
end
|