Merge "Fix ingress bw limit for OVS DPDK ports"

This commit is contained in:
Zuul 2018-01-18 18:07:39 +00:00 committed by Gerrit Code Review
commit ff24fc0727
8 changed files with 177 additions and 74 deletions

View File

@ -31,6 +31,8 @@ from neutron._i18n import _
from neutron.agent.common import ip_lib
from neutron.agent.common import utils
from neutron.agent.ovsdb import api as ovsdb_api
from neutron.common import constants as common_constants
from neutron.common import utils as common_utils
from neutron.conf.agent import ovs_conf
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
import constants
@ -735,16 +737,27 @@ class OVSBridge(BaseOVS):
other_config=qos_other_config))
return qos_uuid
def update_ingress_bw_limit_for_port(self, port_name, max_kbps,
max_burst_kbps):
max_bw_in_bits = str(max_kbps * 1000)
max_burst_in_bits = str(max_burst_kbps * 1000)
def _update_bw_limit_profile_dpdk(self, txn, port_name, qos_uuid,
other_config):
if qos_uuid:
txn.add(self.ovsdb.db_set(
'QoS', qos_uuid, ('other_config', other_config)))
else:
external_ids = {'id': port_name}
qos_uuid = txn.add(
self.ovsdb.db_create(
'QoS', external_ids=external_ids, type='egress-policer',
other_config=other_config))
return qos_uuid
def _update_ingress_bw_limit_for_port(
self, port_name, max_bw_in_bits, max_burst_in_bits):
qos_other_config = {
'max-rate': max_bw_in_bits
'max-rate': str(max_bw_in_bits)
}
queue_other_config = {
'max-rate': max_bw_in_bits,
'burst': max_burst_in_bits,
'max-rate': str(max_bw_in_bits),
'burst': str(max_burst_in_bits),
}
qos = self.find_qos(port_name)
queue = self.find_queue(port_name, QOS_DEFAULT_QUEUE)
@ -764,6 +777,33 @@ class OVSBridge(BaseOVS):
txn.add(self.ovsdb.db_set(
'Port', port_name, ('qos', qos_uuid)))
def _update_ingress_bw_limit_for_dpdk_port(
self, port_name, max_bw_in_bits, max_burst_in_bits):
# cir and cbs should be set in bytes instead of bits
qos_other_config = {
'cir': str(max_bw_in_bits / 8),
'cbs': str(max_burst_in_bits / 8)
}
qos = self.find_qos(port_name)
qos_uuid = qos['_uuid'] if qos else None
with self.ovsdb.transaction(check_error=True) as txn:
qos_uuid = self._update_bw_limit_profile_dpdk(
txn, port_name, qos_uuid, qos_other_config)
txn.add(self.ovsdb.db_set(
'Port', port_name, ('qos', qos_uuid)))
def update_ingress_bw_limit_for_port(self, port_name, max_kbps,
max_burst_kbps):
max_bw_in_bits = max_kbps * common_constants.SI_BASE
max_burst_in_bits = max_burst_kbps * common_constants.SI_BASE
port_type = self._get_port_val(port_name, "type")
if port_type in constants.OVS_DPDK_PORT_TYPES:
self._update_ingress_bw_limit_for_dpdk_port(
port_name, max_bw_in_bits, max_burst_in_bits)
else:
self._update_ingress_bw_limit_for_port(
port_name, max_bw_in_bits, max_burst_in_bits)
def get_ingress_bw_limit_for_port(self, port_name):
max_kbps = None
qos_max_kbps = None
@ -775,17 +815,18 @@ class OVSBridge(BaseOVS):
other_config = qos_res['other_config']
max_bw_in_bits = other_config.get('max-rate')
if max_bw_in_bits is not None:
qos_max_kbps = int(max_bw_in_bits) / 1000
qos_max_kbps = int(max_bw_in_bits) / common_constants.SI_BASE
queue_res = self.find_queue(port_name, QOS_DEFAULT_QUEUE)
if queue_res:
other_config = queue_res['other_config']
max_bw_in_bits = other_config.get('max-rate')
if max_bw_in_bits is not None:
queue_max_kbps = int(max_bw_in_bits) / 1000
queue_max_kbps = int(max_bw_in_bits) / common_constants.SI_BASE
max_burst_in_bits = other_config.get('burst')
if max_burst_in_bits is not None:
max_burst_kbit = int(max_burst_in_bits) / 1000
max_burst_kbit = (
int(max_burst_in_bits) / common_constants.SI_BASE)
if qos_max_kbps == queue_max_kbps:
max_kbps = qos_max_kbps
@ -794,7 +835,24 @@ class OVSBridge(BaseOVS):
"queue max-rate %(queue_max_kbps)s",
{'qos_max_kbps': qos_max_kbps,
'queue_max_kbps': queue_max_kbps})
return max_kbps, max_burst_kbit
def get_ingress_bw_limit_for_dpdk_port(self, port_name):
max_kbps = None
max_burst_kbit = None
res = self.find_qos(port_name)
if res:
other_config = res['other_config']
max_bw_in_bytes = other_config.get("cir")
if max_bw_in_bytes is not None:
max_kbps = common_utils.bits_to_kilobits(
common_utils.bytes_to_bits(int(max_bw_in_bytes)),
common_constants.SI_BASE)
max_burst_in_bytes = other_config.get("cbs")
if max_burst_in_bytes is not None:
max_burst_kbit = common_utils.bits_to_kilobits(
common_utils.bytes_to_bits(int(max_burst_in_bytes)),
common_constants.SI_BASE)
return max_kbps, max_burst_kbit
def delete_ingress_bw_limit_for_port(self, port_name):

View File

@ -20,14 +20,13 @@ from neutron_lib.services.qos import constants as qos_consts
from neutron._i18n import _
from neutron.agent.linux import ip_lib
from neutron.common import constants
from neutron.common import utils
INGRESS_QDISC_ID = "ffff:"
MAX_MTU_VALUE = 65535
SI_BASE = 1000
IEC_BASE = 1024
LATENCY_UNIT = "ms"
BW_LIMIT_UNIT = "kbit" # kilobits per second in tc's notation
BURST_UNIT = "kbit" # kilobits in tc's notation
@ -66,10 +65,10 @@ def convert_to_kilobits(value, base):
if value.isdigit():
value = int(value)
if input_in_bits:
return bits_to_kilobits(value, base)
return utils.bits_to_kilobits(value, base)
else:
bits_value = bytes_to_bits(value)
return bits_to_kilobits(bits_value, base)
bits_value = utils.bytes_to_bits(value)
return utils.bits_to_kilobits(bits_value, base)
unit = value[-1:]
if unit not in UNITS.keys():
raise InvalidUnit(unit=unit)
@ -77,17 +76,8 @@ def convert_to_kilobits(value, base):
if input_in_bits:
bits_value = val * (base ** UNITS[unit])
else:
bits_value = bytes_to_bits(val * (base ** UNITS[unit]))
return bits_to_kilobits(bits_value, base)
def bytes_to_bits(value):
return value * 8
def bits_to_kilobits(value, base):
#NOTE(slaweq): round up that even 1 bit will give 1 kbit as a result
return int((value + (base - 1)) / base)
bits_value = utils.bytes_to_bits(val * (base ** UNITS[unit]))
return utils.bits_to_kilobits(bits_value, base)
class TcCommand(ip_lib.IPDevice):
@ -124,10 +114,11 @@ class TcCommand(ip_lib.IPDevice):
if m:
#NOTE(slaweq): because tc is giving bw limit in SI units
# we need to calculate it as 1000bit = 1kbit:
bw_limit = convert_to_kilobits(m.group(1), SI_BASE)
bw_limit = convert_to_kilobits(m.group(1), constants.SI_BASE)
#NOTE(slaweq): because tc is giving burst limit in IEC units
# we need to calculate it as 1024bit = 1kbit:
burst_limit = convert_to_kilobits(m.group(2), IEC_BASE)
burst_limit = convert_to_kilobits(
m.group(2), constants.IEC_BASE)
return bw_limit, burst_limit
return None, None
@ -144,10 +135,10 @@ class TcCommand(ip_lib.IPDevice):
return None, None
#NOTE(slaweq): because tc is giving bw limit in SI units
# we need to calculate it as 1000bit = 1kbit:
bw_limit = convert_to_kilobits(m.group(2), SI_BASE)
bw_limit = convert_to_kilobits(m.group(2), constants.SI_BASE)
#NOTE(slaweq): because tc is giving burst limit in IEC units
# we need to calculate it as 1024bit = 1kbit:
burst_limit = convert_to_kilobits(m.group(3), IEC_BASE)
burst_limit = convert_to_kilobits(m.group(3), constants.IEC_BASE)
return bw_limit, burst_limit
def set_filters_bw_limit(self, bw_limit, burst_limit):

View File

@ -222,3 +222,7 @@ FLOATING_IP_HOST_NEEDS_BINDING = "FLOATING_IP_HOST_NEEDS_BINDING"
# Possible types of values (e.g. in QoS rule types)
VALUES_TYPE_CHOICES = "choices"
VALUES_TYPE_RANGE = "range"
# Units base
SI_BASE = 1000
IEC_BASE = 1024

View File

@ -801,3 +801,12 @@ def resolve_ref(ref):
if isinstance(ref, weakref.ref):
ref = ref()
return ref
def bytes_to_bits(value):
return value * 8
def bits_to_kilobits(value, base):
#NOTE(slaweq): round up that even 1 bit will give 1 kbit as a result
return int((value + (base - 1)) / base)

View File

@ -137,6 +137,8 @@ OVS_DATAPATH_NETDEV = 'netdev'
OVS_DPDK_VHOST_USER = 'dpdkvhostuser'
OVS_DPDK_VHOST_USER_CLIENT = 'dpdkvhostuserclient'
OVS_DPDK_PORT_TYPES = [OVS_DPDK_VHOST_USER, OVS_DPDK_VHOST_USER_CLIENT]
# default ovs vhost-user socket location
VHOST_USER_SOCKET_DIR = '/var/run/openvswitch'

View File

@ -23,6 +23,8 @@ from ovsdbapp.backend.ovs_idl import idlutils
from neutron.agent.common import ovs_lib
from neutron.agent.linux import ip_lib
from neutron.common import utils
from neutron.plugins.ml2.drivers.openvswitch.agent.common import (
constants as agent_const)
from neutron.tests.common.exclusive_resources import port
from neutron.tests.common import net_helpers
from neutron.tests.functional.agent.linux import base
@ -418,6 +420,27 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
self.assertIsNone(max_rate)
self.assertIsNone(burst)
def test_ingress_bw_limit_dpdk_port(self):
port_name, _ = self.create_ovs_port(
('type', agent_const.OVS_DPDK_VHOST_USER))
self.br.update_ingress_bw_limit_for_port(port_name, 700, 70)
max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port(
port_name)
self.assertEqual(700, max_rate)
self.assertEqual(70, burst)
self.br.update_ingress_bw_limit_for_port(port_name, 750, 100)
max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port(
port_name)
self.assertEqual(750, max_rate)
self.assertEqual(100, burst)
self.br.delete_ingress_bw_limit_for_port(port_name)
max_rate, burst = self.br.get_ingress_bw_limit_for_dpdk_port(
port_name)
self.assertIsNone(max_rate)
self.assertIsNone(burst)
def test_db_create_references(self):
with self.ovs.ovsdb.transaction(check_error=True) as txn:
queue = txn.add(self.ovs.ovsdb.db_create("Queue",

View File

@ -17,6 +17,8 @@ import mock
from neutron_lib.services.qos import constants as qos_consts
from neutron.agent.linux import tc_lib
from neutron.common import constants
from neutron.common import utils
from neutron.tests import base
DEVICE_NAME = "tap_device"
@ -58,7 +60,7 @@ class BaseUnitConversionTest(object):
def test_convert_to_kilobits_bits_value(self):
value = "1000bit"
expected_value = tc_lib.bits_to_kilobits(1000, self.base_unit)
expected_value = utils.bits_to_kilobits(1000, self.base_unit)
self.assertEqual(
expected_value,
tc_lib.convert_to_kilobits(value, self.base_unit)
@ -66,7 +68,7 @@ class BaseUnitConversionTest(object):
def test_convert_to_kilobits_megabytes_value(self):
value = "1m"
expected_value = tc_lib.bits_to_kilobits(
expected_value = utils.bits_to_kilobits(
self.base_unit ** 2 * 8, self.base_unit)
self.assertEqual(
expected_value,
@ -75,7 +77,7 @@ class BaseUnitConversionTest(object):
def test_convert_to_kilobits_megabits_value(self):
value = "1mbit"
expected_value = tc_lib.bits_to_kilobits(
expected_value = utils.bits_to_kilobits(
self.base_unit ** 2, self.base_unit)
self.assertEqual(
expected_value,
@ -89,53 +91,15 @@ class BaseUnitConversionTest(object):
tc_lib.convert_to_kilobits, value, self.base_unit
)
def test_bytes_to_bits(self):
test_values = [
(0, 0), # 0 bytes should be 0 bits
(1, 8) # 1 byte should be 8 bits
]
for input_bytes, expected_bits in test_values:
self.assertEqual(
expected_bits, tc_lib.bytes_to_bits(input_bytes)
)
class TestSIUnitConversions(BaseUnitConversionTest, base.BaseTestCase):
base_unit = tc_lib.SI_BASE
def test_bits_to_kilobits(self):
test_values = [
(0, 0), # 0 bites should be 0 kilobites
(1, 1), # 1 bit should be 1 kilobit
(999, 1), # 999 bits should be 1 kilobit
(1000, 1), # 1000 bits should be 1 kilobit
(1001, 2) # 1001 bits should be 2 kilobits
]
for input_bits, expected_kilobits in test_values:
self.assertEqual(
expected_kilobits,
tc_lib.bits_to_kilobits(input_bits, self.base_unit)
)
base_unit = constants.SI_BASE
class TestIECUnitConversions(BaseUnitConversionTest, base.BaseTestCase):
base_unit = tc_lib.IEC_BASE
def test_bits_to_kilobits(self):
test_values = [
(0, 0), # 0 bites should be 0 kilobites
(1, 1), # 1 bit should be 1 kilobit
(1023, 1), # 1023 bits should be 1 kilobit
(1024, 1), # 1024 bits should be 1 kilobit
(1025, 2) # 1025 bits should be 2 kilobits
]
for input_bits, expected_kilobits in test_values:
self.assertEqual(
expected_kilobits,
tc_lib.bits_to_kilobits(input_bits, self.base_unit)
)
base_unit = constants.IEC_BASE
class TestTcCommand(base.BaseTestCase):

View File

@ -28,6 +28,7 @@ import six
import testscenarios
import testtools
from neutron.common import constants as common_constants
from neutron.common import exceptions as n_exc
from neutron.common import utils
from neutron.plugins.common import utils as plugin_utils
@ -775,3 +776,54 @@ class TestThrottler(base.BaseTestCase):
obj = Klass()
obj.method()
class BaseUnitConversionTest(object):
def test_bytes_to_bits(self):
test_values = [
(0, 0), # 0 bytes should be 0 bits
(1, 8) # 1 byte should be 8 bits
]
for input_bytes, expected_bits in test_values:
self.assertEqual(
expected_bits, utils.bytes_to_bits(input_bytes)
)
class TestSIUnitConversions(BaseUnitConversionTest, base.BaseTestCase):
base_unit = common_constants.SI_BASE
def test_bits_to_kilobits(self):
test_values = [
(0, 0), # 0 bites should be 0 kilobites
(1, 1), # 1 bit should be 1 kilobit
(999, 1), # 999 bits should be 1 kilobit
(1000, 1), # 1000 bits should be 1 kilobit
(1001, 2) # 1001 bits should be 2 kilobits
]
for input_bits, expected_kilobits in test_values:
self.assertEqual(
expected_kilobits,
utils.bits_to_kilobits(input_bits, self.base_unit)
)
class TestIECUnitConversions(BaseUnitConversionTest, base.BaseTestCase):
base_unit = common_constants.IEC_BASE
def test_bits_to_kilobits(self):
test_values = [
(0, 0), # 0 bites should be 0 kilobites
(1, 1), # 1 bit should be 1 kilobit
(1023, 1), # 1023 bits should be 1 kilobit
(1024, 1), # 1024 bits should be 1 kilobit
(1025, 2) # 1025 bits should be 2 kilobits
]
for input_bits, expected_kilobits in test_values:
self.assertEqual(
expected_kilobits,
utils.bits_to_kilobits(input_bits, self.base_unit)
)