~ubuntu-branches/ubuntu/vivid/neutron/vivid-updates

« back to all changes in this revision

Viewing changes to neutron/tests/unit/nec/test_nec_agent.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2015-03-30 11:17:19 UTC
  • mfrom: (1.1.21)
  • Revision ID: package-import@ubuntu.com-20150330111719-h0gx7233p4jkkgfh
Tags: 1:2015.1~b3-0ubuntu1
* New upstream milestone release:
  - d/control: Align version requirements with upstream.
  - d/control: Add new dependency on oslo-log.
  - d/p/*: Rebase.
  - d/control,d/neutron-plugin-hyperv*: Dropped, decomposed into
    separate project upstream.
  - d/control,d/neutron-plugin-openflow*: Dropped, decomposed into
    separate project upstream.
  - d/neutron-common.install: Add neutron-rootwrap-daemon and 
    neutron-keepalived-state-change binaries.
  - d/rules: Ignore neutron-hyperv-agent when installing; only for Windows.
  - d/neutron-plugin-cisco.install: Drop neutron-cisco-cfg-agent as
    decomposed into separate project upstream.
  - d/neutron-plugin-vmware.install: Drop neutron-check-nsx-config and
    neutron-nsx-manage as decomposed into separate project upstream.
  - d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent.
* d/pydist-overrides: Add overrides for oslo packages.
* d/control: Fixup type in package description (LP: #1263539).
* d/p/fixup-driver-test-execution.patch: Cherry pick fix from upstream VCS
  to support unit test exection in out-of-tree vendor drivers.
* d/neutron-common.postinst: Allow general access to /etc/neutron but limit
  access to root/neutron to /etc/neutron/neutron.conf to support execution
  of unit tests in decomposed vendor drivers.
* d/control: Add dependency on python-neutron-fwaas to neutron-l3-agent
  package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2013 NEC Corporation.  All rights reserved.
2
 
#
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
6
 
#
7
 
#         http://www.apache.org/licenses/LICENSE-2.0
8
 
#
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
13
 
#    under the License.
14
 
 
15
 
import contextlib
16
 
import copy
17
 
import itertools
18
 
import time
19
 
 
20
 
import mock
21
 
from oslo.config import cfg
22
 
from six import moves
23
 
import testtools
24
 
 
25
 
from neutron.agent.linux import ovs_lib
26
 
from neutron.extensions import securitygroup as ext_sg
27
 
from neutron.plugins.nec.agent import nec_neutron_agent
28
 
from neutron.tests import base
29
 
 
30
 
DAEMON_LOOP_COUNT = 10
31
 
OVS_DPID = '00000629355b6943'
32
 
OVS_DPID_0X = '0x' + OVS_DPID
33
 
 
34
 
 
35
 
class TestNecAgentBase(base.BaseTestCase):
36
 
 
37
 
    def setUp(self):
38
 
        super(TestNecAgentBase, self).setUp()
39
 
        cfg.CONF.set_default('firewall_driver',
40
 
                             'neutron.agent.firewall.NoopFirewallDriver',
41
 
                             group='SECURITYGROUP')
42
 
        cfg.CONF.set_override('host', 'dummy-host')
43
 
        with contextlib.nested(
44
 
            mock.patch.object(ovs_lib.OVSBridge, 'get_datapath_id',
45
 
                              return_value=OVS_DPID),
46
 
            mock.patch('socket.gethostname', return_value='dummy-host'),
47
 
            mock.patch('neutron.openstack.common.loopingcall.'
48
 
                       'FixedIntervalLoopingCall'),
49
 
            mock.patch('neutron.agent.rpc.PluginReportStateAPI')
50
 
        ) as (get_datapath_id, gethostname,
51
 
              loopingcall, state_rpc_api):
52
 
            kwargs = {'integ_br': 'integ_br',
53
 
                      'root_helper': 'dummy_wrapper',
54
 
                      'polling_interval': 1}
55
 
            self.agent = nec_neutron_agent.NECNeutronAgent(**kwargs)
56
 
            self.loopingcall = loopingcall
57
 
            self.state_rpc_api = state_rpc_api
58
 
 
59
 
 
60
 
class TestNecAgent(TestNecAgentBase):
61
 
 
62
 
    def _setup_mock(self):
63
 
        vif_ports = [ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
64
 
                                     self.agent.int_br),
65
 
                     ovs_lib.VifPort('port2', '2', 'id-2', 'mac-2',
66
 
                                     self.agent.int_br)]
67
 
        self.get_vif_ports = mock.patch.object(
68
 
            ovs_lib.OVSBridge, 'get_vif_ports',
69
 
            return_value=vif_ports).start()
70
 
        self.update_ports = mock.patch.object(
71
 
            nec_neutron_agent.NECPluginApi, 'update_ports').start()
72
 
        self.prepare_devices_filter = mock.patch.object(
73
 
            self.agent.sg_agent, 'prepare_devices_filter').start()
74
 
        self.remove_devices_filter = mock.patch.object(
75
 
            self.agent.sg_agent, 'remove_devices_filter').start()
76
 
 
77
 
    def _test_single_loop(self, with_exc=False, need_sync=False):
78
 
        self.agent.cur_ports = ['id-0', 'id-1']
79
 
        self.agent.need_sync = need_sync
80
 
 
81
 
        self.agent.loop_handler()
82
 
        if with_exc:
83
 
            self.assertEqual(self.agent.cur_ports, ['id-0', 'id-1'])
84
 
            self.assertTrue(self.agent.need_sync)
85
 
        else:
86
 
            self.assertEqual(self.agent.cur_ports, ['id-1', 'id-2'])
87
 
            self.assertFalse(self.agent.need_sync)
88
 
 
89
 
    def test_single_loop_normal(self):
90
 
        self._setup_mock()
91
 
        self._test_single_loop()
92
 
        agent_id = 'nec-q-agent.dummy-host'
93
 
        self.update_ports.assert_called_once_with(
94
 
            mock.ANY, agent_id, OVS_DPID_0X,
95
 
            [{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
96
 
            ['id-0'])
97
 
        self.prepare_devices_filter.assert_called_once_with(['id-2'])
98
 
        self.remove_devices_filter.assert_called_once_with(['id-0'])
99
 
 
100
 
    def test_single_loop_need_sync(self):
101
 
        self._setup_mock()
102
 
        self._test_single_loop(need_sync=True)
103
 
        agent_id = 'nec-q-agent.dummy-host'
104
 
        self.update_ports.assert_called_once_with(
105
 
            mock.ANY, agent_id, OVS_DPID_0X,
106
 
            [{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'},
107
 
             {'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
108
 
            [])
109
 
        self.prepare_devices_filter.assert_called_once_with(['id-1', 'id-2'])
110
 
        self.assertFalse(self.remove_devices_filter.call_count)
111
 
 
112
 
    def test_single_loop_with_sg_exception_remove(self):
113
 
        self._setup_mock()
114
 
        self.update_ports.side_effect = Exception()
115
 
        self._test_single_loop(with_exc=True)
116
 
 
117
 
    def test_single_loop_with_sg_exception_prepare(self):
118
 
        self._setup_mock()
119
 
        self.prepare_devices_filter.side_effect = Exception()
120
 
        self._test_single_loop(with_exc=True)
121
 
 
122
 
    def test_single_loop_with_update_ports_exception(self):
123
 
        self._setup_mock()
124
 
        self.remove_devices_filter.side_effect = Exception()
125
 
        self._test_single_loop(with_exc=True)
126
 
 
127
 
    def test_daemon_loop(self):
128
 
 
129
 
        def state_check(index):
130
 
            self.assertEqual(len(self.vif_ports_scenario[index]),
131
 
                             len(self.agent.cur_ports))
132
 
 
133
 
        # Fake time.sleep to stop the infinite loop in daemon_loop()
134
 
        self.sleep_count = 0
135
 
 
136
 
        def sleep_mock(*args, **kwargs):
137
 
            state_check(self.sleep_count)
138
 
            self.sleep_count += 1
139
 
            if self.sleep_count >= DAEMON_LOOP_COUNT:
140
 
                raise RuntimeError()
141
 
 
142
 
        vif_ports = [ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
143
 
                                     self.agent.int_br),
144
 
                     ovs_lib.VifPort('port2', '2', 'id-2', 'mac-2',
145
 
                                     self.agent.int_br)]
146
 
 
147
 
        self.vif_ports_scenario = [[], [], vif_ports[0:1], vif_ports[0:2],
148
 
                                   vif_ports[1:2], []]
149
 
 
150
 
        # Ensure vif_ports_scenario is longer than DAEMON_LOOP_COUNT
151
 
        if len(self.vif_ports_scenario) < DAEMON_LOOP_COUNT:
152
 
            self.vif_ports_scenario.extend(
153
 
                [] for _i in moves.xrange(DAEMON_LOOP_COUNT -
154
 
                                          len(self.vif_ports_scenario)))
155
 
 
156
 
        with contextlib.nested(
157
 
            mock.patch.object(time, 'sleep', side_effect=sleep_mock),
158
 
            mock.patch.object(ovs_lib.OVSBridge, 'get_vif_ports'),
159
 
            mock.patch.object(nec_neutron_agent.NECPluginApi, 'update_ports'),
160
 
            mock.patch.object(self.agent.sg_agent, 'prepare_devices_filter'),
161
 
            mock.patch.object(self.agent.sg_agent, 'remove_devices_filter')
162
 
        ) as (sleep, get_vif_potrs, update_ports,
163
 
              prepare_devices_filter, remove_devices_filter):
164
 
            get_vif_potrs.side_effect = self.vif_ports_scenario
165
 
 
166
 
            with testtools.ExpectedException(RuntimeError):
167
 
                self.agent.daemon_loop()
168
 
            self.assertEqual(update_ports.call_count, 4)
169
 
            self.assertEqual(sleep.call_count, DAEMON_LOOP_COUNT)
170
 
 
171
 
            agent_id = 'nec-q-agent.dummy-host'
172
 
            expected = [
173
 
                mock.call(mock.ANY, agent_id, OVS_DPID_0X,
174
 
                          [{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'}],
175
 
                          []),
176
 
                mock.call(mock.ANY, agent_id, OVS_DPID_0X,
177
 
                          [{'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}],
178
 
                          []),
179
 
                mock.call(mock.ANY, agent_id, OVS_DPID_0X,
180
 
                          [], ['id-1']),
181
 
                mock.call(mock.ANY, agent_id, OVS_DPID_0X,
182
 
                          [], ['id-2'])
183
 
            ]
184
 
            update_ports.assert_has_calls(expected)
185
 
 
186
 
            expected = [mock.call(['id-1']),
187
 
                        mock.call(['id-2'])]
188
 
            self.assertEqual(prepare_devices_filter.call_count, 2)
189
 
            prepare_devices_filter.assert_has_calls(expected)
190
 
            self.assertEqual(remove_devices_filter.call_count, 2)
191
 
            remove_devices_filter.assert_has_calls(expected)
192
 
 
193
 
            sleep.assert_called_with(self.agent.polling_interval)
194
 
 
195
 
    def test_report_state_installed(self):
196
 
        self.loopingcall.assert_called_once_with(self.agent._report_state)
197
 
        instance = self.loopingcall.return_value
198
 
        self.assertTrue(instance.start.called)
199
 
 
200
 
    def _check_report_state(self, cur_ports, num_ports, fail_mode,
201
 
                            first=False):
202
 
        self.assertEqual(first or fail_mode,
203
 
                         'start_flag' in self.agent.agent_state)
204
 
        self.agent.cur_ports = cur_ports
205
 
 
206
 
        self.agent._report_state()
207
 
 
208
 
        self.assertEqual(fail_mode,
209
 
                         'start_flag' in self.agent.agent_state)
210
 
        self.assertEqual(self.agent.
211
 
                         agent_state['configurations']['devices'],
212
 
                         num_ports)
213
 
        self.num_ports_hist.append(num_ports)
214
 
 
215
 
    def _test_report_state(self, fail_mode):
216
 
        log_mocked = mock.patch.object(nec_neutron_agent, 'LOG')
217
 
        log_patched = log_mocked.start()
218
 
 
219
 
        def record_state(*args, **kwargs):
220
 
            self.record_calls.append(copy.deepcopy(args))
221
 
            if fail_mode:
222
 
                raise Exception()
223
 
 
224
 
        self.record_calls = []
225
 
        self.num_ports_hist = []
226
 
        state_rpc = self.state_rpc_api.return_value
227
 
        state_rpc.report_state.side_effect = record_state
228
 
        dummy_vif = ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1', None)
229
 
 
230
 
        self.state_rpc_api.assert_called_once_with('q-plugin')
231
 
        self.assertIn('start_flag', self.agent.agent_state)
232
 
 
233
 
        self._check_report_state([], 0, fail_mode, first=True)
234
 
        self._check_report_state([dummy_vif] * 2, 2, fail_mode)
235
 
        self._check_report_state([dummy_vif] * 5, 5, fail_mode)
236
 
        self._check_report_state([], 0, fail_mode)
237
 
 
238
 
        # Since loopingcall start is mocked, call_count is same as
239
 
        # the call count of check_report_state.
240
 
        self.assertEqual(state_rpc.report_state.call_count, 4)
241
 
        self.assertEqual(len(self.record_calls), 4)
242
 
 
243
 
        for i, x in enumerate(itertools.izip(self.record_calls,
244
 
                                             self.num_ports_hist)):
245
 
            rec, num_ports = x
246
 
            expected_state = {
247
 
                'binary': 'neutron-nec-agent',
248
 
                'host': 'dummy-host',
249
 
                'topic': 'N/A',
250
 
                'configurations': {'devices': 0},
251
 
                'agent_type': 'NEC plugin agent'}
252
 
            expected_state['configurations']['devices'] = num_ports
253
 
            if i == 0 or fail_mode:
254
 
                expected_state['start_flag'] = True
255
 
            self.assertEqual(expected_state, rec[1])
256
 
 
257
 
        self.assertEqual(fail_mode, log_patched.exception.called)
258
 
 
259
 
    def test_report_state(self):
260
 
        self._test_report_state(fail_mode=False)
261
 
 
262
 
    def test_report_state_fail(self):
263
 
        self._test_report_state(fail_mode=True)
264
 
 
265
 
 
266
 
class TestNecAgentCallback(TestNecAgentBase):
267
 
 
268
 
    def test_port_update(self):
269
 
        with contextlib.nested(
270
 
            mock.patch.object(ovs_lib.OVSBridge, 'get_vif_port_by_id'),
271
 
            mock.patch.object(self.agent.sg_agent, 'refresh_firewall')
272
 
        ) as (get_vif_port_by_id, refresh_firewall):
273
 
            context = mock.Mock()
274
 
            vifport = ovs_lib.VifPort('port1', '1', 'id-1', 'mac-1',
275
 
                                      self.agent.int_br)
276
 
 
277
 
            # The OVS port does not exist.
278
 
            get_vif_port_by_id.return_value = None
279
 
            port = {'id': 'update-port-1'}
280
 
            self.agent.callback_nec.port_update(context, port=port)
281
 
            self.assertEqual(get_vif_port_by_id.call_count, 1)
282
 
            self.assertFalse(refresh_firewall.call_count)
283
 
 
284
 
            # The OVS port exists but no security group is associated.
285
 
            get_vif_port_by_id.return_value = vifport
286
 
            port = {'id': 'update-port-1'}
287
 
            self.agent.callback_nec.port_update(context, port=port)
288
 
            self.assertEqual(get_vif_port_by_id.call_count, 2)
289
 
            self.assertFalse(refresh_firewall.call_count)
290
 
 
291
 
            # The OVS port exists but a security group is associated.
292
 
            get_vif_port_by_id.return_value = vifport
293
 
            port = {'id': 'update-port-1',
294
 
                    ext_sg.SECURITYGROUPS: ['default']}
295
 
            self.agent.callback_nec.port_update(context, port=port)
296
 
            self.assertEqual(get_vif_port_by_id.call_count, 3)
297
 
            self.assertEqual(refresh_firewall.call_count, 1)
298
 
 
299
 
            get_vif_port_by_id.return_value = None
300
 
            port = {'id': 'update-port-1',
301
 
                    ext_sg.SECURITYGROUPS: ['default']}
302
 
            self.agent.callback_nec.port_update(context, port=port)
303
 
            self.assertEqual(get_vif_port_by_id.call_count, 4)
304
 
            self.assertEqual(refresh_firewall.call_count, 1)
305
 
 
306
 
 
307
 
class TestNecAgentPluginApi(TestNecAgentBase):
308
 
 
309
 
    def test_plugin_api(self):
310
 
        with contextlib.nested(
311
 
            mock.patch.object(self.agent.plugin_rpc.client, 'prepare'),
312
 
            mock.patch.object(self.agent.plugin_rpc.client, 'call'),
313
 
        ) as (mock_prepare, mock_call):
314
 
            mock_prepare.return_value = self.agent.plugin_rpc.client
315
 
 
316
 
            agent_id = 'nec-q-agent.dummy-host'
317
 
            port_added = [{'id': 'id-1', 'mac': 'mac-1', 'port_no': '1'},
318
 
                          {'id': 'id-2', 'mac': 'mac-2', 'port_no': '2'}]
319
 
            port_removed = ['id-3', 'id-4', 'id-5']
320
 
 
321
 
            self.agent.plugin_rpc.update_ports(
322
 
                mock.sentinel.ctx, agent_id, OVS_DPID_0X,
323
 
                port_added, port_removed)
324
 
 
325
 
            mock_call.assert_called_once_with(
326
 
                    mock.sentinel.ctx, 'update_ports',
327
 
                    agent_id=agent_id, datapath_id=OVS_DPID_0X,
328
 
                    port_added=port_added, port_removed=port_removed)
329
 
 
330
 
 
331
 
class TestNecAgentMain(base.BaseTestCase):
332
 
    def test_main(self):
333
 
        with contextlib.nested(
334
 
            mock.patch.object(nec_neutron_agent, 'NECNeutronAgent'),
335
 
            mock.patch.object(nec_neutron_agent, 'common_config'),
336
 
            mock.patch.object(nec_neutron_agent, 'config')
337
 
        ) as (agent, common_config, cfg):
338
 
            cfg.OVS.integration_bridge = 'br-int-x'
339
 
            cfg.AGENT.root_helper = 'dummy-helper'
340
 
            cfg.AGENT.polling_interval = 10
341
 
 
342
 
            nec_neutron_agent.main()
343
 
 
344
 
            self.assertTrue(common_config.setup_logging.called)
345
 
            agent.assert_has_calls([
346
 
                mock.call('br-int-x', 'dummy-helper', 10),
347
 
                mock.call().daemon_loop()
348
 
            ])