You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
19 KiB
Python
440 lines
19 KiB
Python
# Copyright (c) 2018 Red Hat, Inc.
|
|
# 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 functools
|
|
|
|
import mock
|
|
from neutron_lib.services.qos import constants as qos_constants
|
|
from oslo_utils import uuidutils
|
|
import six
|
|
|
|
from neutron.agent.common import ovs_lib
|
|
from neutron.agent.linux import ip_lib
|
|
from neutron.common import utils as common_utils
|
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
|
|
import constants as ovs_constants
|
|
from neutron.tests.functional import base
|
|
|
|
|
|
MIN_RATE_DEFAULT = 1000000
|
|
MAX_RATE_DEFAULT = 3000000
|
|
BURST_DEFAULT = 2000000
|
|
QUEUE_NUM_DEFAULT = 'queue_num'
|
|
OTHER_CONFIG_DEFAULT = {six.u('max-rate'): six.u(str(MAX_RATE_DEFAULT)),
|
|
six.u('burst'): six.u(str(BURST_DEFAULT)),
|
|
six.u('min-rate'): six.u(str(MIN_RATE_DEFAULT))}
|
|
|
|
|
|
class BaseOVSTestCase(base.BaseSudoTestCase):
|
|
|
|
def setUp(self):
|
|
super(BaseOVSTestCase, self).setUp()
|
|
self.br_name = ('br-' + uuidutils.generate_uuid())[:10]
|
|
self.port_id = ('port-' + uuidutils.generate_uuid())[:8]
|
|
self.ovs = ovs_lib.OVSBridge(self.br_name)
|
|
self.elements_to_clean = {'bridges': [], 'devices': [],
|
|
'qoses': [], 'queues': []}
|
|
self.addCleanup(self._clean_system)
|
|
|
|
def _clean_system(self):
|
|
# NOTE(ralonsoh): the deletion order is important. First we need to
|
|
# delete any bridge (and the ports attached); then the physical devices
|
|
# created. QoS registers can be deleted if no port has those rules
|
|
# assigned. Queues registers can be deleted if no QoS register refers
|
|
# to those Queues.
|
|
for bridge in self.elements_to_clean['bridges']:
|
|
self.ovs.ovsdb.del_br(bridge).execute()
|
|
for device in self.elements_to_clean['devices']:
|
|
ip_lib.IPDevice(device).link.delete()
|
|
for qos in self.elements_to_clean['qoses']:
|
|
self.ovs.ovsdb.db_destroy('QoS', qos).execute()
|
|
for queue in self.elements_to_clean['queues']:
|
|
self.ovs.ovsdb.db_destroy('Queue', queue).execute()
|
|
|
|
def _list_queues(self, queue_id=None):
|
|
queues = self.ovs.ovsdb.db_list(
|
|
'Queue',
|
|
columns=('_uuid', 'other_config', 'external_ids')).execute()
|
|
if queue_id:
|
|
for queue in (queue for queue in queues
|
|
if queue['_uuid'] == queue_id):
|
|
return queue
|
|
else:
|
|
return None
|
|
return queues
|
|
|
|
def _create_queue(self, max_kbps=int(MAX_RATE_DEFAULT / 1000),
|
|
max_burst_kbps=int(BURST_DEFAULT / 1000),
|
|
min_kbps=int(MIN_RATE_DEFAULT / 1000),
|
|
neutron_port_id=None, queue_num=None):
|
|
neutron_port_id = (('port-' + uuidutils.generate_uuid())[:13]
|
|
if not neutron_port_id else neutron_port_id)
|
|
queue_num = QUEUE_NUM_DEFAULT if not queue_num else queue_num
|
|
queue_id = self.ovs._update_queue(neutron_port_id, queue_num,
|
|
max_kbps=max_kbps,
|
|
max_burst_kbps=max_burst_kbps,
|
|
min_kbps=min_kbps)
|
|
|
|
self.elements_to_clean['queues'].append(queue_id)
|
|
return queue_id, neutron_port_id
|
|
|
|
def _create_qos(self, qos_id=None, queues=None):
|
|
qos_id = self.ovs._update_qos(qos_id=qos_id, queues=queues)
|
|
self.elements_to_clean['qoses'].append(qos_id)
|
|
return qos_id
|
|
|
|
def _list_qos(self, qos_id=None):
|
|
qoses = self.ovs.ovsdb.db_list(
|
|
'QoS',
|
|
columns=('_uuid', 'queues', 'external_ids', 'type')).execute()
|
|
if qos_id:
|
|
for qos in (qos for qos in qoses if qos['_uuid'] == qos_id):
|
|
return qos
|
|
else:
|
|
return None
|
|
return qoses
|
|
|
|
def _create_bridge(self):
|
|
self.ovs.ovsdb.add_br(self.br_name).execute()
|
|
self.elements_to_clean['bridges'].append(self.br_name)
|
|
|
|
def _create_port(self, port_name):
|
|
self.ovs.ovsdb.add_port(self.br_name, port_name).execute()
|
|
|
|
def _find_port_uuid(self, port_name):
|
|
return self.ovs.ovsdb.db_get('Port', port_name, '_uuid').execute()
|
|
|
|
def _find_port_qos(self, port_name):
|
|
return self.ovs.ovsdb.db_get('Port', port_name, 'qos').execute()
|
|
|
|
def _create_dummy_device(self):
|
|
device_name = ('dummy-' + uuidutils.generate_uuid())[:12]
|
|
ip_lib.IPWrapper().add_dummy(device_name)
|
|
self.elements_to_clean['devices'].append(device_name)
|
|
return device_name
|
|
|
|
def _check_value(self, expected_value, retrieve_fn, *args, **kwargs):
|
|
def check_value(ret, keys_to_check):
|
|
ret[0] = retrieve_fn(*args, **kwargs)
|
|
if keys_to_check and isinstance(expected_value, dict):
|
|
for key in keys_to_check:
|
|
if ret[0][key] != expected_value[key]:
|
|
return False
|
|
return True
|
|
return ret[0] == expected_value
|
|
|
|
ret = [None]
|
|
keys_to_check = kwargs.pop('keys_to_check', None)
|
|
part_check_value = functools.partial(check_value, ret, keys_to_check)
|
|
try:
|
|
common_utils.wait_until_true(part_check_value, timeout=5, sleep=1)
|
|
except common_utils.WaitTimeout:
|
|
self.fail('Expected value: %s, retrieved value: %s' %
|
|
(expected_value, ret[0]))
|
|
|
|
def test__update_queue_new(self):
|
|
queue_id, neutron_port_id = self._create_queue()
|
|
self.assertIsNotNone(queue_id)
|
|
external_ids = {six.u('port'): six.u(neutron_port_id),
|
|
six.u('queue-num'): six.u('queue_num'),
|
|
six.u('type'):
|
|
six.u(qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)}
|
|
|
|
expected = {'_uuid': queue_id,
|
|
'other_config': OTHER_CONFIG_DEFAULT,
|
|
'external_ids': external_ids}
|
|
self._check_value(expected, self._list_queues, queue_id)
|
|
|
|
def test__update_queue_update(self):
|
|
queue_id, neutron_port_id = self._create_queue()
|
|
self.assertIsNotNone(queue_id)
|
|
other_config = {six.u('max-rate'): six.u('6000000'),
|
|
six.u('burst'): six.u('5000000'),
|
|
six.u('min-rate'): six.u('4000000')}
|
|
external_ids = {six.u('port'): six.u(neutron_port_id),
|
|
six.u('queue-num'): six.u('queue_num'),
|
|
six.u('type'):
|
|
six.u(qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)}
|
|
queue = self._list_queues(queue_id)
|
|
self.assertIsNotNone(queue)
|
|
|
|
queue_id, _ = self._create_queue(max_kbps=6000, max_burst_kbps=5000,
|
|
min_kbps=4000, queue_num=queue_id,
|
|
neutron_port_id=neutron_port_id)
|
|
self.assertIsNotNone(queue_id)
|
|
expected = {'_uuid': queue_id,
|
|
'other_config': other_config,
|
|
'external_ids': external_ids}
|
|
self._check_value(expected, self._list_queues, queue_id)
|
|
|
|
def test__find_queue(self):
|
|
queue_id, neutron_port_id = self._create_queue()
|
|
external_ids = {six.u('port'): six.u(neutron_port_id),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u('queue_num')}
|
|
expected = {'_uuid': queue_id,
|
|
'external_ids': external_ids,
|
|
'other_config': OTHER_CONFIG_DEFAULT}
|
|
self._check_value(expected, self.ovs._find_queue, neutron_port_id)
|
|
|
|
def test__list_queues(self):
|
|
ports = []
|
|
queue_ids = []
|
|
for _ in range(5):
|
|
queue_id, neutron_port_id = self._create_queue()
|
|
queue_ids.append(queue_id)
|
|
ports.append(neutron_port_id)
|
|
|
|
for idx, port in enumerate(ports):
|
|
external_ids = {six.u('port'): six.u(ports[idx]),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u('queue_num')}
|
|
expected = {'_uuid': queue_ids[idx],
|
|
'external_ids': external_ids,
|
|
'other_config': OTHER_CONFIG_DEFAULT}
|
|
self._check_value([expected], self.ovs._list_queues, port=port,
|
|
_type=qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)
|
|
self._check_value([], self.ovs._list_queues, port=port,
|
|
_type='other_type')
|
|
|
|
def test__delete_queue(self):
|
|
queue_id, port_id = self._create_queue()
|
|
external_ids = {six.u('port'): six.u(port_id),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u('queue_num')}
|
|
expected = {'_uuid': queue_id,
|
|
'external_ids': external_ids,
|
|
'other_config': OTHER_CONFIG_DEFAULT}
|
|
self._check_value(expected, self._list_queues, queue_id=queue_id)
|
|
|
|
self.ovs._delete_queue(queue_id)
|
|
self._check_value(None, self._list_queues, queue_id=queue_id)
|
|
|
|
def test__update_qos_new(self):
|
|
queue_id, port_id = self._create_queue()
|
|
queues = {1: queue_id}
|
|
|
|
qos_id = self._create_qos(queues=queues)
|
|
external_ids = {six.u('id'): six.u(self.ovs._min_bw_qos_id),
|
|
six.u('_type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)}
|
|
expected = {'_uuid': qos_id,
|
|
'type': 'linux-htb',
|
|
'external_ids': external_ids}
|
|
self._check_value(expected, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid', 'type', 'external_ids'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(queues[1], qos['queues'][1].uuid)
|
|
|
|
def test__update_qos_update(self):
|
|
queue_id_1, _ = self._create_queue()
|
|
queues = {1: queue_id_1}
|
|
|
|
qos_id = self._create_qos(queues=queues)
|
|
external_ids = {six.u('id'): six.u(self.ovs._min_bw_qos_id),
|
|
six.u('_type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH)}
|
|
expected = {'_uuid': qos_id,
|
|
'type': 'linux-htb',
|
|
'external_ids': external_ids}
|
|
self._check_value(expected, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid', 'type', 'external_ids'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(queues[1], qos['queues'][1].uuid)
|
|
|
|
queue_id_2, _ = self._create_queue()
|
|
queues[2] = queue_id_2
|
|
|
|
self._create_qos(qos_id=qos_id, queues=queues)
|
|
self._check_value(expected, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid', 'type', 'external_ids'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(2, len(qos['queues']))
|
|
self.assertEqual(queues[1], qos['queues'][1].uuid)
|
|
self.assertEqual(queues[2], qos['queues'][2].uuid)
|
|
|
|
def test__find_qos(self):
|
|
queue_id, _ = self._create_queue()
|
|
queues = {1: queue_id}
|
|
qos_id = self._create_qos(queues=queues)
|
|
self._check_value((qos_id, queues), self.ovs._find_qos)
|
|
|
|
def test__set_port_qos(self):
|
|
port_name = 'test_port'
|
|
self._create_bridge()
|
|
self._create_port(port_name)
|
|
self._check_value([], self._find_port_qos, port_name)
|
|
|
|
qos_id = self._create_qos()
|
|
self.ovs._set_port_qos(port_name, qos_id=qos_id)
|
|
self._check_value(qos_id, self._find_port_qos, port_name)
|
|
|
|
self.ovs._set_port_qos(port_name)
|
|
self._check_value([], self._find_port_qos, port_name)
|
|
|
|
def test_get_bridge_ports(self):
|
|
self._create_bridge()
|
|
device_names = []
|
|
for _ in range(5):
|
|
device_name = self._create_dummy_device()
|
|
device_names.append(device_name)
|
|
self._create_port(device_name)
|
|
|
|
bridge_ports = self.ovs.get_bridge_ports('')
|
|
device_names.sort()
|
|
bridge_ports.sort()
|
|
self.assertEqual(device_names, bridge_ports)
|
|
|
|
def test__set_queue_for_minimum_bandwidth(self):
|
|
self._create_bridge()
|
|
self.ovs._set_queue_for_minimum_bandwidth(1234)
|
|
flows = self.ovs.dump_flows_for_table(ovs_constants.LOCAL_SWITCHING)
|
|
expected = 'priority=200,reg4=0,in_port=1234 actions=set_queue:1234,' \
|
|
'load:0x1->NXM_NX_REG4[0],resubmit(,0)'
|
|
self.assertIn(expected, flows)
|
|
|
|
def test__unset_queue_for_minimum_bandwidth(self):
|
|
self.test__set_queue_for_minimum_bandwidth()
|
|
|
|
self.ovs._unset_queue_for_minimum_bandwidth(1234)
|
|
flows = self.ovs.dump_flows_for_table(ovs_constants.LOCAL_SWITCHING)
|
|
expected = 'in_port=1234'
|
|
self.assertNotIn(expected, flows)
|
|
|
|
def test_update_minimum_bandwidth_queue(self):
|
|
port_name = 'test_output_port_1'
|
|
self._create_bridge()
|
|
self._create_port(port_name)
|
|
queue_num = 1
|
|
queue_id, port_id = self._create_queue(neutron_port_id=self.port_id)
|
|
queues = {queue_num: queue_id}
|
|
qos_id = self._create_qos(queues=queues)
|
|
|
|
self.ovs.update_minimum_bandwidth_queue(self.port_id, [port_name],
|
|
queue_num, 1800)
|
|
self._check_value(qos_id, self._find_port_qos, port_name)
|
|
external_ids = {six.u('port'): six.u(port_id),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u('queue_num')}
|
|
other_config = {six.u('max-rate'): six.u(str(MAX_RATE_DEFAULT)),
|
|
six.u('burst'): six.u(str(BURST_DEFAULT)),
|
|
six.u('min-rate'): six.u('1800000')}
|
|
expected = {'_uuid': queue_id,
|
|
'external_ids': external_ids,
|
|
'other_config': other_config}
|
|
self._check_value(expected, self._list_queues, queue_id)
|
|
|
|
def test_update_minimum_bandwidth_queue_no_qos_no_queue(self):
|
|
port_name = 'test_output_port_2'
|
|
self._create_bridge()
|
|
self._create_port(port_name)
|
|
queue_num = 1
|
|
|
|
self.ovs.update_minimum_bandwidth_queue(self.port_id, [port_name],
|
|
queue_num, 1700)
|
|
qos_id = self._find_port_qos(port_name)
|
|
qos = self._list_qos(qos_id)
|
|
queue_id = qos['queues'][1].uuid
|
|
external_ids = {six.u('port'): six.u(self.port_id),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u(str(queue_num))}
|
|
other_config = {six.u('min-rate'): six.u('1700000')}
|
|
expected = {'_uuid': queue_id,
|
|
'external_ids': external_ids,
|
|
'other_config': other_config}
|
|
self._check_value(expected, self._list_queues, queue_id)
|
|
self.elements_to_clean['qoses'].append(qos_id)
|
|
self.elements_to_clean['queues'].append(queue_id)
|
|
|
|
def test_delete_minimum_bandwidth_queue(self):
|
|
queue_id_1, neutron_port_id_1 = self._create_queue(queue_num=1)
|
|
queue_id_2, neutron_port_id_2 = self._create_queue(queue_num=2)
|
|
queues = {1: queue_id_1, 2: queue_id_2}
|
|
qos_id = self._create_qos(queues=queues)
|
|
self._check_value({'_uuid': qos_id}, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(queue_id_1, qos['queues'][1].uuid)
|
|
self.assertEqual(queue_id_2, qos['queues'][2].uuid)
|
|
|
|
self.ovs.delete_minimum_bandwidth_queue(neutron_port_id_2)
|
|
self._check_value({'_uuid': qos_id}, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(1, len(qos['queues']))
|
|
self.assertEqual(queue_id_1, qos['queues'][1].uuid)
|
|
|
|
self.ovs.delete_minimum_bandwidth_queue(neutron_port_id_1)
|
|
self._check_value({'_uuid': qos_id}, self._list_qos, qos_id,
|
|
keys_to_check=['_uuid'])
|
|
qos = self._list_qos(qos_id)
|
|
self.assertEqual(0, len(qos['queues']))
|
|
|
|
def test_delete_minimum_bandwidth_queue_no_qos_found(self):
|
|
queue_id, neutron_port_id = self._create_queue(queue_num=1)
|
|
self.addCleanup(self.ovs._delete_queue, queue_id)
|
|
|
|
# Check that it will not raise any exception even if there is no
|
|
# qos with associated queues
|
|
self.ovs.delete_minimum_bandwidth_queue(neutron_port_id)
|
|
|
|
# And verify that this queue wasn't in fact deleted as there was no
|
|
# qos found
|
|
queue = self._list_queues(queue_id)
|
|
self.assertEqual(queue_id, queue['_uuid'])
|
|
|
|
def test_clear_minimum_bandwidth_qos(self):
|
|
queue_id_1, _ = self._create_queue(queue_num=1)
|
|
queue_id_2, _ = self._create_queue(queue_num=2)
|
|
queue_id_3, port_id_3 = self._create_queue()
|
|
queues = {1: queue_id_1, 2: queue_id_2}
|
|
qos_id = self._create_qos(queues=queues)
|
|
|
|
# NOTE(ralonsoh): we need to clean only the QoS rule created in this
|
|
# test in order to avoid any interference with other tests.
|
|
qoses = self.ovs._list_qos(_id=self.ovs._min_bw_qos_id)
|
|
with mock.patch.object(self.ovs, '_list_qos') as mock_list_qos:
|
|
mock_list_qos.return_value = qoses
|
|
self.ovs.clear_minimum_bandwidth_qos()
|
|
self._check_value(None, self._list_qos, qos_id=qos_id)
|
|
self._check_value(None, self._list_queues, queue_id=queue_id_1)
|
|
self._check_value(None, self._list_queues, queue_id=queue_id_2)
|
|
external_ids = {six.u('port'): six.u(port_id_3),
|
|
six.u('type'): six.u(
|
|
qos_constants.RULE_TYPE_MINIMUM_BANDWIDTH),
|
|
six.u('queue-num'): six.u('queue_num')}
|
|
expected = {'_uuid': queue_id_3,
|
|
'external_ids': external_ids,
|
|
'other_config': OTHER_CONFIG_DEFAULT}
|
|
self._check_value(expected, self._list_queues, queue_id=queue_id_3)
|
|
|
|
def test_get_egress_min_bw_for_port(self):
|
|
self.ovs.update_minimum_bandwidth_queue(self.port_id, [], 1, 2800)
|
|
self._check_value(2800, self.ovs.get_egress_min_bw_for_port,
|
|
port_id=self.port_id)
|
|
|
|
def test_set_controllers_inactivity_probe(self):
|
|
self._create_bridge()
|
|
self.ovs.set_controller(['tcp:127.0.0.1:6633'])
|
|
self.ovs.set_controllers_inactivity_probe(8)
|
|
self.assertEqual(8000,
|
|
self.ovs.db_get_val('Controller', self.br_name,
|
|
'inactivity_probe'))
|