NSX|v QoS DSCP marking support

Adding support for the QoS DSCP marking rules for networks, and creating
a matching filter policy on the dvs

Change-Id: I56418ddd9ebbbed9576d41e8fd915b5b8c570283
This commit is contained in:
Adit Sarfaty 2016-04-25 13:06:40 +03:00
parent e430c32db0
commit 6ebc74622a
4 changed files with 161 additions and 19 deletions

View File

@ -23,6 +23,8 @@ from vmware_nsx.dvs import dvs_utils
LOG = logging.getLogger(__name__)
PORTGROUP_PREFIX = 'dvportgroup'
QOS_OUT_DIRECTION = 'outgoingPackets'
QOS_AGENT_NAME = 'dvfilter-generic-vmware'
class DvsManager(object):
@ -139,8 +141,10 @@ class DvsManager(object):
return pg_spec
def update_port_group_spec_qos(self, pg_spec, qos_data):
outPol = pg_spec.defaultPortConfig.outShapingPolicy
if qos_data.enabled:
port_conf = pg_spec.defaultPortConfig
# Update the out bandwidth shaping policy
outPol = port_conf.outShapingPolicy
if qos_data.bandwidthEnabled:
outPol.inherited = False
outPol.enabled.inherited = False
outPol.enabled.value = True
@ -153,6 +157,46 @@ class DvsManager(object):
else:
outPol.inherited = True
# Update the DSCP marking
if (port_conf.filterPolicy.inherited or
len(port_conf.filterPolicy.filterConfig) == 0 or
len(port_conf.filterPolicy.filterConfig[
0].trafficRuleset.rules) == 0):
if qos_data.dscpMarkEnabled:
# create the entire structure
client_factory = self._session.vim.client.factory
filter_rule = client_factory.create('ns0:DvsTrafficRule')
filter_rule.action = client_factory.create(
'ns0:DvsUpdateTagNetworkRuleAction')
filter_rule.action.dscpTag = qos_data.dscpMarkValue
# mark only outgoing packets
filter_rule.direction = QOS_OUT_DIRECTION
traffic_filter_config = client_factory.create(
'ns0:DvsTrafficFilterConfig')
traffic_filter_config.trafficRuleset.rules = [filter_rule]
traffic_filter_config.trafficRuleset.enabled = True
traffic_filter_config.agentName = QOS_AGENT_NAME
traffic_filter_config.inherited = False
port_conf.filterPolicy = client_factory.create(
'ns0:DvsFilterPolicy')
port_conf.filterPolicy.filterConfig = [
traffic_filter_config]
port_conf.filterPolicy.inherited = False
else:
# The structure was already initialized
filter_policy = port_conf.filterPolicy
if qos_data.dscpMarkEnabled:
# just update the DSCP value
traffic_filter_config = filter_policy.filterConfig[0]
filter_rule = traffic_filter_config.trafficRuleset.rules[0]
filter_rule.action.dscpTag = qos_data.dscpMarkValue
else:
# delete the filter policy data
filter_policy.filterConfig = []
def _reconfigure_port_group(self, pg_moref, spec_update_calback,
spec_update_data):
# Get the current configuration of the port group

View File

@ -141,6 +141,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
"advanced-service-providers",
"subnet_allocation"]
supported_qos_rule_types = [qos_consts.RULE_TYPE_BANDWIDTH_LIMIT,
qos_consts.RULE_TYPE_DSCP_MARK]
__native_bulk_support = True
__native_pagination_support = True
__native_sorting_support = True

View File

@ -32,14 +32,19 @@ class NsxVQosRule(object):
def __init__(self, context=None, qos_policy_id=None):
super(NsxVQosRule, self).__init__()
# Data structure to hold the NSX-V representation
# of the neutron qos rule.
self._qos_plugin = None
self.enabled = False
# Data structure to hold the NSX-V representation
# of the neutron QoS Bandwidth rule
self.bandwidthEnabled = False
self.averageBandwidth = 0
self.peakBandwidth = 0
self.burstSize = 0
# And data for the DSCP marking rule
self.dscpMarkEnabled = False
self.dscpMarkValue = 0
if qos_policy_id is not None:
self._init_from_policy_id(context, qos_policy_id)
@ -51,16 +56,18 @@ class NsxVQosRule(object):
# init the nsx_v qos data (outShapingPolicy) from a neutron qos policy
def _init_from_policy_id(self, context, qos_policy_id):
self.enabled = False
self.bandwidthEnabled = False
self.dscpMarkEnabled = False
# read the neutron policy restrictions
if qos_policy_id is not None:
# read the QOS rule from DB
plugin = self._get_qos_plugin()
# read the QoS BW rule from DB
rules_obj = plugin.get_policy_bandwidth_limit_rules(
context, qos_policy_id)
if rules_obj is not None and len(rules_obj) > 0:
rule_obj = rules_obj[0]
self.enabled = True
rule_obj = rules_obj[0] # neutron supports only 1 rule for now
self.bandwidthEnabled = True
# averageBandwidth: kbps (neutron) -> bps (nsxv)
self.averageBandwidth = rule_obj['max_kbps'] * 1024
# peakBandwidth: the same as the average value because the
@ -68,6 +75,15 @@ class NsxVQosRule(object):
self.peakBandwidth = self.averageBandwidth
# burstSize: kbps (neutron) -> Bytes (nsxv)
self.burstSize = rule_obj['max_burst_kbps'] * 128
# read the QoS DSCP marking rule from DB
rules_obj = plugin.get_policy_dscp_marking_rules(
context, qos_policy_id)
if rules_obj is not None and len(rules_obj) > 0:
rule_obj = rules_obj[0] # neutron supports only 1 rule for now
self.dscpMarkEnabled = True
self.dscpMarkValue = rule_obj['dscp_mark']
return self

View File

@ -72,12 +72,17 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
'bandwidth_limit_rule': {'id': uuidutils.generate_uuid(),
'max_kbps': 100,
'max_burst_kbps': 150}}
self.dscp_rule_data = {
'dscp_marking_rule': {'id': uuidutils.generate_uuid(),
'dscp_mark': 22}}
self.policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
self.rule = rule_object.QosBandwidthLimitRule(
self.ctxt, **self.rule_data['bandwidth_limit_rule'])
self.dscp_rule = rule_object.QosDscpMarkingRule(
self.ctxt, **self.dscp_rule_data['dscp_marking_rule'])
self._net_data = {'network': {
'name': 'test-qos',
@ -88,6 +93,7 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
'shared': False
}}
self._rules = [self.rule_data['bandwidth_limit_rule']]
self._dscp_rules = [self.dscp_rule_data['dscp_marking_rule']]
mock.patch('neutron.objects.db.api.create_object').start()
mock.patch('neutron.objects.db.api.update_object').start()
@ -117,21 +123,25 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
# Create a policy with a rule
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
setattr(_policy, "rules", [self.rule])
setattr(_policy, "rules", [self.rule, self.dscp_rule])
with mock.patch('neutron.services.qos.qos_plugin.QoSPlugin.'
'get_policy_bandwidth_limit_rules',
return_value=self._rules) as get_rules_mock:
# create the network to use this policy
net = self._create_net()
with mock.patch('neutron.services.qos.qos_plugin.QoSPlugin.'
'get_policy_dscp_marking_rules',
return_value=self._dscp_rules) as get_dscp_mock:
# create the network to use this policy
net = self._create_net()
# make sure the network-policy binding was updated
update_bindings_mock.assert_called_once_with(
self.ctxt, net['id'], self.policy.id)
# make sure the qos rule was found
get_rules_mock.assert_called_with(self.ctxt, self.policy.id)
# make sure the dvs was updated
self.assertTrue(dvs_update_mock.called)
# make sure the network-policy binding was updated
update_bindings_mock.assert_called_once_with(
self.ctxt, net['id'], self.policy.id)
# make sure the qos rule was found
get_rules_mock.assert_called_with(self.ctxt, self.policy.id)
get_dscp_mock.assert_called_with(self.ctxt, self.policy.id)
# make sure the dvs was updated
self.assertTrue(dvs_update_mock.called)
def _test_rule_action_notification(self, action):
with mock.patch.object(qos_com_utils, 'update_network_policy_binding'):
@ -189,3 +199,72 @@ class TestQosNsxVNotification(test_plugin.NsxVPluginV2TestCase,
is deleted
"""
self._test_rule_action_notification('delete')
def _test_dscp_rule_action_notification(self, action):
with mock.patch.object(qos_com_utils, 'update_network_policy_binding'):
with mock.patch.object(dvs.DvsManager,
'update_port_groups_config') as dvs_mock:
# Create a policy with a rule
_policy = policy_object.QosPolicy(
self.ctxt, **self.policy_data['policy'])
# set the rule in the policy data
if action != 'create':
setattr(_policy, "rules", [self.dscp_rule])
plugin = self.qos_plugin
with mock.patch('neutron.services.qos.qos_plugin.QoSPlugin.'
'get_policy_dscp_marking_rules',
return_value=self._dscp_rules) as rules_mock:
with mock.patch('neutron.objects.qos.policy.'
'QosPolicy.get_object',
return_value=_policy):
# create the network to use this policy
self._create_net()
# create/update/delete the rule
if action == 'create':
with mock.patch('neutron.objects.db.api.'
'create_object',
return_value=self.dscp_rule_data):
plugin.create_policy_dscp_marking_rule(
self.ctxt,
self.policy.id,
self.dscp_rule_data)
elif action == 'update':
with mock.patch('neutron.objects.db.api.'
'update_object',
return_value=self.dscp_rule_data):
plugin.update_policy_dscp_marking_rule(
self.ctxt,
self.dscp_rule.id,
self.policy.id,
self.dscp_rule_data)
else:
plugin.delete_policy_dscp_marking_rule(
self.ctxt,
self.dscp_rule.id,
self.policy.id)
# make sure the qos rule was found
self.assertTrue(rules_mock.called)
# make sure the dvs was updated
self.assertTrue(dvs_mock.called)
def test_create_dscp_rule_notification(self):
"""Test the DVS update when a QoS DSCP rule, attached to a network,
is created
"""
self._test_dscp_rule_action_notification('create')
def test_update_dscp_rule_notification(self):
"""Test the DVS update when a QoS DSCP rule, attached to a network,
is modified
"""
self._test_dscp_rule_action_notification('update')
def test_delete_dscp_rule_notification(self):
"""Test the DVS update when a QoS DSCP rule, attached to a network,
is deleted
"""
self._test_dscp_rule_action_notification('delete')