192 lines
8.2 KiB
Python
192 lines
8.2 KiB
Python
# Copyright (c) 2023 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.
|
|
|
|
from unittest import mock
|
|
|
|
from oslo_utils import uuidutils
|
|
|
|
from neutron.agent.ovn.agent import ovsdb as agent_ovsdb
|
|
from neutron.agent.ovn.extensions import qos_hwol
|
|
from neutron.common.ovn import constants as ovn_const
|
|
from neutron.common.ovn import utils
|
|
from neutron.common import utils as n_utils
|
|
from neutron.tests.functional import base
|
|
|
|
|
|
class OVSInterfaceEventTestCase(base.TestOVNFunctionalBase):
|
|
|
|
def _cleanup(self):
|
|
self.ovs_idl.del_port(self.port_name, bridge=self.br_name).execute(
|
|
check_error=False)
|
|
self.ovs_idl.del_br(self.br_name).execute(check_error=False)
|
|
|
|
def test_port_creation_and_deletion(self):
|
|
def check_add_port_called():
|
|
try:
|
|
mock_agent.qos_hwol_ext.add_port.assert_has_calls(
|
|
[mock.call(port_iface_id, self.port_name)])
|
|
return True
|
|
except AssertionError:
|
|
return False
|
|
|
|
def check_remove_egress_called():
|
|
try:
|
|
mock_agent.qos_hwol_ext.remove_egress.assert_has_calls(
|
|
[mock.call(port_iface_id)])
|
|
return True
|
|
except AssertionError:
|
|
return False
|
|
|
|
port_iface_id = 'port_iface-id'
|
|
mock_agent = mock.Mock()
|
|
events = [qos_hwol.OVSInterfaceEvent(mock_agent)]
|
|
self.ovs_idl = agent_ovsdb.MonitorAgentOvsIdl(events=events).start()
|
|
self.br_name = ('brtest-' + uuidutils.generate_uuid())[:13]
|
|
self.port_name = ('port-' + uuidutils.generate_uuid())[:13]
|
|
self.addCleanup(self._cleanup)
|
|
with self.ovs_idl.transaction() as txn:
|
|
txn.add(self.ovs_idl.add_br(self.br_name))
|
|
txn.add(self.ovs_idl.add_port(self.br_name, self.port_name))
|
|
txn.add(self.ovs_idl.iface_set_external_id(
|
|
self.port_name, 'iface-id', port_iface_id))
|
|
txn.add(self.ovs_idl.db_set(
|
|
'Interface', self.port_name, ('type', 'internal')))
|
|
|
|
exc = Exception('Port %s was not added to the bridge %s' %
|
|
(self.port_name, self.br_name))
|
|
n_utils.wait_until_true(check_add_port_called, timeout=5,
|
|
exception=exc)
|
|
|
|
self.ovs_idl.del_port(self.port_name).execute(check_error=True)
|
|
exc = Exception('Port %s was not deleted from the bridge %s' %
|
|
(self.port_name, self.br_name))
|
|
n_utils.wait_until_true(check_remove_egress_called, timeout=5,
|
|
exception=exc)
|
|
|
|
|
|
class QoSBandwidthLimitEventTestCase(base.TestOVNFunctionalBase):
|
|
|
|
def setUp(self, **kwargs):
|
|
super().setUp(**kwargs)
|
|
self.net = self._make_network(self.fmt, 'n1', True)['network']
|
|
res = self._create_subnet(self.fmt, self.net['id'], '10.0.0.0/24')
|
|
self.subnet = self.deserialize(self.fmt, res)['subnet']
|
|
res = self._create_port(self.fmt, self.net['id'])
|
|
self.port = self.deserialize(self.fmt, res)['port']
|
|
|
|
def test_qos_bw_limit_created_and_updated(self):
|
|
def check_update_egress_called(rate):
|
|
try:
|
|
mock_agent.qos_hwol_ext.update_egress.assert_has_calls(
|
|
[mock.call(port_id, rate, 0)])
|
|
return True
|
|
except AssertionError:
|
|
return False
|
|
|
|
mock_agent = mock.Mock(nb_idl=self.nb_api)
|
|
events = [qos_hwol.QoSBandwidthLimitEvent(mock_agent)]
|
|
agent_ovsdb.MonitorAgentOvnNbIdl(qos_hwol.NB_IDL_TABLES,
|
|
events).start()
|
|
lswitch_name = utils.ovn_name(self.net['id'])
|
|
port_id = self.port['id']
|
|
ovn_rule = {'switch': lswitch_name,
|
|
'priority': 1000,
|
|
'direction': 'from-lport',
|
|
'match': 'inport == ' + port_id,
|
|
'rate': 10000,
|
|
'external_ids': {ovn_const.OVN_PORT_EXT_ID_KEY: port_id}}
|
|
self.nb_api.qos_add(**ovn_rule).execute(check_error=True)
|
|
n_utils.wait_until_true(
|
|
lambda: check_update_egress_called(ovn_rule['rate']), timeout=5)
|
|
|
|
ovn_rule['rate'] = 15000
|
|
self.nb_api.qos_add(**ovn_rule, may_exist=True).execute(
|
|
check_error=True)
|
|
n_utils.wait_until_true(
|
|
lambda: check_update_egress_called(ovn_rule['rate']), timeout=5)
|
|
|
|
|
|
class QoSMinimumBandwidthEventTestCase(base.TestOVNFunctionalBase):
|
|
|
|
def setUp(self, **kwargs):
|
|
super().setUp(**kwargs)
|
|
self.net = self._make_network(self.fmt, 'n1', True)['network']
|
|
res = self._create_subnet(self.fmt, self.net['id'], '10.0.0.0/24')
|
|
self.subnet = self.deserialize(self.fmt, res)['subnet']
|
|
res = self._create_port(self.fmt, self.net['id'])
|
|
self.port = self.deserialize(self.fmt, res)['port']
|
|
|
|
def test_qos_min_bw_created_and_updated(self):
|
|
def check_update_egress_called(max_kbps, min_kbps):
|
|
try:
|
|
mock_agent.qos_hwol_ext.update_egress.assert_has_calls(
|
|
[mock.call(port_id, max_kbps, min_kbps)])
|
|
return True
|
|
except AssertionError:
|
|
return False
|
|
|
|
mock_agent = mock.Mock(nb_idl=self.nb_api)
|
|
events = [qos_hwol.QoSMinimumBandwidthEvent(mock_agent)]
|
|
agent_ovsdb.MonitorAgentOvnNbIdl(qos_hwol.NB_IDL_TABLES,
|
|
events).start()
|
|
port_id = self.port['id']
|
|
min_kbps = 5000
|
|
lsp = self.nb_api.lsp_get(port_id).execute(check_error=True)
|
|
options = {ovn_const.LSP_OPTIONS_QOS_MIN_RATE: str(min_kbps)}
|
|
self.nb_api.update_lswitch_qos_options(lsp, **options).execute(
|
|
check_error=True)
|
|
n_utils.wait_until_true(
|
|
lambda: check_update_egress_called(0, min_kbps), timeout=5)
|
|
|
|
|
|
class PortBindingChassisCreatedEventTestCase(base.TestOVNFunctionalBase):
|
|
|
|
def setUp(self, **kwargs):
|
|
super().setUp(**kwargs)
|
|
self.net = self._make_network(self.fmt, 'n1', True)['network']
|
|
res = self._create_subnet(self.fmt, self.net['id'], '10.0.0.0/24')
|
|
self.subnet = self.deserialize(self.fmt, res)['subnet']
|
|
res = self._create_port(self.fmt, self.net['id'])
|
|
self.port = self.deserialize(self.fmt, res)['port']
|
|
|
|
@mock.patch.object(agent_ovsdb, 'get_ovs_port_name')
|
|
@mock.patch.object(agent_ovsdb, 'get_port_qos')
|
|
def test_port_binding_chassis_create_event(self, mock_get_port_qos,
|
|
*args):
|
|
def check_update_egress_called(max_kbps, min_kbps):
|
|
try:
|
|
mock_agent.qos_hwol_ext.update_egress.assert_has_calls(
|
|
[mock.call(self.port['id'], max_kbps, min_kbps)])
|
|
return True
|
|
except AssertionError:
|
|
return False
|
|
|
|
max_kbps, min_kbps = 1000, 800
|
|
mock_get_port_qos.return_value = max_kbps, min_kbps
|
|
mock_agent = mock.Mock(nb_idl=self.nb_api)
|
|
events = [qos_hwol.PortBindingChassisCreatedEvent(mock_agent)]
|
|
chassis_name = self.add_fake_chassis('ovn-host-fake')
|
|
mock_agent.chassis = chassis_name
|
|
agent_ovsdb.MonitorAgentOvnSbIdl(qos_hwol.SB_IDL_TABLES, events,
|
|
chassis=chassis_name).start()
|
|
lsp_columns = {}
|
|
lsp_name = self.port['id']
|
|
ls_name = utils.ovn_name(self.net['id'])
|
|
self.nb_api.create_lswitch_port(
|
|
lsp_name, ls_name, **lsp_columns).execute(check_error=True)
|
|
self.sb_api.lsp_bind(lsp_name, chassis_name).execute(check_error=True)
|
|
n_utils.wait_until_true(
|
|
lambda: check_update_egress_called(max_kbps, min_kbps), timeout=5)
|