rbeapi 0.5.1 → 1.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 (57) hide show
  1. data/CHANGELOG.md +211 -76
  2. data/Gemfile +14 -3
  3. data/README.md +74 -38
  4. data/Rakefile +38 -17
  5. data/gems/inifile/inifile.spec.tmpl +31 -4
  6. data/gems/net_http_unix/net_http_unix.spec.tmpl +34 -8
  7. data/gems/netaddr/netaddr.spec.tmpl +31 -5
  8. data/guide/getting-started.rst +95 -64
  9. data/guide/installation.rst +27 -6
  10. data/guide/release-notes.rst +5 -1
  11. data/guide/testing.rst +5 -2
  12. data/guide/upgrading.rst +2 -0
  13. data/lib/rbeapi/api/dns.rb +8 -2
  14. data/lib/rbeapi/api/interfaces.rb +107 -21
  15. data/lib/rbeapi/api/ipinterfaces.rb +48 -0
  16. data/lib/rbeapi/api/prefixlists.rb +53 -23
  17. data/lib/rbeapi/api/routemaps.rb +11 -0
  18. data/lib/rbeapi/api/stp.rb +6 -3
  19. data/lib/rbeapi/api/switchports.rb +5 -11
  20. data/lib/rbeapi/api/system.rb +1 -1
  21. data/lib/rbeapi/api/users.rb +2 -0
  22. data/lib/rbeapi/api/varp.rb +6 -0
  23. data/lib/rbeapi/api/vlans.rb +44 -0
  24. data/lib/rbeapi/api/vrrp.rb +13 -0
  25. data/lib/rbeapi/client.rb +19 -4
  26. data/lib/rbeapi/switchconfig.rb +330 -0
  27. data/lib/rbeapi/version.rb +1 -1
  28. data/rbeapi.gemspec +2 -0
  29. data/rbeapi.spec.tmpl +30 -3
  30. data/spec/fixtures/.gitignore +1 -0
  31. data/spec/support/matchers/switch_config_sections.rb +80 -0
  32. data/spec/system/rbeapi/api/interfaces_base_spec.rb +32 -3
  33. data/spec/system/rbeapi/api/interfaces_ethernet_spec.rb +56 -8
  34. data/spec/system/rbeapi/api/interfaces_portchannel_spec.rb +33 -1
  35. data/spec/system/rbeapi/api/interfaces_vxlan_spec.rb +27 -0
  36. data/spec/system/rbeapi/api/ipinterfaces_spec.rb +34 -1
  37. data/spec/system/rbeapi/api/prefixlists_spec.rb +198 -0
  38. data/spec/system/rbeapi/api/stp_instances_spec.rb +49 -5
  39. data/spec/system/rbeapi/api/switchports_spec.rb +15 -9
  40. data/spec/system/rbeapi/api/vlans_spec.rb +46 -0
  41. data/spec/unit/rbeapi/api/interfaces/base_spec.rb +1 -1
  42. data/spec/unit/rbeapi/api/interfaces/ethernet_spec.rb +1 -1
  43. data/spec/unit/rbeapi/api/interfaces/portchannel_spec.rb +9 -2
  44. data/spec/unit/rbeapi/api/interfaces/vxlan_spec.rb +1 -1
  45. data/spec/unit/rbeapi/api/prefixlists/default_spec.rb +202 -0
  46. data/spec/unit/rbeapi/api/prefixlists/fixture_prefixlists.text +11 -0
  47. data/spec/unit/rbeapi/api/routemaps/default_spec.rb +5 -0
  48. data/spec/unit/rbeapi/api/switchports/default_spec.rb +4 -4
  49. data/spec/unit/rbeapi/api/system/default_spec.rb +5 -0
  50. data/spec/unit/rbeapi/api/system/fixture_system.text +1 -0
  51. data/spec/unit/rbeapi/api/vlans/default_spec.rb +30 -0
  52. data/spec/unit/rbeapi/api/vrrp/default_spec.rb +10 -0
  53. data/spec/unit/rbeapi/client_spec.rb +42 -0
  54. data/spec/unit/rbeapi/switchconfig2_spec.rb +119 -0
  55. data/spec/unit/rbeapi/switchconfig3_spec.rb +125 -0
  56. data/spec/unit/rbeapi/switchconfig_spec.rb +335 -0
  57. metadata +21 -7
@@ -0,0 +1,198 @@
1
+ #
2
+ # Copyright (c) 2016, Arista Networks, Inc.
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are
7
+ # met:
8
+ #
9
+ # Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ #
12
+ # Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # Neither the name of Arista Networks nor the names of its
17
+ # contributors may be used to endorse or promote products derived from
18
+ # this software without specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
24
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29
+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
30
+ # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+ require 'spec_helper'
33
+
34
+ require 'rbeapi/client'
35
+ require 'rbeapi/api/prefixlists'
36
+
37
+ describe Rbeapi::Api::Prefixlists do
38
+ subject { described_class.new(node) }
39
+
40
+ let(:node) do
41
+ Rbeapi::Client.config.read(fixture_file('dut.conf'))
42
+ Rbeapi::Client.connect_to('dut')
43
+ end
44
+
45
+ describe '#get' do
46
+ before do
47
+ node.config(['no ip prefix-list test1',
48
+ 'ip prefix-list test1',
49
+ 'seq 10 permit 1.2.3.0/24',
50
+ 'seq 20 permit 2.3.4.0/24 le 30',
51
+ 'seq 30 deny 3.4.5.0/24 ge 26 le 30',
52
+ 'permit 5.6.7.16/28 eq 29'])
53
+ end
54
+
55
+ let(:prefixlist) { subject.get('test1') }
56
+
57
+ it 'returns the prefix list for an existing name' do
58
+ expect(prefixlist).to be_a_kind_of(Array)
59
+ end
60
+
61
+ it 'returns all rules as hash' do
62
+ expect(prefixlist).to all ( be_an(Hash) )
63
+ end
64
+
65
+ it 'has all keys for each rule' do
66
+ prefixlist.each do |rule|
67
+ expect(rule).to have_key('seq')
68
+ expect(rule).to have_key('prefix')
69
+ expect(rule).to have_key('action')
70
+ end
71
+ end
72
+
73
+ let(:values) do
74
+ [
75
+ {
76
+ 'seq' => '10',
77
+ 'action' => 'permit',
78
+ 'prefix' => '1.2.3.0/24'
79
+ },
80
+ {
81
+ 'seq' => '20',
82
+ 'action' => 'permit',
83
+ 'prefix' => '2.3.4.0/24 le 30'
84
+ },
85
+ {
86
+ 'seq' => '30',
87
+ 'action' => 'deny',
88
+ 'prefix' => '3.4.5.0/24 ge 26 le 30'
89
+ },
90
+ {
91
+ 'seq' => '40',
92
+ 'action' => 'permit',
93
+ 'prefix' => '5.6.7.16/28 eq 29'
94
+ }
95
+ ]
96
+ end
97
+
98
+ it 'returns the correct values for all the keys' do
99
+ expect(prefixlist).to eq(values)
100
+ end
101
+ end
102
+
103
+ describe '#getall' do
104
+ let(:del_pref_lists) {
105
+ subject.getall.keys.map { |k| "no ip prefix-list #{k}" }
106
+ }
107
+
108
+ before do
109
+ node.config(del_pref_lists +
110
+ ['ip prefix-list test1',
111
+ 'seq 10 permit 1.2.3.0/24',
112
+ 'seq 20 permit 2.3.4.0/24 le 30',
113
+ 'seq 30 deny 3.4.5.0/24 ge 26 le 30',
114
+ 'permit 5.6.7.8/28',
115
+ 'ip prefix-list test2',
116
+ 'seq 10 permit 10.11.0.0/16',
117
+ 'seq 20 permit 10.12.0.0/16 le 24',
118
+ 'ip prefix-list test3'])
119
+ end
120
+
121
+ let(:prefixlists) { subject.getall }
122
+
123
+ it 'returns the collection as hash' do
124
+ expect(prefixlists).to be_a_kind_of(Hash)
125
+ end
126
+
127
+ it 'returns all prefix lists as array' do
128
+ expect(prefixlists).to all ( be_an(Array) )
129
+ end
130
+
131
+ it 'has three prefix lists' do
132
+ expect(prefixlists.size).to eq(3)
133
+ end
134
+ end
135
+
136
+ describe '#create' do
137
+ before do
138
+ node.config('no ip prefix-list test4')
139
+ end
140
+
141
+ it 'creates a new prefix list' do
142
+ expect(subject.get('test4')).to eq(nil)
143
+ expect(subject.create('test4')).to be_truthy
144
+ expect(subject.get('test4')).to eq([])
145
+ expect(subject.get('test4').size).to eq(0)
146
+ end
147
+ end
148
+
149
+ describe '#add_rule' do
150
+ before do
151
+ node.config(['no ip prefix-list test5',
152
+ 'ip prefix-list test5'])
153
+ end
154
+
155
+ it 'adds rule to an existing prefix list' do
156
+ expect(subject.get('test5')).to eq([])
157
+ expect(subject.add_rule('test5', 'permit', '1.1.1.0/24')).to be_truthy
158
+ expect(subject.get('test5')).to eq([{
159
+ "seq" => "10",
160
+ "action" => "permit",
161
+ "prefix" => "1.1.1.0/24"}])
162
+ end
163
+
164
+ it 'adds rule to a non-existent prefix list' do
165
+ expect(subject.get('test6')).to eq(nil)
166
+ expect(subject.add_rule('test6', 'deny', '2.2.2.0/24')).to be_truthy
167
+ expect(subject.get('test6')).to eq([{
168
+ "seq" => "10",
169
+ "action" => "deny",
170
+ "prefix" => "2.2.2.0/24"}])
171
+ end
172
+ end
173
+
174
+ describe '#delete' do
175
+ before do
176
+ node.config(['no ip prefix-list test7',
177
+ 'no ip prefix-list test8',
178
+ 'ip prefix-list test7',
179
+ 'seq 10 permit 7.7.0.0/16',
180
+ 'ip prefix-list test8',
181
+ 'seq 10 permit 8.8.0.0/16',
182
+ 'deny 9.9.0.0/16 le 24'])
183
+ end
184
+
185
+ it 'delets a prefix list' do
186
+ expect(subject.get('test7')).to be_truthy
187
+ expect(subject.delete('test7')).to be_truthy
188
+ expect(subject.get('test7')).to eq(nil)
189
+ end
190
+
191
+ it 'deletes a rule in the prefix list' do
192
+ expect(subject.get('test8')).to be_truthy
193
+ expect(subject.delete('test8', 20)).to be_truthy
194
+ expect(subject.get('test8').size).to eq(1)
195
+ expect(subject.get('test8')[1]).to eq(nil)
196
+ end
197
+ end
198
+ end
@@ -15,14 +15,24 @@ describe Rbeapi::Api::StpInstances do
15
15
  before do
16
16
  node.config(['spanning-tree mode mstp',
17
17
  'spanning-tree mst configuration',
18
- 'instance 1 vlans 1', 'exit'])
18
+ 'instance 10 vlans 100', 'exit'])
19
19
  end
20
20
 
21
21
  it 'returns the stp instance resource as a hash' do
22
+ expect(subject.get('10')).to be_a_kind_of(Hash)
23
+ end
24
+
25
+ it 'returns the default stp instance resources as a hash' do
26
+ expect(subject.get('0')).to be_a_kind_of(Hash)
22
27
  expect(subject.get('1')).to be_a_kind_of(Hash)
23
28
  end
24
29
 
25
30
  it 'returns the instance priority' do
31
+ expect(subject.get('10')).to include(:priority)
32
+ end
33
+
34
+ it 'returns the default instances priority' do
35
+ expect(subject.get('0')).to include(:priority)
26
36
  expect(subject.get('1')).to include(:priority)
27
37
  end
28
38
  end
@@ -41,38 +51,65 @@ describe Rbeapi::Api::StpInstances do
41
51
  before do
42
52
  node.config(['spanning-tree mode mstp',
43
53
  'spanning-tree mst configuration',
44
- 'instance 1 vlans 1', 'exit'])
54
+ 'instance 1 vlans 1',
55
+ 'instance 10 vlans 100', 'exit'])
45
56
  end
46
57
 
47
58
  it 'deletes the mst instance' do
59
+ expect(subject.get('10')).not_to be_nil
60
+ expect(subject.delete('10')).to be_truthy
61
+ expect(subject.get('10')).to be_nil
62
+ end
63
+
64
+ it 'does not delete the default mst instance' do
48
65
  expect(subject.get('1')).not_to be_nil
49
66
  expect(subject.delete('1')).to be_truthy
50
- expect(subject.get('1')).to be_nil
67
+ expect(subject.get('1')).not_to be_nil
51
68
  end
52
69
  end
53
70
 
54
71
  describe '#set_priority' do
55
72
  before do
56
- node.config(['default spanning-tree mst 1 priority',
73
+ node.config(['default spanning-tree mst 10 priority',
74
+ 'no spanning-tree mst 1 priority',
57
75
  'spanning-tree mode mstp',
58
76
  'default spanning-tree mst configuration',
59
77
  'spanning-tree mst configuration',
60
- 'instance 1 vlans 1', 'exit'])
78
+ 'instance 10 vlans 100', 'exit'])
61
79
  end
62
80
 
63
81
  it 'set the instance priority' do
82
+ expect(subject.get('10')[:priority]).to eq('32768')
83
+ expect(subject.set_priority('10', value: '16384')).to be_truthy
84
+ expect(subject.get('10')[:priority]).to eq('16384')
85
+ end
86
+
87
+ it 'set the default instance priority' do
64
88
  expect(subject.get('1')[:priority]).to eq('32768')
65
89
  expect(subject.set_priority('1', value: '4096')).to be_truthy
66
90
  expect(subject.get('1')[:priority]).to eq('4096')
67
91
  end
68
92
 
69
93
  it 'set the instance priority to default' do
94
+ expect(subject.set_priority('10', value: '16384',
95
+ default: true)).to be_truthy
96
+ expect(subject.get('10')[:priority]).to eq('32768')
97
+ end
98
+
99
+ it 'set the default instance priority to default' do
70
100
  expect(subject.set_priority('1', value: '4096',
71
101
  default: true)).to be_truthy
72
102
  expect(subject.get('1')[:priority]).to eq('32768')
73
103
  end
74
104
 
75
105
  it 'set the instance priority to enable false' do
106
+ expect(subject.set_priority('10', value: '16384',
107
+ default: false,
108
+ enable: false)).to be_truthy
109
+ expect(subject.get('10')[:priority]).to eq('32768')
110
+ end
111
+
112
+ it 'set the default instance priority to enable false' do
76
113
  expect(subject.set_priority('1', value: '4096',
77
114
  default: false,
78
115
  enable: false)).to be_truthy
@@ -80,6 +117,13 @@ describe Rbeapi::Api::StpInstances do
80
117
  end
81
118
 
82
119
  it 'set the instance priority to enable true' do
120
+ expect(subject.set_priority('10', value: '16384',
121
+ default: false,
122
+ enable: true)).to be_truthy
123
+ expect(subject.get('10')[:priority]).to eq('16384')
124
+ end
125
+
126
+ it 'set the default instance priority to enable true' do
83
127
  expect(subject.set_priority('1', value: '4096',
84
128
  default: false,
85
129
  enable: true)).to be_truthy
@@ -191,34 +191,40 @@ describe Rbeapi::Api::Switchports do
191
191
  .to raise_error(ArgumentError)
192
192
  end
193
193
 
194
- it 'sets vlan 8 and 9 to the trunk allowed vlans' do
194
+ it 'sets vlan 8-10 and 100 to the trunk allowed vlans' do
195
195
  node.config(['interface Ethernet1', 'switchport trunk allowed vlan none'])
196
196
  expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to be_empty
197
- expect(subject.set_trunk_allowed_vlans('Ethernet1', value: [8, 9]))
197
+ expect(subject.set_trunk_allowed_vlans('Ethernet1', value: ['8-10',
198
+ '100']))
198
199
  .to be_truthy
199
- expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq([8, 9])
200
+ expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq(['8-10',
201
+ '100'])
200
202
  end
201
203
 
202
204
  it 'negate switchport trunk allowed vlan' do
203
205
  node.config(['interface Ethernet1', 'switchport trunk allowed vlan none'])
204
206
  expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to be_empty
205
- expect(subject.set_trunk_allowed_vlans('Ethernet1', value: [8, 9]))
207
+ expect(subject.set_trunk_allowed_vlans('Ethernet1', value: ['8-10',
208
+ '100']))
206
209
  .to be_truthy
207
- expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq([8, 9])
210
+ expect(subject.get('Ethernet1')[:trunk_allowed_vlans])
211
+ .to eq(['8-10', '100'])
208
212
  expect(subject.set_trunk_allowed_vlans('Ethernet1', enable: false))
209
213
  .to be_truthy
210
- expect(subject.get('Ethernet1')[:trunk_allowed_vlans].length).to eq(4094)
214
+ expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq(['1-4094'])
211
215
  end
212
216
 
213
217
  it 'default switchport trunk allowed vlan' do
214
218
  node.config(['interface Ethernet1', 'switchport trunk allowed vlan none'])
215
219
  expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to be_empty
216
- expect(subject.set_trunk_allowed_vlans('Ethernet1', value: [8, 9]))
220
+ expect(subject.set_trunk_allowed_vlans('Ethernet1', value: ['8-10',
221
+ '100']))
217
222
  .to be_truthy
218
- expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq([8, 9])
223
+ expect(subject.get('Ethernet1')[:trunk_allowed_vlans])
224
+ .to eq(['8-10', '100'])
219
225
  expect(subject.set_trunk_allowed_vlans('Ethernet1', default: true))
220
226
  .to be_truthy
221
- expect(subject.get('Ethernet1')[:trunk_allowed_vlans].length).to eq(4094)
227
+ expect(subject.get('Ethernet1')[:trunk_allowed_vlans]).to eq(['1-4094'])
222
228
  end
223
229
  end
224
230
 
@@ -159,4 +159,50 @@ describe Rbeapi::Api::Vlans do
159
159
  expect(subject.get('1')[:trunk_groups]).not_to include('foo')
160
160
  end
161
161
  end
162
+
163
+ describe '#set_trunk_groups' do
164
+ before do
165
+ node.config(['vlan 1', 'default trunk group'])
166
+ end
167
+
168
+ it 'raises an ArgumentError if value is not an array' do
169
+ expect { subject.set_trunk_groups('1', value: 'foo') }
170
+ .to raise_error(ArgumentError)
171
+ end
172
+
173
+ it 'sets trunk group to foo bar bang' do
174
+ node.config(['vlan 1', 'trunk group bang', 'trunk group baz'])
175
+ expect(subject.get('1')[:trunk_groups]).to eq(%w(bang baz))
176
+ expect(subject.set_trunk_groups('1', value: %w(foo bar bang)))
177
+ .to be_truthy
178
+ expect(subject.get('1')[:trunk_groups].sort).to eq(%w(bang bar foo))
179
+ end
180
+
181
+ it 'clears trunk group if no value specified' do
182
+ node.config(['vlan 1', 'trunk group bang', 'trunk group baz'])
183
+ expect(subject.get('1')[:trunk_groups]).to eq(%w(bang baz))
184
+ expect(subject.set_trunk_groups('1')).to be_truthy
185
+ expect(subject.get('1')[:trunk_groups]).to be_empty
186
+ end
187
+
188
+ it 'negate trunk group' do
189
+ node.config(['vlan 1', 'trunk group bang', 'trunk group baz'])
190
+ expect(subject.get('1')[:trunk_groups]).to eq(%w(bang baz))
191
+ expect(subject.set_trunk_groups('1', value: %w(foo bar bang)))
192
+ .to be_truthy
193
+ expect(subject.get('1')[:trunk_groups].sort).to eq(%w(bang bar foo))
194
+ expect(subject.set_trunk_groups('1', enable: false)).to be_truthy
195
+ expect(subject.get('1')[:trunk_groups]).to be_empty
196
+ end
197
+
198
+ it 'default trunk group' do
199
+ node.config(['vlan 1', 'trunk group bang', 'trunk group baz'])
200
+ expect(subject.get('1')[:trunk_groups]).to eq(%w(bang baz))
201
+ expect(subject.set_trunk_groups('1', value: %w(foo bar bang)))
202
+ .to be_truthy
203
+ expect(subject.get('1')[:trunk_groups].sort).to eq(%w(bang bar foo))
204
+ expect(subject.set_trunk_groups('1', default: true)).to be_truthy
205
+ expect(subject.get('1')[:trunk_groups]).to be_empty
206
+ end
207
+ end
162
208
  end
@@ -23,7 +23,7 @@ describe Rbeapi::Api::BaseInterface do
23
23
  let(:resource) { subject.get('Loopback0') }
24
24
 
25
25
  let(:keys) do
26
- [:type, :shutdown, :description, :name]
26
+ [:type, :shutdown, :load_interval, :description, :name]
27
27
  end
28
28
 
29
29
  it 'returns an ethernet resource as a hash' do
@@ -24,7 +24,7 @@ describe Rbeapi::Api::EthernetInterface do
24
24
 
25
25
  let(:keys) do
26
26
  [:type, :speed, :sflow, :flowcontrol_send, :flowcontrol_receive,
27
- :forced, :shutdown, :description, :name]
27
+ :shutdown, :description, :name, :load_interval, :lacp_priority]
28
28
  end
29
29
 
30
30
  it 'returns an ethernet resource as a hash' do
@@ -31,8 +31,8 @@ describe Rbeapi::Api::PortchannelInterface do
31
31
  let(:resource) { subject.get('Port-Channel1') }
32
32
 
33
33
  let(:keys) do
34
- [:type, :shutdown, :description, :name, :members, :lacp_mode,
35
- :minimum_links, :lacp_timeout, :lacp_fallback]
34
+ [:type, :shutdown, :load_interval, :description, :name, :members,
35
+ :lacp_mode, :minimum_links, :lacp_timeout, :lacp_fallback]
36
36
  end
37
37
 
38
38
  it 'returns an ethernet resource as a hash' do
@@ -141,4 +141,11 @@ describe Rbeapi::Api::PortchannelInterface do
141
141
  expect(subject.set_shutdown('Port-Channel1', opts)).to be_truthy
142
142
  end
143
143
  end
144
+
145
+ describe '#set_members' do
146
+ it 'raises an ArgumentError if members is not an array' do
147
+ expect { subject.set_members('Port-Channel1', 'Ethernet3') }.to \
148
+ raise_error(ArgumentError)
149
+ end
150
+ end
144
151
  end