1 # Copyright (c) 2015 Red Hat, Inc.
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
20 from neutron.agent.common import ovs_lib
21 from neutron.agent.linux import ip_lib
22 from neutron.tests import base as tests_base
23 from neutron.tests.common import net_helpers
24 from neutron.tests.functional.agent.linux import base
27 class OVSBridgeTestBase(base.BaseOVSLinuxTestCase):
28 # TODO(twilson) So far, only ovsdb-related tests are written. It would be
29 # good to also add the openflow-related functions
31 super(OVSBridgeTestBase, self).setUp()
32 self.ovs = ovs_lib.BaseOVS()
33 self.br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
35 def create_ovs_port(self, *interface_attrs):
36 # Convert ((a, b), (c, d)) to {a: b, c: d} and add 'type' by default
37 attrs = collections.OrderedDict(interface_attrs)
38 attrs.setdefault('type', 'internal')
39 port_name = tests_base.get_rand_device_name(net_helpers.PORT_PREFIX)
40 return (port_name, self.br.add_port(port_name, *attrs.items()))
42 def create_ovs_vif_port(self, iface_id=None, mac=None,
43 iface_field='iface-id'):
45 iface_id = base.get_rand_name()
47 mac = base.get_rand_name()
48 attrs = ('external_ids', {iface_field: iface_id, 'attached-mac': mac})
49 port_name, ofport = self.create_ovs_port(attrs)
50 return ovs_lib.VifPort(port_name, ofport, iface_id, mac, self.br)
53 class OVSBridgeTestCase(OVSBridgeTestBase):
55 def test_port_lifecycle(self):
56 (port_name, ofport) = self.create_ovs_port(('type', 'internal'))
57 # ofport should always be an integer string with value -1 or > 0.
58 self.assertTrue(int(ofport))
59 self.assertTrue(int(self.br.get_port_ofport(port_name)))
60 self.assertTrue(self.br.port_exists(port_name))
61 self.assertEqual(self.br.br_name,
62 self.br.get_bridge_for_iface(port_name))
63 self.br.delete_port(port_name)
64 self.assertFalse(self.br.port_exists(port_name))
66 def test_duplicate_port_may_exist_false(self):
67 port_name, ofport = self.create_ovs_port(('type', 'internal'))
68 cmd = self.br.ovsdb.add_port(self.br.br_name,
69 port_name, may_exist=False)
70 self.assertRaises(RuntimeError, cmd.execute, check_error=True)
72 def test_delete_port_if_exists_false(self):
73 cmd = self.br.ovsdb.del_port('nonexistantport', if_exists=False)
74 self.assertRaises(RuntimeError, cmd.execute, check_error=True)
76 def test_replace_port(self):
77 port_name = tests_base.get_rand_device_name(net_helpers.PORT_PREFIX)
78 self.br.replace_port(port_name, ('type', 'internal'))
79 self.assertTrue(self.br.port_exists(port_name))
80 self.assertEqual('internal',
81 self.br.db_get_val('Interface', port_name, 'type'))
82 self.br.replace_port(port_name, ('type', 'internal'),
83 ('external_ids', {'test': 'test'}))
84 self.assertTrue(self.br.port_exists(port_name))
85 self.assertEqual('test', self.br.db_get_val('Interface', port_name,
86 'external_ids')['test'])
88 def test_attribute_lifecycle(self):
89 (port_name, ofport) = self.create_ovs_port()
91 self.ovs.set_db_attribute('Port', port_name, 'tag', tag)
92 self.assertEqual(tag, self.ovs.db_get_val('Port', port_name, 'tag'))
93 self.assertEqual(tag, self.br.get_port_tag_dict()[port_name])
94 self.ovs.clear_db_attribute('Port', port_name, 'tag')
95 self.assertEqual([], self.ovs.db_get_val('Port', port_name, 'tag'))
96 self.assertEqual([], self.br.get_port_tag_dict()[port_name])
98 def test_get_bridge_external_bridge_id(self):
99 self.ovs.set_db_attribute('Bridge', self.br.br_name,
101 {'bridge-id': self.br.br_name})
104 self.ovs.get_bridge_external_bridge_id(self.br.br_name))
106 def test_controller_lifecycle(self):
107 controllers = {'tcp:127.0.0.1:6633', 'tcp:172.17.16.10:55'}
108 self.br.set_controller(controllers)
109 self.assertSetEqual(controllers, set(self.br.get_controller()))
110 self.br.del_controller()
111 self.assertEqual([], self.br.get_controller())
113 def test_non_index_queries(self):
114 controllers = ['tcp:127.0.0.1:6633']
115 self.br.set_controller(controllers)
116 cmd = self.br.ovsdb.db_set('Controller', self.br.br_name,
117 ('connection_mode', 'out-of-band'))
118 cmd.execute(check_error=True)
119 self.assertEqual('out-of-band',
120 self.br.db_get_val('Controller', self.br.br_name,
123 def test_set_fail_mode_secure(self):
124 self.br.set_secure_mode()
125 self._assert_br_fail_mode(ovs_lib.FAILMODE_SECURE)
127 def test_set_fail_mode_standalone(self):
128 self.br.set_standalone_mode()
129 self._assert_br_fail_mode(ovs_lib.FAILMODE_STANDALONE)
131 def _assert_br_fail_mode(self, fail_mode):
133 self.br.db_get_val('Bridge', self.br.br_name, 'fail_mode'),
136 def test_set_protocols(self):
137 self.br.set_protocols('OpenFlow10')
139 self.br.db_get_val('Bridge', self.br.br_name, 'protocols'),
142 def test_get_datapath_id(self):
143 brdev = ip_lib.IPDevice(self.br.br_name)
144 dpid = brdev.link.attributes['link/ether'].replace(':', '')
145 self.br.set_db_attribute('Bridge',
146 self.br.br_name, 'datapath_id', dpid)
147 self.assertIn(dpid, self.br.get_datapath_id())
149 def test_add_tunnel_port(self):
151 'remote_ip': '192.0.2.1', # RFC 5737 TEST-NET-1
152 'local_ip': '198.51.100.1', # RFC 5737 TEST-NET-2
154 port_name = tests_base.get_rand_device_name(net_helpers.PORT_PREFIX)
155 self.br.add_tunnel_port(port_name, attrs['remote_ip'],
157 self.assertEqual(self.ovs.db_get_val('Interface', port_name, 'type'),
159 options = self.ovs.db_get_val('Interface', port_name, 'options')
160 for attr, val in attrs.items():
161 self.assertEqual(val, options[attr])
163 def test_add_patch_port(self):
164 local = tests_base.get_rand_device_name(net_helpers.PORT_PREFIX)
166 self.br.add_patch_port(local, peer)
167 self.assertEqual(self.ovs.db_get_val('Interface', local, 'type'),
169 options = self.ovs.db_get_val('Interface', local, 'options')
170 self.assertEqual(peer, options['peer'])
172 def test_get_port_name_list(self):
173 # Note that ovs-vsctl's list-ports does not include the port created
174 # with the same name as the bridge
175 ports = {self.create_ovs_port()[0] for i in range(5)}
176 self.assertSetEqual(ports, set(self.br.get_port_name_list()))
178 def test_get_iface_name_list(self):
179 ifaces = {self.create_ovs_port()[0] for i in range(5)}
180 self.assertSetEqual(ifaces, set(self.br.get_iface_name_list()))
182 def test_get_port_stats(self):
183 # Nothing seems to use this function?
184 (port_name, ofport) = self.create_ovs_port()
185 stats = set(self.br.get_port_stats(port_name).keys())
186 self.assertTrue(set(['rx_packets', 'tx_packets']).issubset(stats))
188 def test_get_vif_ports(self):
190 self.create_ovs_port()
191 vif_ports = [self.create_ovs_vif_port() for i in range(3)]
192 ports = self.br.get_vif_ports()
193 self.assertEqual(3, len(ports))
194 self.assertTrue(all([isinstance(x, ovs_lib.VifPort) for x in ports]))
195 self.assertEqual(sorted([x.port_name for x in vif_ports]),
196 sorted([x.port_name for x in ports]))
198 def test_get_vif_ports_with_bond(self):
200 self.create_ovs_port()
201 vif_ports = [self.create_ovs_vif_port() for i in range(3)]
202 # bond ports don't have records in the Interface table but they do in
204 orig = self.br.get_port_name_list
205 new_port_name_list = lambda: orig() + ['bondport']
206 mock.patch.object(self.br, 'get_port_name_list',
207 new=new_port_name_list).start()
208 ports = self.br.get_vif_ports()
209 self.assertEqual(3, len(ports))
210 self.assertTrue(all([isinstance(x, ovs_lib.VifPort) for x in ports]))
211 self.assertEqual(sorted([x.port_name for x in vif_ports]),
212 sorted([x.port_name for x in ports]))
214 def test_get_vif_port_set(self):
216 self.create_ovs_port()
217 vif_ports = [self.create_ovs_vif_port() for i in range(2)]
218 ports = self.br.get_vif_port_set()
219 expected = set([x.vif_id for x in vif_ports])
220 self.assertEqual(expected, ports)
222 def test_get_vif_port_set_with_missing_port(self):
223 self.create_ovs_port()
224 vif_ports = [self.create_ovs_vif_port()]
226 # return an extra port to make sure the db list ignores it
227 orig = self.br.get_port_name_list
228 new_port_name_list = lambda: orig() + ['anotherport']
229 mock.patch.object(self.br, 'get_port_name_list',
230 new=new_port_name_list).start()
231 ports = self.br.get_vif_port_set()
232 expected = set([vif_ports[0].vif_id])
233 self.assertEqual(expected, ports)
235 def test_get_vif_port_set_on_empty_bridge_returns_empty_set(self):
236 # Create a port on self.br
237 self.create_ovs_vif_port()
239 # Create another, empty bridge
240 br_2 = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
242 # Assert that get_vif_port_set on an empty bridge returns an empty set,
243 # and does not return the other bridge's ports.
244 self.assertEqual(set(), br_2.get_vif_port_set())
246 def test_get_ports_attributes(self):
247 port_names = [self.create_ovs_port()[0], self.create_ovs_port()[0]]
248 db_ports = self.br.get_ports_attributes('Interface', columns=['name'])
249 db_ports_names = [p['name'] for p in db_ports]
250 self.assertEqual(sorted(port_names), sorted(db_ports_names))
252 def test_get_port_tag_dict(self):
253 # Simple case tested in port test_set_get_clear_db_val
256 def test_get_vif_port_by_id(self):
258 self.create_ovs_port()
259 vif_ports = [self.create_ovs_vif_port() for i in range(3)]
260 for vif in vif_ports:
261 self.assertEqual(self.br.get_vif_port_by_id(vif.vif_id).vif_id,
264 def test_get_vifs_by_ids(self):
266 self.create_ovs_port()
267 vif_ports = [self.create_ovs_vif_port() for i in range(3)]
268 by_id = self.br.get_vifs_by_ids([v.vif_id for v in vif_ports])
269 # convert to str for comparison of VifPorts
270 by_id = {vid: str(vport) for vid, vport in by_id.items()}
271 self.assertEqual({v.vif_id: str(v) for v in vif_ports}, by_id)
273 def test_delete_ports(self):
274 # TODO(twilson) I intensely dislike the current delete_ports function
275 # as the default behavior is really delete_vif_ports(), then it acts
276 # more like a delete_ports() seems like it should if all_ports=True is
278 # Create 2 non-vif ports and 2 vif ports
279 nonvifs = {self.create_ovs_port()[0] for i in range(2)}
280 vifs = {self.create_ovs_vif_port().port_name for i in range(2)}
281 self.assertSetEqual(nonvifs.union(vifs),
282 set(self.br.get_port_name_list()))
283 self.br.delete_ports()
284 self.assertSetEqual(nonvifs, set(self.br.get_port_name_list()))
285 self.br.delete_ports(all_ports=True)
286 self.assertEqual(len(self.br.get_port_name_list()), 0)
288 def test_set_controller_connection_mode(self):
289 controllers = ['tcp:192.0.2.0:6633']
290 self._set_controllers_connection_mode(controllers)
292 def test_set_multi_controllers_connection_mode(self):
293 controllers = ['tcp:192.0.2.0:6633', 'tcp:192.0.2.1:55']
294 self._set_controllers_connection_mode(controllers)
296 def _set_controllers_connection_mode(self, controllers):
297 self.br.set_controller(controllers)
298 self.assertEqual(sorted(controllers), sorted(self.br.get_controller()))
299 self.br.set_controllers_connection_mode('out-of-band')
300 self._assert_controllers_connection_mode('out-of-band')
301 self.br.del_controller()
302 self.assertEqual([], self.br.get_controller())
304 def _assert_controllers_connection_mode(self, connection_mode):
305 controllers = self.br.db_get_val('Bridge', self.br.br_name,
307 controllers = [controllers] if isinstance(
308 controllers, uuid.UUID) else controllers
309 for controller in controllers:
310 self.assertEqual(connection_mode,
311 self.br.db_get_val('Controller',
315 def test_egress_bw_limit(self):
316 port_name, _ = self.create_ovs_port()
317 self.br.create_egress_bw_limit_for_port(port_name, 700, 70)
318 max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name)
319 self.assertEqual(700, max_rate)
320 self.assertEqual(70, burst)
321 self.br.delete_egress_bw_limit_for_port(port_name)
322 max_rate, burst = self.br.get_egress_bw_limit_for_port(port_name)
323 self.assertIsNone(max_rate)
324 self.assertIsNone(burst)
327 class OVSLibTestCase(base.BaseOVSLinuxTestCase):
330 super(OVSLibTestCase, self).setUp()
331 self.ovs = ovs_lib.BaseOVS()
333 def test_bridge_lifecycle_baseovs(self):
334 name = base.get_rand_name(prefix=net_helpers.BR_PREFIX)
335 self.addCleanup(self.ovs.delete_bridge, name)
336 br = self.ovs.add_bridge(name)
337 self.assertEqual(br.br_name, name)
338 self.assertTrue(self.ovs.bridge_exists(name))
339 self.ovs.delete_bridge(name)
340 self.assertFalse(self.ovs.bridge_exists(name))
342 def test_get_bridges(self):
344 self.useFixture(net_helpers.OVSBridgeFixture()).bridge.br_name
346 self.assertTrue(set(self.ovs.get_bridges()).issuperset(bridges))
348 def test_bridge_lifecycle_ovsbridge(self):
349 name = base.get_rand_name(prefix=net_helpers.BR_PREFIX)
350 br = ovs_lib.OVSBridge(name)
351 self.assertEqual(br.br_name, name)
352 # Make sure that instantiating an OVSBridge does not actually create
353 self.assertFalse(self.ovs.bridge_exists(name))
354 self.addCleanup(self.ovs.delete_bridge, name)
356 self.assertTrue(self.ovs.bridge_exists(name))
358 self.assertFalse(self.ovs.bridge_exists(name))