# Copyright (c) 2014 OpenStack Foundation. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import mock from oslo_config import cfg import webob.exc from neutron import context from neutron.tests.unit.api import test_extensions from vmware_nsx.neutron.plugins.vmware.dbexts import qos_db from vmware_nsx.neutron.plugins.vmware.extensions import qos as ext_qos from vmware_nsx.neutron.plugins.vmware import nsxlib from vmware_nsx.tests.unit import vmware from vmware_nsx.tests.unit.vmware import test_nsx_plugin class QoSTestExtensionManager(object): def get_resources(self): return ext_qos.Qos.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class TestQoSQueue(test_nsx_plugin.NsxPluginV2TestCase): def setUp(self, plugin=None): cfg.CONF.set_override('api_extensions_path', vmware.NSXEXT_PATH) super(TestQoSQueue, self).setUp() ext_mgr = QoSTestExtensionManager() self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) def _create_qos_queue(self, fmt, body, **kwargs): qos_queue = self.new_create_request('qos-queues', body) if (kwargs.get('set_context') and 'tenant_id' in kwargs): # create a specific auth context for this request qos_queue.environ['neutron.context'] = context.Context( '', kwargs['tenant_id']) return qos_queue.get_response(self.ext_api) @contextlib.contextmanager def qos_queue(self, name='foo', min='0', max='10', qos_marking=None, dscp='0', default=None, do_delete=True): body = {'qos_queue': {'tenant_id': 'tenant', 'name': name, 'min': min, 'max': max}} if qos_marking: body['qos_queue']['qos_marking'] = qos_marking if dscp: body['qos_queue']['dscp'] = dscp if default: body['qos_queue']['default'] = default res = self._create_qos_queue('json', body) qos_queue = self.deserialize('json', res) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) yield qos_queue if do_delete: self._delete('qos-queues', qos_queue['qos_queue']['id']) def test_create_qos_queue(self): with self.qos_queue(name='fake_lqueue', min=34, max=44, qos_marking='untrusted', default=False) as q: self.assertEqual(q['qos_queue']['name'], 'fake_lqueue') self.assertEqual(q['qos_queue']['min'], 34) self.assertEqual(q['qos_queue']['max'], 44) self.assertEqual(q['qos_queue']['qos_marking'], 'untrusted') self.assertFalse(q['qos_queue']['default']) def test_create_trusted_qos_queue(self): with mock.patch.object(qos_db.LOG, 'info') as log: with mock.patch.object(nsxlib, 'do_request', return_value={"uuid": "fake_queue"}): with self.qos_queue(name='fake_lqueue', min=34, max=44, qos_marking='trusted', default=False) as q: self.assertIsNone(q['qos_queue']['dscp']) self.assertTrue(log.called) def test_create_qos_queue_name_exceeds_40_chars(self): name = 'this_is_a_queue_whose_name_is_longer_than_40_chars' with self.qos_queue(name=name) as queue: # Assert Neutron name is not truncated self.assertEqual(queue['qos_queue']['name'], name) def test_create_qos_queue_default(self): with self.qos_queue(default=True) as q: self.assertTrue(q['qos_queue']['default']) def test_create_qos_queue_two_default_queues_fail(self): with self.qos_queue(default=True): body = {'qos_queue': {'tenant_id': 'tenant', 'name': 'second_default_queue', 'default': True}} res = self._create_qos_queue('json', body) self.assertEqual(res.status_int, 409) def test_create_port_with_queue(self): with self.qos_queue(default=True) as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) self.assertEqual(net1['network'][ext_qos.QUEUE], q1['qos_queue']['id']) device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" with self.port(device_id=device_id) as p: self.assertEqual(len(p['port'][ext_qos.QUEUE]), 36) def test_create_shared_queue_networks(self): with self.qos_queue(default=True, do_delete=False) as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) self.assertEqual(net1['network'][ext_qos.QUEUE], q1['qos_queue']['id']) res = self._create_network('json', 'net2', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net2 = self.deserialize('json', res) self.assertEqual(net1['network'][ext_qos.QUEUE], q1['qos_queue']['id']) device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" res = self._create_port('json', net1['network']['id'], device_id=device_id) port1 = self.deserialize('json', res) res = self._create_port('json', net2['network']['id'], device_id=device_id) port2 = self.deserialize('json', res) self.assertEqual(port1['port'][ext_qos.QUEUE], port2['port'][ext_qos.QUEUE]) self._delete('ports', port1['port']['id']) self._delete('ports', port2['port']['id']) def test_remove_queue_in_use_fail(self): with self.qos_queue(do_delete=False) as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" res = self._create_port('json', net1['network']['id'], device_id=device_id) port = self.deserialize('json', res) self._delete('qos-queues', port['port'][ext_qos.QUEUE], 409) def test_update_network_new_queue(self): with self.qos_queue() as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) with self.qos_queue() as new_q: data = {'network': {ext_qos.QUEUE: new_q['qos_queue']['id']}} req = self.new_update_request('networks', data, net1['network']['id']) res = req.get_response(self.api) net1 = self.deserialize('json', res) self.assertEqual(net1['network'][ext_qos.QUEUE], new_q['qos_queue']['id']) def test_update_port_adding_device_id(self): with self.qos_queue(do_delete=False) as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" res = self._create_port('json', net1['network']['id']) port = self.deserialize('json', res) self.assertIsNone(port['port'][ext_qos.QUEUE]) data = {'port': {'device_id': device_id}} req = self.new_update_request('ports', data, port['port']['id']) res = req.get_response(self.api) port = self.deserialize('json', res) self.assertEqual(len(port['port'][ext_qos.QUEUE]), 36) def test_get_port_with_qos_not_admin(self): body = {'qos_queue': {'tenant_id': 'not_admin', 'name': 'foo', 'min': 20, 'max': 20}} res = self._create_qos_queue('json', body, tenant_id='not_admin') q1 = self.deserialize('json', res) res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE, 'tenant_id',), queue_id=q1['qos_queue']['id'], tenant_id="not_admin") net1 = self.deserialize('json', res) self.assertEqual(len(net1['network'][ext_qos.QUEUE]), 36) res = self._create_port('json', net1['network']['id'], tenant_id='not_admin', set_context=True) port = self.deserialize('json', res) self.assertNotIn(ext_qos.QUEUE, port['port']) def test_dscp_value_out_of_range(self): body = {'qos_queue': {'tenant_id': 'admin', 'dscp': '64', 'name': 'foo', 'min': 20, 'max': 20}} res = self._create_qos_queue('json', body) self.assertEqual(res.status_int, 400) def test_dscp_value_with_qos_marking_trusted_returns_400(self): body = {'qos_queue': {'tenant_id': 'admin', 'dscp': '1', 'qos_marking': 'trusted', 'name': 'foo', 'min': 20, 'max': 20}} res = self._create_qos_queue('json', body) self.assertEqual(res.status_int, 400) def test_non_admin_cannot_create_queue(self): body = {'qos_queue': {'tenant_id': 'not_admin', 'name': 'foo', 'min': 20, 'max': 20}} res = self._create_qos_queue('json', body, tenant_id='not_admin', set_context=True) self.assertEqual(res.status_int, 403) def test_update_port_non_admin_does_not_show_queue_id(self): body = {'qos_queue': {'tenant_id': 'not_admin', 'name': 'foo', 'min': 20, 'max': 20}} res = self._create_qos_queue('json', body, tenant_id='not_admin') q1 = self.deserialize('json', res) res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), tenant_id='not_admin', queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) res = self._create_port('json', net1['network']['id'], tenant_id='not_admin', set_context=True) port = self.deserialize('json', res) device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1" data = {'port': {'device_id': device_id}} neutron_context = context.Context('', 'not_admin') port = self._update('ports', port['port']['id'], data, neutron_context=neutron_context) self.assertNotIn(ext_qos.QUEUE, port['port']) def _test_rxtx_factor(self, max_value, rxtx_factor): with self.qos_queue(max=max_value) as q1: res = self._create_network('json', 'net1', True, arg_list=(ext_qos.QUEUE,), queue_id=q1['qos_queue']['id']) net1 = self.deserialize('json', res) res = self._create_port('json', net1['network']['id'], arg_list=(ext_qos.RXTX_FACTOR,), rxtx_factor=rxtx_factor, device_id='1') port = self.deserialize('json', res) req = self.new_show_request('qos-queues', port['port'][ext_qos.QUEUE]) res = req.get_response(self.ext_api) queue = self.deserialize('json', res) self.assertEqual(queue['qos_queue']['max'], max_value * rxtx_factor) def test_rxtx_factor(self): self._test_rxtx_factor(10, 2) def test_decimal_rxtx_factor(self): self._test_rxtx_factor(10, 1.5) def test_decimal_rxtx_factor_below_1(self): self._test_rxtx_factor(10, 0.5)