Allow replacing the QoS policy of bound port
Change-Id: Iebdfd2b303f47ff9f049cf583ea754b35ca26f78 Related-Bug: #1882804 Depends-On: https://review.opendev.org/748279
This commit is contained in:
parent
69bedc9beb
commit
87e5131432
@ -61,6 +61,26 @@ Limitations
|
|||||||
effect. That is ports of the QoS policy are not yet used by Nova. Requests
|
effect. That is ports of the QoS policy are not yet used by Nova. Requests
|
||||||
to change guarantees of in-use policies are rejected.
|
to change guarantees of in-use policies are rejected.
|
||||||
|
|
||||||
|
* Changing the QoS policy of the port with new ``minimum_bandwidth`` rules
|
||||||
|
changes placement ``allocations`` from Victoria release.
|
||||||
|
If the VM was booted with port without QoS policy and ``minimum_bandwidth``
|
||||||
|
rules the port update succeeds but placement allocations will not change.
|
||||||
|
The same is true if the port has no ``binding:profile``, thus no placement
|
||||||
|
allocation record exists for it. But if the VM was booted with a port with
|
||||||
|
QoS policy and ``minimum_bandwidth`` rules the update is possible and the
|
||||||
|
allocations are changed in placement as well.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
As it is possible to update a port to remove the QoS policy, updating it
|
||||||
|
back to have QoS policy with ``minimum_bandwidth`` rule will not result in
|
||||||
|
``placement allocation`` record, only the dataplane enforcement will happen.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
updating the ``minimum_bandwidth`` rule of a QoS policy that is attached
|
||||||
|
to a port which is bound to a VM is still not possible.
|
||||||
|
|
||||||
* The first data-plane-only Guaranteed Minimum Bandwidth implementation
|
* The first data-plane-only Guaranteed Minimum Bandwidth implementation
|
||||||
(for SR-IOV egress traffic) was released in the Newton
|
(for SR-IOV egress traffic) was released in the Newton
|
||||||
release of Neutron. Because of the known lack of
|
release of Neutron. Because of the known lack of
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
from neutron_lib.api.definitions import port as port_def
|
from neutron_lib.api.definitions import port as port_def
|
||||||
from neutron_lib.api.definitions import port_resource_request
|
from neutron_lib.api.definitions import port_resource_request
|
||||||
from neutron_lib.api.definitions import portbindings
|
from neutron_lib.api.definitions import portbindings
|
||||||
@ -32,9 +33,12 @@ from neutron_lib.db import api as db_api
|
|||||||
from neutron_lib.db import resource_extend
|
from neutron_lib.db import resource_extend
|
||||||
from neutron_lib import exceptions as lib_exc
|
from neutron_lib import exceptions as lib_exc
|
||||||
from neutron_lib.exceptions import qos as qos_exc
|
from neutron_lib.exceptions import qos as qos_exc
|
||||||
|
from neutron_lib.placement import client as pl_client
|
||||||
from neutron_lib.placement import constants as pl_constants
|
from neutron_lib.placement import constants as pl_constants
|
||||||
from neutron_lib.placement import utils as pl_utils
|
from neutron_lib.placement import utils as pl_utils
|
||||||
from neutron_lib.services.qos import constants as qos_consts
|
from neutron_lib.services.qos import constants as qos_consts
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.db import db_base_plugin_common
|
from neutron.db import db_base_plugin_common
|
||||||
@ -44,10 +48,14 @@ from neutron.objects import network as network_object
|
|||||||
from neutron.objects import ports as ports_object
|
from neutron.objects import ports as ports_object
|
||||||
from neutron.objects.qos import policy as policy_object
|
from neutron.objects.qos import policy as policy_object
|
||||||
from neutron.objects.qos import qos_policy_validator as checker
|
from neutron.objects.qos import qos_policy_validator as checker
|
||||||
|
from neutron.objects.qos import rule as rule_object
|
||||||
from neutron.objects.qos import rule_type as rule_type_object
|
from neutron.objects.qos import rule_type as rule_type_object
|
||||||
from neutron.services.qos.drivers import manager
|
from neutron.services.qos.drivers import manager
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@resource_extend.has_resource_extenders
|
@resource_extend.has_resource_extenders
|
||||||
class QoSPlugin(qos.QoSPluginBase):
|
class QoSPlugin(qos.QoSPluginBase):
|
||||||
"""Implementation of the Neutron QoS Service Plugin.
|
"""Implementation of the Neutron QoS Service Plugin.
|
||||||
@ -72,11 +80,19 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(QoSPlugin, self).__init__()
|
super(QoSPlugin, self).__init__()
|
||||||
self.driver_manager = manager.QosServiceDriverManager()
|
self.driver_manager = manager.QosServiceDriverManager()
|
||||||
|
self._placement_client = pl_client.PlacementAPIClient(cfg.CONF)
|
||||||
|
|
||||||
callbacks_registry.subscribe(
|
callbacks_registry.subscribe(
|
||||||
self._validate_create_port_callback,
|
self._validate_create_port_callback,
|
||||||
callbacks_resources.PORT,
|
callbacks_resources.PORT,
|
||||||
callbacks_events.PRECOMMIT_CREATE)
|
callbacks_events.PRECOMMIT_CREATE)
|
||||||
|
# TODO(lajoskatona): PORT BEFORE_UPDATE is a notify, so
|
||||||
|
# "old style" kwargs instead of payload object, let's change it
|
||||||
|
# to notify and payload.
|
||||||
|
callbacks_registry.subscribe(
|
||||||
|
self._check_port_for_placement_allocation_change,
|
||||||
|
callbacks_resources.PORT,
|
||||||
|
callbacks_events.BEFORE_UPDATE)
|
||||||
callbacks_registry.subscribe(
|
callbacks_registry.subscribe(
|
||||||
self._validate_update_port_callback,
|
self._validate_update_port_callback,
|
||||||
callbacks_resources.PORT,
|
callbacks_resources.PORT,
|
||||||
@ -172,6 +188,81 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||||||
context.elevated(), id=policy_id)
|
context.elevated(), id=policy_id)
|
||||||
self.validate_policy_for_port(context, policy, port)
|
self.validate_policy_for_port(context, policy, port)
|
||||||
|
|
||||||
|
def _check_port_for_placement_allocation_change(self, resource, event,
|
||||||
|
trigger, **kwargs):
|
||||||
|
context = kwargs['context']
|
||||||
|
orig_port = kwargs['original_port']
|
||||||
|
original_policy_id = orig_port.get(qos_consts.QOS_POLICY_ID)
|
||||||
|
policy_id = kwargs['port'].get(qos_consts.QOS_POLICY_ID)
|
||||||
|
|
||||||
|
if policy_id == original_policy_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Do this only for compute bound ports
|
||||||
|
if (nl_constants.DEVICE_OWNER_COMPUTE_PREFIX in
|
||||||
|
orig_port['device_owner']):
|
||||||
|
original_policy = policy_object.QosPolicy.get_object(
|
||||||
|
context.elevated(), id=original_policy_id)
|
||||||
|
policy = policy_object.QosPolicy.get_object(
|
||||||
|
context.elevated(), id=policy_id)
|
||||||
|
self._change_placement_allocation(original_policy, policy,
|
||||||
|
orig_port)
|
||||||
|
|
||||||
|
def _prepare_allocation_needs(self, original_rule, desired_rule):
|
||||||
|
if (isinstance(desired_rule, rule_object.QosMinimumBandwidthRule) or
|
||||||
|
isinstance(desired_rule, dict)):
|
||||||
|
o_dir = original_rule.get('direction')
|
||||||
|
o_minkbps = original_rule.get('min_kbps')
|
||||||
|
d_minkbps = desired_rule.get('min_kbps')
|
||||||
|
d_dir = desired_rule.get('direction')
|
||||||
|
if o_dir == d_dir and o_minkbps != d_minkbps:
|
||||||
|
diff = d_minkbps - o_minkbps
|
||||||
|
# TODO(lajoskatona): move this to neutron-lib, see similar
|
||||||
|
# dict @l125.
|
||||||
|
if d_dir == 'egress':
|
||||||
|
drctn = pl_constants.CLASS_NET_BW_EGRESS_KBPS
|
||||||
|
else:
|
||||||
|
drctn = pl_constants.CLASS_NET_BW_INGRESS_KBPS
|
||||||
|
return {drctn: diff}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _change_placement_allocation(self, original_policy, desired_policy,
|
||||||
|
orig_port):
|
||||||
|
alloc_diff = {}
|
||||||
|
original_rules = []
|
||||||
|
rp_uuid = orig_port['binding:profile'].get('allocation')
|
||||||
|
device_id = orig_port['device_id']
|
||||||
|
if original_policy:
|
||||||
|
original_rules = original_policy.get('rules')
|
||||||
|
if desired_policy:
|
||||||
|
desired_rules = desired_policy.get('rules')
|
||||||
|
else:
|
||||||
|
desired_rules = [{'direction': 'egress', 'min_kbps': 0},
|
||||||
|
{'direction': 'ingress', 'min_kbps': 0}]
|
||||||
|
|
||||||
|
any_rules_minbw = any(
|
||||||
|
[isinstance(r, rule_object.QosMinimumBandwidthRule)
|
||||||
|
for r in desired_rules])
|
||||||
|
if (not original_rules and any_rules_minbw) or not rp_uuid:
|
||||||
|
LOG.warning("There was no QoS policy with minimum_bandwidth rule "
|
||||||
|
"attached to the port %s, there is no allocation "
|
||||||
|
"record in placement for it, only the dataplane "
|
||||||
|
"enforcement will happen!", orig_port['id'])
|
||||||
|
return
|
||||||
|
for o_rule in original_rules:
|
||||||
|
if isinstance(o_rule, rule_object.QosMinimumBandwidthRule):
|
||||||
|
for d_rule in desired_rules:
|
||||||
|
alloc_diff.update(
|
||||||
|
self._prepare_allocation_needs(o_rule, d_rule))
|
||||||
|
if alloc_diff:
|
||||||
|
try:
|
||||||
|
self._placement_client.update_qos_minbw_allocation(
|
||||||
|
consumer_uuid=device_id, minbw_alloc_diff=alloc_diff,
|
||||||
|
rp_uuid=rp_uuid)
|
||||||
|
except ks_exc.Conflict:
|
||||||
|
raise qos_exc.QosPlacementAllocationConflict(
|
||||||
|
consumer=device_id, rp=rp_uuid)
|
||||||
|
|
||||||
def _validate_update_port_callback(self, resource, event, trigger,
|
def _validate_update_port_callback(self, resource, event, trigger,
|
||||||
payload=None):
|
payload=None):
|
||||||
context = payload.context
|
context = payload.context
|
||||||
|
@ -13,12 +13,14 @@
|
|||||||
import copy
|
import copy
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
from keystoneauth1 import exceptions as ks_exc
|
||||||
import netaddr
|
import netaddr
|
||||||
from neutron_lib.api.definitions import qos
|
from neutron_lib.api.definitions import qos
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
from neutron_lib import constants as lib_constants
|
from neutron_lib import constants as lib_constants
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
from neutron_lib import exceptions as lib_exc
|
from neutron_lib import exceptions as lib_exc
|
||||||
|
from neutron_lib.exceptions import placement as pl_exc
|
||||||
from neutron_lib.exceptions import qos as qos_exc
|
from neutron_lib.exceptions import qos as qos_exc
|
||||||
from neutron_lib.objects import utils as obj_utils
|
from neutron_lib.objects import utils as obj_utils
|
||||||
from neutron_lib.placement import constants as pl_constants
|
from neutron_lib.placement import constants as pl_constants
|
||||||
@ -1221,6 +1223,14 @@ class TestQosPluginDB(base.BaseQosTestCase):
|
|||||||
qos_policy.create()
|
qos_policy.create()
|
||||||
return qos_policy
|
return qos_policy
|
||||||
|
|
||||||
|
def _make_qos_minbw_rule(self, policy_id, direction='ingress',
|
||||||
|
min_kbps=1000):
|
||||||
|
qos_rule = rule_object.QosMinimumBandwidthRule(
|
||||||
|
self.context, project_id=self.project_id,
|
||||||
|
qos_policy_id=policy_id, direction=direction, min_kbps=min_kbps)
|
||||||
|
qos_rule.create()
|
||||||
|
return qos_rule
|
||||||
|
|
||||||
def _make_port(self, network_id, qos_policy_id=None):
|
def _make_port(self, network_id, qos_policy_id=None):
|
||||||
base_mac = ['aa', 'bb', 'cc', 'dd', 'ee', 'ff']
|
base_mac = ['aa', 'bb', 'cc', 'dd', 'ee', 'ff']
|
||||||
mac = netaddr.EUI(next(net_utils.random_mac_generator(base_mac)))
|
mac = netaddr.EUI(next(net_utils.random_mac_generator(base_mac)))
|
||||||
@ -1277,3 +1287,204 @@ class TestQosPluginDB(base.BaseQosTestCase):
|
|||||||
|
|
||||||
def test_validate_create_port_callback_no_policy(self):
|
def test_validate_create_port_callback_no_policy(self):
|
||||||
self._test_validate_create_port_callback()
|
self._test_validate_create_port_callback()
|
||||||
|
|
||||||
|
def _prepare_for_port_placement_allocation_change(self, qos1, qos2):
|
||||||
|
qos1_id = qos1.id if qos1 else None
|
||||||
|
qos2_id = qos2.id if qos2 else None
|
||||||
|
|
||||||
|
network = self._make_network()
|
||||||
|
port = self._make_port(network.id, qos_policy_id=qos1_id)
|
||||||
|
|
||||||
|
return {"context": self.context,
|
||||||
|
"original_port": {
|
||||||
|
"id": port.id,
|
||||||
|
"device_owner": "compute:uu:id",
|
||||||
|
"qos_policy_id": qos1_id},
|
||||||
|
"port": {"id": port.id, "qos_policy_id": qos2_id}}
|
||||||
|
|
||||||
|
def test_check_port_for_placement_allocation_change_no_qos_change(self):
|
||||||
|
qos1_obj = self._make_qos_policy()
|
||||||
|
kwargs = self._prepare_for_port_placement_allocation_change(
|
||||||
|
qos1=qos1_obj, qos2=qos1_obj)
|
||||||
|
with mock.patch.object(
|
||||||
|
self.qos_plugin,
|
||||||
|
'_change_placement_allocation') as mock_alloc_change:
|
||||||
|
self.qos_plugin._check_port_for_placement_allocation_change(
|
||||||
|
'PORT', 'before_update', 'test_plugin', **kwargs)
|
||||||
|
mock_alloc_change.assert_not_called()
|
||||||
|
|
||||||
|
def test_check_port_for_placement_allocation_change(self):
|
||||||
|
qos1_obj = self._make_qos_policy()
|
||||||
|
qos2_obj = self._make_qos_policy()
|
||||||
|
kwargs = self._prepare_for_port_placement_allocation_change(
|
||||||
|
qos1=qos1_obj, qos2=qos2_obj)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
self.qos_plugin,
|
||||||
|
'_change_placement_allocation') as mock_alloc_change:
|
||||||
|
self.qos_plugin._check_port_for_placement_allocation_change(
|
||||||
|
'PORT', 'before_update', 'test_plugin', **kwargs)
|
||||||
|
mock_alloc_change.assert_called_once_with(
|
||||||
|
qos1_obj, qos2_obj, kwargs['original_port'])
|
||||||
|
|
||||||
|
def test_check_port_for_placement_allocation_change_no_new_policy(self):
|
||||||
|
qos1_obj = self._make_qos_policy()
|
||||||
|
kwargs = self._prepare_for_port_placement_allocation_change(
|
||||||
|
qos1=qos1_obj, qos2=None)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
self.qos_plugin,
|
||||||
|
'_change_placement_allocation') as mock_alloc_change:
|
||||||
|
self.qos_plugin._check_port_for_placement_allocation_change(
|
||||||
|
'PORT', 'before_update', 'test_plugin', **kwargs)
|
||||||
|
mock_alloc_change.assert_called_once_with(
|
||||||
|
qos1_obj, None, kwargs['original_port'])
|
||||||
|
|
||||||
|
def _prepare_port_for_placement_allocation(self, qos1, qos2=None,
|
||||||
|
min_kbps1=1000, min_kbps2=2000):
|
||||||
|
rule1_obj = self._make_qos_minbw_rule(qos1.id, min_kbps=min_kbps1)
|
||||||
|
qos1.rules = [rule1_obj]
|
||||||
|
if qos2:
|
||||||
|
rule2_obj = self._make_qos_minbw_rule(qos2.id, min_kbps=min_kbps2)
|
||||||
|
qos2.rules = [rule2_obj]
|
||||||
|
orig_port = {'binding:profile': {'allocation': 'rp:uu:id'},
|
||||||
|
'device_id': 'uu:id'}
|
||||||
|
return orig_port
|
||||||
|
|
||||||
|
def test_change_placement_allocation_increase(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1, qos2)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, qos2, port)
|
||||||
|
mock_update_qos_alloc.assert_called_once_with(
|
||||||
|
consumer_uuid='uu:id',
|
||||||
|
minbw_alloc_diff={'NET_BW_IGR_KILOBIT_PER_SEC': 1000},
|
||||||
|
rp_uuid='rp:uu:id')
|
||||||
|
|
||||||
|
def test_test_change_placement_allocation_decrease(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos2, qos1)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, qos2, port)
|
||||||
|
mock_update_qos_alloc.assert_called_once_with(
|
||||||
|
consumer_uuid='uu:id',
|
||||||
|
minbw_alloc_diff={'NET_BW_IGR_KILOBIT_PER_SEC': -1000},
|
||||||
|
rp_uuid='rp:uu:id')
|
||||||
|
|
||||||
|
def test_change_placement_allocation_no_original_qos(self):
|
||||||
|
qos1 = None
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
rule2_obj = self._make_qos_minbw_rule(qos2.id, min_kbps=1000)
|
||||||
|
qos2.rules = [rule2_obj]
|
||||||
|
orig_port = {'id': 'u:u', 'device_id': 'i:d', 'binding:profile': {}}
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(
|
||||||
|
qos1, qos2, orig_port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_no_original_allocation(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
rule1_obj = self._make_qos_minbw_rule(qos1.id, min_kbps=500)
|
||||||
|
qos1.rules = [rule1_obj]
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
rule2_obj = self._make_qos_minbw_rule(qos2.id, min_kbps=1000)
|
||||||
|
qos2.rules = [rule2_obj]
|
||||||
|
orig_port = {'id': 'u:u', 'device_id': 'i:d', 'binding:profile': {}}
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(
|
||||||
|
qos1, qos2, orig_port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_new_policy_empty(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, None, port)
|
||||||
|
mock_update_qos_alloc.assert_called_once_with(
|
||||||
|
consumer_uuid='uu:id',
|
||||||
|
minbw_alloc_diff={'NET_BW_IGR_KILOBIT_PER_SEC': -1000},
|
||||||
|
rp_uuid='rp:uu:id')
|
||||||
|
|
||||||
|
def test_change_placement_allocation_no_min_bw(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
bw_limit_rule1 = rule_object.QosDscpMarkingRule(dscp_mark=16)
|
||||||
|
bw_limit_rule2 = rule_object.QosDscpMarkingRule(dscp_mark=18)
|
||||||
|
qos1.rules = [bw_limit_rule1]
|
||||||
|
qos2.rules = [bw_limit_rule2]
|
||||||
|
port = {'binding:profile': {'allocation': 'rp:uu:id'},
|
||||||
|
'device_id': 'uu:id'}
|
||||||
|
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, None, port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_old_rule_not_min_bw(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
bw_limit_rule = rule_object.QosDscpMarkingRule(dscp_mark=16)
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1, qos2)
|
||||||
|
qos1.rules = [bw_limit_rule]
|
||||||
|
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, qos2, port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_new_rule_not_min_bw(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
bw_limit_rule = rule_object.QosDscpMarkingRule(dscp_mark=16)
|
||||||
|
qos2.rules = [bw_limit_rule]
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1)
|
||||||
|
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, qos2, port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_equal_minkbps(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1, qos2, 1000,
|
||||||
|
1000)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
self.qos_plugin._change_placement_allocation(qos1, qos2, port)
|
||||||
|
mock_update_qos_alloc.assert_not_called()
|
||||||
|
|
||||||
|
def test_change_placement_allocation_update_conflict(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1, qos2)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
mock_update_qos_alloc.side_effect = ks_exc.Conflict(
|
||||||
|
response={'errors': [{'code': 'placement.concurrent_update'}]}
|
||||||
|
)
|
||||||
|
self.assertRaises(
|
||||||
|
qos_exc.QosPlacementAllocationConflict,
|
||||||
|
self.qos_plugin._change_placement_allocation,
|
||||||
|
qos1, qos2, port)
|
||||||
|
|
||||||
|
def test_change_placement_allocation_update_generation_conflict(self):
|
||||||
|
qos1 = self._make_qos_policy()
|
||||||
|
qos2 = self._make_qos_policy()
|
||||||
|
port = self._prepare_port_for_placement_allocation(qos1, qos2)
|
||||||
|
with mock.patch.object(self.qos_plugin._placement_client,
|
||||||
|
'update_qos_minbw_allocation') as mock_update_qos_alloc:
|
||||||
|
mock_update_qos_alloc.side_effect = (
|
||||||
|
pl_exc.PlacementAllocationGenerationConflict(
|
||||||
|
consumer='rp:uu:id'))
|
||||||
|
self.assertRaises(
|
||||||
|
pl_exc.PlacementAllocationGenerationConflict,
|
||||||
|
self.qos_plugin._change_placement_allocation,
|
||||||
|
qos1, qos2, port)
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Update of an ``already bound port`` with ``QoS minimum_bandwidth`` rule
|
||||||
|
with new ``QoS policy`` with ``minimum_bandwidth`` rule changes the
|
||||||
|
allocations in placement as well. ``NOTE``: updating the
|
||||||
|
``minimum_bandwidth`` rule of a QoS policy that is attached to a port
|
||||||
|
which is bound to a VM is still not possible.
|
Loading…
Reference in New Issue
Block a user