Fill port-resource-request
port-resource-request is an admin-only, read-only attribute for neutron ports. The content of this field is filled from the QoS minimum bandwidth rule attached to the port. Change-Id: Ic9862a1b5e24eb798d67823ac3bacab9e54420d8 Partial-Bug: #1578989 See-Also: https://review.openstack.org/502306 (nova spec) See-Also: https://review.openstack.org/508149 (neutron spec)
This commit is contained in:
parent
7bb0b84151
commit
e78f82e64f
|
@ -106,6 +106,10 @@ rules = [
|
|||
'get_port:binding:profile',
|
||||
'rule:admin_only',
|
||||
description='Access rule for getting binding profile of port'),
|
||||
policy.RuleDefault(
|
||||
'get_port:resource_request',
|
||||
'rule:admin_only',
|
||||
description='Access rule for getting resource request of port'),
|
||||
# TODO(amotoki): Add get_port:binding:vnic_type
|
||||
# TODO(amotoki): Add get_port:binding:data_plane_status
|
||||
|
||||
|
|
|
@ -13,16 +13,24 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
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 portbindings
|
||||
from neutron_lib.api.definitions import qos as qos_apidef
|
||||
from neutron_lib.api.definitions import qos_bw_minimum_ingress
|
||||
from neutron_lib.callbacks import events as callbacks_events
|
||||
from neutron_lib.callbacks import registry as callbacks_registry
|
||||
from neutron_lib.callbacks import resources as callbacks_resources
|
||||
from neutron_lib import constants as nl_constants
|
||||
from neutron_lib import context
|
||||
from neutron_lib.db import api as db_api
|
||||
from neutron_lib import exceptions as lib_exc
|
||||
from neutron_lib.placement import constants as pl_constants
|
||||
from neutron_lib.placement import utils as pl_utils
|
||||
from neutron_lib.services.qos import constants as qos_consts
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.db import _resource_extend as resource_extend
|
||||
from neutron.db import db_base_plugin_common
|
||||
from neutron.extensions import qos
|
||||
from neutron.objects import base as base_obj
|
||||
|
@ -34,6 +42,7 @@ from neutron.objects.qos import rule_type as rule_type_object
|
|||
from neutron.services.qos.drivers import manager
|
||||
|
||||
|
||||
@resource_extend.has_resource_extenders
|
||||
class QoSPlugin(qos.QoSPluginBase):
|
||||
"""Implementation of the Neutron QoS Service Plugin.
|
||||
|
||||
|
@ -45,6 +54,7 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||
'qos-bw-limit-direction',
|
||||
'qos-default',
|
||||
'qos-rule-type-details',
|
||||
port_resource_request.ALIAS,
|
||||
qos_bw_minimum_ingress.ALIAS]
|
||||
|
||||
__native_pagination_support = True
|
||||
|
@ -68,6 +78,69 @@ class QoSPlugin(qos.QoSPluginBase):
|
|||
callbacks_resources.NETWORK,
|
||||
callbacks_events.PRECOMMIT_UPDATE)
|
||||
|
||||
@staticmethod
|
||||
@resource_extend.extends([port_def.COLLECTION_NAME])
|
||||
def _extend_port_resource_request(port_res, port_db):
|
||||
"""Add resource request to a port."""
|
||||
port_res['resource_request'] = None
|
||||
qos_policy = policy_object.QosPolicy.get_port_policy(
|
||||
context.get_admin_context(), port_res['id'])
|
||||
# Note(lajoskatona): QosPolicyPortBinding is not ready for some
|
||||
# reasons, so let's try and fetch the QoS policy directly if there is a
|
||||
# qos_policy_id in port_res.
|
||||
if (not qos_policy and 'qos_policy_id' in port_res and
|
||||
port_res['qos_policy_id']):
|
||||
qos_policy = policy_object.QosPolicy.get_policy_obj(
|
||||
context.get_admin_context(), port_res['qos_policy_id']
|
||||
)
|
||||
|
||||
# Note(lajoskatona): handle the case when the port inherits qos-policy
|
||||
# from the network.
|
||||
if not qos_policy:
|
||||
net = network_object.Network.get_object(
|
||||
context.get_admin_context(), id=port_res['network_id'])
|
||||
if net.qos_policy_id:
|
||||
qos_policy = policy_object.QosPolicy.get_network_policy(
|
||||
context.get_admin_context(), net.id)
|
||||
|
||||
if not qos_policy:
|
||||
return port_res
|
||||
|
||||
resources = {}
|
||||
rule_direction_class = {
|
||||
nl_constants.INGRESS_DIRECTION:
|
||||
pl_constants.CLASS_NET_BW_INGRESS_KBPS,
|
||||
nl_constants.EGRESS_DIRECTION:
|
||||
pl_constants.CLASS_NET_BW_EGRESS_KBPS
|
||||
}
|
||||
for rule in qos_policy.rules:
|
||||
if rule.rule_type == qos_consts.RULE_TYPE_MINIMUM_BANDWIDTH:
|
||||
resources[rule_direction_class[rule.direction]] = rule.min_kbps
|
||||
if not resources:
|
||||
return port_res
|
||||
|
||||
vnic_trait = pl_utils.vnic_type_trait(
|
||||
port_res[portbindings.VNIC_TYPE])
|
||||
|
||||
# TODO(lajoskatona): Change to handle all segments when any traits
|
||||
# support will be available. See Placement spec:
|
||||
# https://review.openstack.org/565730
|
||||
first_segment = network_object.NetworkSegment.get_objects(
|
||||
context.get_admin_context(),
|
||||
network_id=port_res['network_id'])[0]
|
||||
|
||||
if not first_segment or not first_segment.physical_network:
|
||||
return port_res
|
||||
physnet_trait = pl_utils.physnet_trait(
|
||||
first_segment.physical_network)
|
||||
|
||||
resource_request = {
|
||||
'required': [physnet_trait, vnic_trait],
|
||||
'resources': resources
|
||||
}
|
||||
port_res['resource_request'] = resource_request
|
||||
return port_res
|
||||
|
||||
def _get_ports_with_policy(self, context, policy):
|
||||
networks_ids = policy.get_bound_networks()
|
||||
ports_with_net_policy = ports_object.Port.get_objects(
|
||||
|
|
|
@ -14,8 +14,10 @@ import copy
|
|||
|
||||
import mock
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib import constants as lib_constants
|
||||
from neutron_lib import context
|
||||
from neutron_lib import exceptions as lib_exc
|
||||
from neutron_lib.placement import constants as pl_constants
|
||||
from neutron_lib.plugins import constants as plugins_constants
|
||||
from neutron_lib.plugins import directory
|
||||
from neutron_lib.services.qos import constants as qos_consts
|
||||
|
@ -107,6 +109,112 @@ class TestQosPlugin(base.BaseQosTestCase):
|
|||
self.assertEqual(call_args[1], ctxt)
|
||||
self.assertIsInstance(call_args[2], policy_object.QosPolicy)
|
||||
|
||||
def _create_and_extend_port(self, bw_rules, physical_network='public',
|
||||
has_qos_policy=True, network_qos=None):
|
||||
network_id = uuidutils.generate_uuid()
|
||||
|
||||
if has_qos_policy or network_qos:
|
||||
policy = self.policy
|
||||
policy_id = self.policy.id
|
||||
self.policy.rules = bw_rules
|
||||
for rule in bw_rules:
|
||||
rule.qos_policy_id = self.policy.id
|
||||
else:
|
||||
policy = None
|
||||
policy_id = None
|
||||
|
||||
port_res = {
|
||||
"id": uuidutils.generate_uuid(),
|
||||
"qos_policy_id": policy_id,
|
||||
"network_id": network_id,
|
||||
"binding:vnic_type": "normal",
|
||||
}
|
||||
network_mock = mock.MagicMock(id=network_id, qos_policy_id=policy_id)
|
||||
segment_mock = mock.MagicMock(network_id=network_id,
|
||||
physical_network=physical_network)
|
||||
|
||||
with mock.patch(
|
||||
'neutron.objects.network.Network.get_object',
|
||||
return_value=network_mock
|
||||
), mock.patch(
|
||||
'neutron.objects.network.NetworkSegment.get_objects',
|
||||
return_value=[segment_mock]
|
||||
), mock.patch(
|
||||
'neutron.objects.qos.policy.QosPolicy.get_port_policy',
|
||||
return_value=policy
|
||||
):
|
||||
return qos_plugin.QoSPlugin._extend_port_resource_request(
|
||||
port_res, {})
|
||||
|
||||
def test__extend_port_resource_request_min_bw_rule(self):
|
||||
self.min_rule.direction = lib_constants.EGRESS_DIRECTION
|
||||
port = self._create_and_extend_port([self.min_rule])
|
||||
|
||||
self.assertEqual(
|
||||
['CUSTOM_PHYSNET_PUBLIC', 'CUSTOM_VNIC_TYPE_NORMAL'],
|
||||
port['resource_request']['required']
|
||||
)
|
||||
self.assertEqual(
|
||||
{pl_constants.CLASS_NET_BW_EGRESS_KBPS: 10},
|
||||
port['resource_request']['resources'],
|
||||
)
|
||||
|
||||
def test__extend_port_resource_request_mixed_rules(self):
|
||||
self.min_rule.direction = lib_constants.EGRESS_DIRECTION
|
||||
|
||||
min_rule_ingress_data = {
|
||||
'id': uuidutils.generate_uuid(),
|
||||
'min_kbps': 20,
|
||||
'direction': lib_constants.INGRESS_DIRECTION}
|
||||
min_rule_ingress = rule_object.QosMinimumBandwidthRule(
|
||||
self.ctxt, **min_rule_ingress_data)
|
||||
|
||||
port = self._create_and_extend_port([self.min_rule, min_rule_ingress])
|
||||
self.assertEqual(
|
||||
['CUSTOM_PHYSNET_PUBLIC', 'CUSTOM_VNIC_TYPE_NORMAL'],
|
||||
port['resource_request']['required']
|
||||
)
|
||||
self.assertEqual(
|
||||
{
|
||||
pl_constants.CLASS_NET_BW_EGRESS_KBPS: 10,
|
||||
pl_constants.CLASS_NET_BW_INGRESS_KBPS: 20
|
||||
},
|
||||
port['resource_request']['resources'],
|
||||
)
|
||||
|
||||
def test__extend_port_resource_request_non_min_bw_rule(self):
|
||||
port = self._create_and_extend_port([self.rule])
|
||||
|
||||
self.assertIsNone(port.get('resource_request'))
|
||||
|
||||
def test__extend_port_resource_request_non_provider_net(self):
|
||||
self.min_rule.direction = lib_constants.EGRESS_DIRECTION
|
||||
|
||||
port = self._create_and_extend_port([self.min_rule],
|
||||
physical_network=None)
|
||||
self.assertIsNone(port.get('resource_request'))
|
||||
|
||||
def test__extend_port_resource_request_no_qos_policy(self):
|
||||
port = self._create_and_extend_port([], physical_network='public',
|
||||
has_qos_policy=False)
|
||||
self.assertIsNone(port.get('resource_request'))
|
||||
|
||||
def test__extend_port_resource_request_inherited_policy(self):
|
||||
self.min_rule.direction = lib_constants.EGRESS_DIRECTION
|
||||
self.policy.rules = [self.min_rule]
|
||||
self.min_rule.qos_policy_id = self.policy.id
|
||||
|
||||
port = self._create_and_extend_port([self.min_rule],
|
||||
network_qos=self.policy)
|
||||
self.assertEqual(
|
||||
['CUSTOM_PHYSNET_PUBLIC', 'CUSTOM_VNIC_TYPE_NORMAL'],
|
||||
port['resource_request']['required']
|
||||
)
|
||||
self.assertEqual(
|
||||
{pl_constants.CLASS_NET_BW_EGRESS_KBPS: 10},
|
||||
port['resource_request']['resources'],
|
||||
)
|
||||
|
||||
def test_get_ports_with_policy(self):
|
||||
network_ports = [
|
||||
mock.MagicMock(qos_policy_id=None),
|
||||
|
|
Loading…
Reference in New Issue