Merge "Refactor for meter ID Generator"
This commit is contained in:
commit
1434a1e5dd
@ -28,3 +28,6 @@ NO_PBLOCKS_TYPES = [
|
|||||||
constants.DEVICE_OWNER_ROUTER_HA_INTF,
|
constants.DEVICE_OWNER_ROUTER_HA_INTF,
|
||||||
constants.DEVICE_OWNER_FLOATINGIP,
|
constants.DEVICE_OWNER_FLOATINGIP,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
METER_FLAG_BPS = "bps"
|
||||||
|
METER_FLAG_PPS = "pps"
|
||||||
|
@ -22,6 +22,7 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron.agent.l2.extensions import qos_linux as qos
|
from neutron.agent.l2.extensions import qos_linux as qos
|
||||||
|
from neutron.plugins.ml2.common import constants as comm_consts
|
||||||
from neutron.services.qos.drivers.openvswitch import driver
|
from neutron.services.qos.drivers.openvswitch import driver
|
||||||
|
|
||||||
|
|
||||||
@ -30,27 +31,71 @@ LOG = logging.getLogger(__name__)
|
|||||||
MAX_RETIES = 1000
|
MAX_RETIES = 1000
|
||||||
|
|
||||||
|
|
||||||
class MeterRuleManager(object):
|
class MeterIDGenerator(object):
|
||||||
# This cache will be:
|
# This cache will be:
|
||||||
# PORT_METER_ID = {"port_id_1_ingress": 1,
|
# PORT_METER_ID = {"port_id_1_ingress": 1,
|
||||||
# "port_id_1_egress: 2,
|
# "port_id_1_egress: 2,
|
||||||
# "port_id_2_ingress": 3,
|
# "port_id_2_ingress": 3,
|
||||||
# "port_id_2_egress: 4}
|
# "port_id_2_egress: 4}
|
||||||
PORT_METER_ID = {}
|
|
||||||
# This will be:
|
|
||||||
# PORT_INFO_INGRESS = {"port_id_1": (of_port_name, mac_1, local_vlan_1),
|
|
||||||
# "port_id_2": (of_port_name, mac_2, local_vlan_2),
|
|
||||||
PORT_INFO_INGRESS = {}
|
|
||||||
# PORT_INFO_EGRESS = {"port_id_1": (of_port_name, mac_1, of_port_1),
|
|
||||||
# "port_id_2": (of_port_name, mac_2, of_port_2),
|
|
||||||
PORT_INFO_EGRESS = {}
|
|
||||||
|
|
||||||
def __init__(self, br_int):
|
def __new__(cls, *args, **kwargs):
|
||||||
|
# make it a singleton
|
||||||
|
if not hasattr(cls, '_instance'):
|
||||||
|
cls._instance = super(MeterIDGenerator, cls).__new__(cls)
|
||||||
|
cls.PORT_METER_ID = {}
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
def __init__(self, max_meter):
|
||||||
|
self.max_meter = max_meter
|
||||||
|
|
||||||
|
def _generate_meter_id(self):
|
||||||
|
if self.max_meter <= 0:
|
||||||
|
return
|
||||||
|
used_meter_ids = self.PORT_METER_ID.values()
|
||||||
|
cid = None
|
||||||
|
times = 0
|
||||||
|
while not cid or cid in used_meter_ids:
|
||||||
|
cid = random.randint(1, self.max_meter)
|
||||||
|
times += 1
|
||||||
|
if times >= MAX_RETIES:
|
||||||
|
return
|
||||||
|
return cid
|
||||||
|
|
||||||
|
def allocate_meter_id(self, key):
|
||||||
|
meter_id = self._generate_meter_id()
|
||||||
|
if not meter_id:
|
||||||
|
return
|
||||||
|
self.set_meter_id(key, meter_id)
|
||||||
|
return meter_id
|
||||||
|
|
||||||
|
def remove_port_meter_id(self, key):
|
||||||
|
return self.PORT_METER_ID.pop(key, None)
|
||||||
|
|
||||||
|
def set_meter_id(self, key, meter_id):
|
||||||
|
self.PORT_METER_ID[key] = meter_id
|
||||||
|
|
||||||
|
|
||||||
|
class MeterRuleManager(object):
|
||||||
|
|
||||||
|
def __init__(self, br_int, type_=comm_consts.METER_FLAG_PPS):
|
||||||
self.br_int = br_int
|
self.br_int = br_int
|
||||||
|
self.max_meter = 0
|
||||||
self._init_max_meter_id()
|
self._init_max_meter_id()
|
||||||
|
self.rule_type = type_
|
||||||
|
self.generator = MeterIDGenerator(self.max_meter)
|
||||||
|
# This will be:
|
||||||
|
# PORT_INFO_INGRESS = {"port_id_1": (mac_1, 1),
|
||||||
|
# "port_id_2": (mac_2, 2),
|
||||||
|
# "port_id_3": (mac_3, 3),
|
||||||
|
# "port_id_4": (mac_4, 4)}
|
||||||
|
self.PORT_INFO_INGRESS = {}
|
||||||
|
# PORT_INFO_EGRESS = {"port_id_1": (mac_1, 1),
|
||||||
|
# "port_id_2": (mac_2, 1),
|
||||||
|
# "port_id_3": (mac_3, 1),
|
||||||
|
# "port_id_4": (mac_4, 1)}
|
||||||
|
self.PORT_INFO_EGRESS = {}
|
||||||
|
|
||||||
def _init_max_meter_id(self):
|
def _init_max_meter_id(self):
|
||||||
self.max_meter = 0
|
|
||||||
features = self.br_int.list_meter_features()
|
features = self.br_int.list_meter_features()
|
||||||
for f in features:
|
for f in features:
|
||||||
if f["max_meter"] > 0:
|
if f["max_meter"] > 0:
|
||||||
@ -58,20 +103,20 @@ class MeterRuleManager(object):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def get_data_key(self, port_id, direction):
|
def get_data_key(self, port_id, direction):
|
||||||
return "%s_%s" % (port_id, direction)
|
return "%s_%s_%s" % (self.rule_type, port_id, direction)
|
||||||
|
|
||||||
def load_port_meter_id(self, port_name, port_id, direction):
|
def load_port_meter_id(self, port_name, port_id, direction):
|
||||||
key = self.get_data_key(port_id, direction)
|
key = self.get_data_key(port_id, direction)
|
||||||
meter_id = self.br_int.get_value_from_other_config(
|
try:
|
||||||
port_name, key, value_type=int)
|
meter_id = self.br_int.get_value_from_other_config(
|
||||||
if meter_id:
|
port_name, key, value_type=int)
|
||||||
self.PORT_METER_ID[key] = meter_id
|
self.generator.set_meter_id(key, meter_id)
|
||||||
else:
|
return meter_id
|
||||||
LOG.warning("Failed to load port %(port)s meter id in "
|
except Exception:
|
||||||
|
LOG.warning("Failed to load port $(port)s meter id in "
|
||||||
"direction %(direction)s",
|
"direction %(direction)s",
|
||||||
{"direction": direction,
|
{"direction": direction,
|
||||||
"port": port_id})
|
"port": port_id})
|
||||||
return meter_id
|
|
||||||
|
|
||||||
def store_port_meter_id_to_ovsdb(self, port_name, port_id,
|
def store_port_meter_id_to_ovsdb(self, port_name, port_id,
|
||||||
direction, meter_id):
|
direction, meter_id):
|
||||||
@ -84,32 +129,13 @@ class MeterRuleManager(object):
|
|||||||
self.br_int.remove_value_from_other_config(
|
self.br_int.remove_value_from_other_config(
|
||||||
port_name, key)
|
port_name, key)
|
||||||
|
|
||||||
def generate_meter_id(self):
|
|
||||||
if self.max_meter <= 0:
|
|
||||||
return
|
|
||||||
used_meter_ids = self.PORT_METER_ID.values()
|
|
||||||
cid = None
|
|
||||||
times = 0
|
|
||||||
while not cid or cid in used_meter_ids:
|
|
||||||
cid = random.randint(1, self.max_meter)
|
|
||||||
times += 1
|
|
||||||
if times >= MAX_RETIES:
|
|
||||||
LOG.warning("Failed to allocate meter "
|
|
||||||
"id after %d retries", times)
|
|
||||||
return
|
|
||||||
return cid
|
|
||||||
|
|
||||||
def allocate_meter_id(self, port_id, direction):
|
def allocate_meter_id(self, port_id, direction):
|
||||||
meter_id = self.generate_meter_id()
|
|
||||||
if not meter_id:
|
|
||||||
return
|
|
||||||
key = self.get_data_key(port_id, direction)
|
key = self.get_data_key(port_id, direction)
|
||||||
self.PORT_METER_ID[key] = meter_id
|
return self.generator.allocate_meter_id(key)
|
||||||
return meter_id
|
|
||||||
|
|
||||||
def remove_port_meter_id(self, port_id, direction):
|
def remove_port_meter_id(self, port_id, direction):
|
||||||
key = self.get_data_key(port_id, direction)
|
key = self.get_data_key(port_id, direction)
|
||||||
return self.PORT_METER_ID.pop(key, None)
|
return self.generator.remove_port_meter_id(key)
|
||||||
|
|
||||||
def set_port_info_ingress(self, port_id, port_name, mac, vlan):
|
def set_port_info_ingress(self, port_id, port_name, mac, vlan):
|
||||||
self.PORT_INFO_INGRESS[port_id] = (port_name, mac, vlan)
|
self.PORT_INFO_INGRESS[port_id] = (port_name, mac, vlan)
|
||||||
|
@ -21,6 +21,7 @@ from oslo_log import log as logging
|
|||||||
from neutron.agent.common import async_process
|
from neutron.agent.common import async_process
|
||||||
from neutron.agent.linux import iptables_manager
|
from neutron.agent.linux import iptables_manager
|
||||||
from neutron.common import utils as common_utils
|
from neutron.common import utils as common_utils
|
||||||
|
from neutron.plugins.ml2.common import constants as comm_consts
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -87,11 +88,12 @@ def wait_until_dscp_marking_rule_applied_ovs(bridge, port_vif, rule):
|
|||||||
|
|
||||||
|
|
||||||
def wait_until_pkt_meter_rule_applied_ovs(bridge, port_vif, port_id,
|
def wait_until_pkt_meter_rule_applied_ovs(bridge, port_vif, port_id,
|
||||||
direction, mac=None):
|
direction, mac=None,
|
||||||
|
type_=comm_consts.METER_FLAG_PPS):
|
||||||
def _pkt_rate_limit_rule_applied():
|
def _pkt_rate_limit_rule_applied():
|
||||||
port_num = bridge.get_port_ofport(port_vif)
|
port_num = bridge.get_port_ofport(port_vif)
|
||||||
port_vlan = bridge.get_port_tag_by_name(port_vif)
|
port_vlan = bridge.get_port_tag_by_name(port_vif)
|
||||||
key = "%s_%s" % (port_id, direction)
|
key = "%s_%s_%s" % (type_, port_id, direction)
|
||||||
meter_id = bridge.get_value_from_other_config(
|
meter_id = bridge.get_value_from_other_config(
|
||||||
port_vif, key, value_type=int)
|
port_vif, key, value_type=int)
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from oslo_utils import uuidutils
|
|||||||
|
|
||||||
from neutron.objects.qos import policy
|
from neutron.objects.qos import policy
|
||||||
from neutron.objects.qos import rule
|
from neutron.objects.qos import rule
|
||||||
|
from neutron.plugins.ml2.common import constants as comm_consts
|
||||||
from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
from neutron.plugins.ml2.drivers.openvswitch.agent import (
|
||||||
ovs_agent_extension_api as ovs_ext_api)
|
ovs_agent_extension_api as ovs_ext_api)
|
||||||
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import (
|
from neutron.plugins.ml2.drivers.openvswitch.agent.extension_drivers import (
|
||||||
@ -243,6 +244,90 @@ class QosOVSAgentDriverTestCase(ovs_test_base.OVSAgentConfigTestBase):
|
|||||||
self.delete_meter.assert_not_called()
|
self.delete_meter.assert_not_called()
|
||||||
self.remove_meter_from_port.assert_not_called()
|
self.remove_meter_from_port.assert_not_called()
|
||||||
|
|
||||||
|
def test_meter_manager_allocate_meter_id(self):
|
||||||
|
meter_cache_pps = qos_driver.MeterRuleManager(mock.Mock())
|
||||||
|
meter_cache_pps.generator.max_meter = 10000
|
||||||
|
meter_cache_bps = qos_driver.MeterRuleManager(
|
||||||
|
mock.Mock(), type_=comm_consts.METER_FLAG_BPS)
|
||||||
|
meter_cache_bps.generator.max_meter = 10000
|
||||||
|
meter_cache_pps.allocate_meter_id("1", "ingress")
|
||||||
|
meter_cache_pps.allocate_meter_id("1", "egress")
|
||||||
|
meter_cache_bps.allocate_meter_id("1", "ingress")
|
||||||
|
meter_cache_bps.allocate_meter_id("1", "egress")
|
||||||
|
meter_cache_pps.allocate_meter_id("2", "ingress")
|
||||||
|
meter_cache_pps.allocate_meter_id("2", "egress")
|
||||||
|
meter_cache_bps.allocate_meter_id("2", "ingress")
|
||||||
|
meter_cache_bps.allocate_meter_id("2", "egress")
|
||||||
|
self.assertEqual(
|
||||||
|
meter_cache_pps.generator.PORT_METER_ID,
|
||||||
|
meter_cache_bps.generator.PORT_METER_ID)
|
||||||
|
pps_keys = meter_cache_pps.generator.PORT_METER_ID.keys()
|
||||||
|
bps_keys = meter_cache_bps.generator.PORT_METER_ID.keys()
|
||||||
|
self.assertEqual(
|
||||||
|
2, len([k for k in pps_keys if k.startswith('pps_1')]))
|
||||||
|
self.assertEqual(
|
||||||
|
2, len([k for k in bps_keys if k.startswith('bps_1')]))
|
||||||
|
self.assertEqual(
|
||||||
|
2, len([k for k in pps_keys if k.startswith('pps_2')]))
|
||||||
|
self.assertEqual(
|
||||||
|
2, len([k for k in bps_keys if k.startswith('bps_2')]))
|
||||||
|
self.assertEqual(
|
||||||
|
meter_cache_pps.generator.PORT_METER_ID.keys(),
|
||||||
|
meter_cache_bps.generator.PORT_METER_ID.keys())
|
||||||
|
pps_values = list(meter_cache_pps.generator.PORT_METER_ID.values())
|
||||||
|
bps_values = list(meter_cache_bps.generator.PORT_METER_ID.values())
|
||||||
|
self.assertEqual(pps_values, bps_values)
|
||||||
|
except_keys = ["pps_1_ingress", "pps_1_egress",
|
||||||
|
"bps_1_ingress", "bps_1_egress",
|
||||||
|
"pps_2_ingress", "pps_2_egress",
|
||||||
|
"bps_2_ingress", "bps_2_egress"]
|
||||||
|
except_values = []
|
||||||
|
for key in except_keys:
|
||||||
|
value = meter_cache_bps.generator.PORT_METER_ID.get(key)
|
||||||
|
if value:
|
||||||
|
except_values.append(value)
|
||||||
|
self.assertEqual(8, len(set(except_values)))
|
||||||
|
|
||||||
|
def test_meter_manager_remove_port_meter_id(self):
|
||||||
|
meter_cache_pps = qos_driver.MeterRuleManager(mock.Mock())
|
||||||
|
meter_cache_pps.generator.max_meter = 10000
|
||||||
|
meter_cache_bps = qos_driver.MeterRuleManager(
|
||||||
|
mock.Mock(), type_=comm_consts.METER_FLAG_BPS)
|
||||||
|
meter_cache_bps.generator.max_meter = 10000
|
||||||
|
meter_cache_pps.allocate_meter_id("1", "ingress")
|
||||||
|
meter_cache_pps.allocate_meter_id("1", "egress")
|
||||||
|
meter_cache_bps.allocate_meter_id("1", "ingress")
|
||||||
|
meter_cache_bps.allocate_meter_id("1", "egress")
|
||||||
|
meter_cache_pps.allocate_meter_id("2", "ingress")
|
||||||
|
meter_cache_pps.allocate_meter_id("2", "egress")
|
||||||
|
meter_cache_bps.allocate_meter_id("2", "ingress")
|
||||||
|
meter_cache_bps.allocate_meter_id("2", "egress")
|
||||||
|
self.assertEqual(
|
||||||
|
meter_cache_pps.generator.PORT_METER_ID,
|
||||||
|
meter_cache_bps.generator.PORT_METER_ID)
|
||||||
|
|
||||||
|
meter_cache_bps.remove_port_meter_id("2", "ingress")
|
||||||
|
meter_cache_pps.remove_port_meter_id("1", "egress")
|
||||||
|
|
||||||
|
self.assertNotIn(
|
||||||
|
"pps_1_egress", meter_cache_pps.generator.PORT_METER_ID.keys())
|
||||||
|
self.assertNotIn(
|
||||||
|
"bps_2_ingress", meter_cache_pps.generator.PORT_METER_ID.keys())
|
||||||
|
|
||||||
|
pps_values = list(meter_cache_pps.generator.PORT_METER_ID.values())
|
||||||
|
bps_values = list(meter_cache_bps.generator.PORT_METER_ID.values())
|
||||||
|
self.assertEqual(pps_values, bps_values)
|
||||||
|
except_keys = ["pps_1_ingress",
|
||||||
|
"bps_1_ingress", "bps_1_egress",
|
||||||
|
"pps_2_ingress", "pps_2_egress",
|
||||||
|
"bps_2_egress"]
|
||||||
|
except_values = []
|
||||||
|
for key in except_keys:
|
||||||
|
value = meter_cache_bps.generator.PORT_METER_ID.get(key)
|
||||||
|
if value:
|
||||||
|
except_values.append(value)
|
||||||
|
self.assertEqual(6, len(set(except_values)))
|
||||||
|
|
||||||
def _assert_rules_create_updated(self):
|
def _assert_rules_create_updated(self):
|
||||||
self.create_egress.assert_called_once_with(
|
self.create_egress.assert_called_once_with(
|
||||||
self.port_name, self.rules[0].max_kbps,
|
self.port_name, self.rules[0].max_kbps,
|
||||||
|
Loading…
Reference in New Issue
Block a user