1 # Copyright 2013 Red Hat, Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
16 Tests in this module will be skipped unless:
18 - ovsdb-client is installed
20 - ovsdb-client can be invoked password-less via the configured root helper
22 - sudo testing is enabled (see neutron.tests.functional.base for details)
25 from oslo_config import cfg
27 from neutron.agent.linux import ovsdb_monitor
28 from neutron.agent.linux import utils
29 from neutron.tests.common import net_helpers
30 from neutron.tests.functional.agent.linux import base as linux_base
31 from neutron.tests.functional import base as functional_base
34 class BaseMonitorTest(linux_base.BaseOVSLinuxTestCase):
37 super(BaseMonitorTest, self).setUp()
39 rootwrap_not_configured = (cfg.CONF.AGENT.root_helper ==
40 functional_base.SUDO_CMD)
41 if rootwrap_not_configured:
42 # The monitor tests require a nested invocation that has
43 # to be emulated by double sudo if rootwrap is not
45 self.config(group='AGENT',
46 root_helper=" ".join([functional_base.SUDO_CMD] * 2))
48 self._check_test_requirements()
49 # ovsdb-client monitor needs to have a bridge to make any output
50 self.useFixture(net_helpers.OVSBridgeFixture())
52 def _check_test_requirements(self):
53 self.check_command(['ovsdb-client', 'list-dbs'],
55 'password-less sudo not granted for ovsdb-client',
59 class TestOvsdbMonitor(BaseMonitorTest):
62 super(TestOvsdbMonitor, self).setUp()
64 self.monitor = ovsdb_monitor.OvsdbMonitor('Bridge')
65 self.addCleanup(self.monitor.stop)
68 def collect_monitor_output(self):
69 output = list(self.monitor.iter_stdout())
71 # Output[0] is header row with spaces for column separation.
72 # Use 'other_config' as an indication of the table header.
73 self.assertIn('other_config', output[0])
76 def test_monitor_generates_initial_output(self):
77 utils.wait_until_true(self.collect_monitor_output, timeout=30)
80 class TestSimpleInterfaceMonitor(BaseMonitorTest):
83 super(TestSimpleInterfaceMonitor, self).setUp()
85 self.monitor = ovsdb_monitor.SimpleInterfaceMonitor()
86 self.addCleanup(self.monitor.stop)
87 self.monitor.start(block=True, timeout=60)
89 def test_has_updates(self):
90 utils.wait_until_true(lambda: self.monitor.has_updates)
91 # clear the event list
92 self.monitor.get_events()
93 self.useFixture(net_helpers.OVSPortFixture())
94 # has_updates after port addition should become True
95 utils.wait_until_true(lambda: self.monitor.has_updates is True)
97 def _expected_devices_events(self, devices, state):
98 """Helper to check that events are received for expected devices.
100 :param devices: The list of expected devices. WARNING: This list
101 is modified by this method
102 :param state: The state of the devices (added or removed)
104 events = self.monitor.get_events()
106 (dev['name'], dev['external_ids']) for dev in events.get(state)]
107 for dev in event_devices:
108 if dev[0] in devices:
109 devices.remove(dev[0])
110 self.assertEqual(dev[1].get('iface-status'), 'active')
114 def test_get_events(self):
115 utils.wait_until_true(lambda: self.monitor.has_updates)
116 devices = self.monitor.get_events()
117 self.assertTrue(devices.get('added'),
118 'Initial call should always be true')
119 br = self.useFixture(net_helpers.OVSBridgeFixture())
120 p1 = self.useFixture(net_helpers.OVSPortFixture(br.bridge))
121 p2 = self.useFixture(net_helpers.OVSPortFixture(br.bridge))
122 added_devices = [p1.port.name, p2.port.name]
123 utils.wait_until_true(
124 lambda: self._expected_devices_events(added_devices, 'added'))
125 br.bridge.delete_port(p1.port.name)
126 br.bridge.delete_port(p2.port.name)
127 removed_devices = [p1.port.name, p2.port.name]
128 utils.wait_until_true(
129 lambda: self._expected_devices_events(removed_devices, 'removed'))
131 self.monitor.stop(block=True)
132 self.monitor.start(block=True, timeout=60)
133 devices = self.monitor.get_events()
134 self.assertTrue(devices.get('added'),
135 'Initial call should always be true')