Quality of Service support via NSPs
QoS support for PTs inside PTG, as 2 new NSP param types. The new NSP param types, qos_maxrate and qos_burstrate, map to the Neutron QoS Policy resource, which then gets associated to a QoS bandwith limit rule with a certain maximum rate and/or burst rate (in Kbps) set. Change-Id: I4a15daf5e0edd76d2d436eac6fdfb6b9f64992b2
This commit is contained in:
parent
5fa26eb3a6
commit
6b2c15c3b8
@ -14,4 +14,7 @@ if [[ $ENABLE_APIC_AIM = True || $ENABLE_APIC_AIM_GATE = True ]]; then
|
||||
ML2_L3_PLUGIN=${ML2_L3_PLUGIN:-apic_aim_l3}
|
||||
|
||||
Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-opflex}
|
||||
else
|
||||
Q_SERVICE_PLUGIN_CLASSES=${Q_SERVICE_PLUGIN_CLASSES},qos
|
||||
Q_ML2_PLUGIN_EXT_DRIVERS=${Q_ML2_PLUGIN_EXT_DRIVERS},qos
|
||||
fi
|
||||
|
@ -24,6 +24,7 @@ function gbp_configure_neutron {
|
||||
iniset $NEUTRON_CONF quotas quota_security_group_rule "-1"
|
||||
iniset $NEUTRON_CONF quotas quota_router "-1"
|
||||
iniset $NEUTRON_CONF quotas quota_floatingip "-1"
|
||||
iniset $NEUTRON_CONF agent extensions "qos"
|
||||
}
|
||||
|
||||
function nfp_configure_neutron {
|
||||
@ -41,12 +42,12 @@ function nfp_configure_neutron {
|
||||
fi
|
||||
iniset $NEUTRON_CONF nfp_node_driver is_service_admin_owned "True"
|
||||
iniset $NEUTRON_CONF nfp_node_driver svc_management_ptg_name "svc_management_ptg"
|
||||
#extn_drivers=$(iniget $NEUTRON_ML2_CONF ml2 extension_drivers)
|
||||
#if [[ -n $extn_drivers ]];then
|
||||
# iniset $NEUTRON_ML2_CONF ml2 extension_drivers $extn_drivers,port_security
|
||||
#else
|
||||
# iniset $NEUTRON_ML2_CONF ml2 extension_drivers port_security
|
||||
#fi
|
||||
extn_drivers=$(iniget $NEUTRON_ML2_CONF ml2 extension_drivers)
|
||||
if [[ -n $extn_drivers ]];then
|
||||
iniset $NEUTRON_ML2_CONF ml2 extension_drivers $extn_drivers,port_security
|
||||
else
|
||||
iniset $NEUTRON_ML2_CONF ml2 extension_drivers port_security
|
||||
fi
|
||||
}
|
||||
|
||||
function configure_nfp_loadbalancer {
|
||||
|
55
doc/source/devref/neutron-qos-support.rst
Normal file
55
doc/source/devref/neutron-qos-support.rst
Normal file
@ -0,0 +1,55 @@
|
||||
..
|
||||
This work is licensed under a Creative Commons Attribution 3.0 Unported
|
||||
License.
|
||||
|
||||
http://creativecommons.org/licenses/by/3.0/legalcode
|
||||
|
||||
Neutron QoS Support
|
||||
===================
|
||||
|
||||
Quality of Service (QoS) support is available in GBP using the Neutron API
|
||||
(via the GBP Neutron Resource Mapping Driver).
|
||||
|
||||
This feature can be used by creating Network Service Policies (NSP) with the
|
||||
following NSP parameter types:
|
||||
- `qos_maxrate`: maximum bandwidth limiting rate for a Policy Target (in Kbps)
|
||||
- `qos_burstrate`: burst bandwidth limiting rate for a Policy Target (in Kbps)
|
||||
|
||||
When a NSP contains one or both of the previous parameter types, and is
|
||||
associated to a Policy Target Group (PTG), all Policy Targets part of that PTG
|
||||
will individually inherit the QoS definitions intended. In other words, each
|
||||
Policy Target will have their bandwidth limited by the amounts specified in
|
||||
`qos_maxrate` and `qos_burstrate`, independently of the network activity in
|
||||
other Policy Targets of the same PTG.
|
||||
|
||||
The QoS NSP parameter types expect a numerical value bound by Neutron's QoS.
|
||||
|
||||
Resource Mapping Driver
|
||||
-----------------------
|
||||
When a NSP contains one or both of the supported parameter types, and a
|
||||
respective numerical value, the Resource Mapping Driver will automatically be
|
||||
called to create one QoS Policy resource and one QoS Bandwidth Limit Rule
|
||||
resource, via Neutron's REST API.
|
||||
|
||||
The Resource Mapping Driver includes the NSP Manager as a mixin, which
|
||||
provides the data model and methods for creating mappings from GBP to QoS
|
||||
resources in Neutron, as explained in more detail below.
|
||||
|
||||
This driver also handles the association of each Policy Target (part of a PTG
|
||||
having a NSP that includes QoS parameters) to the actual QoS Policies that
|
||||
have been created in Neutron. The Neutron Ports already mapped to Policy
|
||||
Targets will be updated to include the correct Neutron QoS Policy that was
|
||||
previously created and mapped to GBP.
|
||||
|
||||
NSP Manager
|
||||
-----------
|
||||
A special resource exists in GBP, `ServicePolicyQosPolicyMapping`, which keeps
|
||||
track of the mapping between NSPs in GBP and QoS Policies in Neutron, and is
|
||||
specifically defined inside the NSP Manager file (`nsp_manager.py`).
|
||||
|
||||
Furthermore, `NetworkServicePolicyMappingMixin` includes the database
|
||||
operations for creating, deleting and reading these mappings.
|
||||
|
||||
DevStack Support
|
||||
----------------
|
||||
The GBP DevStack plugin will automatically enable QoS support.
|
@ -176,6 +176,18 @@ class LocalAPI(object):
|
||||
raise exc.GroupPolicyDeploymentError()
|
||||
return l3_plugin
|
||||
|
||||
@property
|
||||
def _qos_plugin(self):
|
||||
# Probably as well:
|
||||
# REVISIT(rkukura): Need initialization method after all
|
||||
# plugins are loaded to grab and store plugin.
|
||||
plugins = manager.NeutronManager.get_service_plugins()
|
||||
qos_plugin = plugins.get(pconst.QOS)
|
||||
if not qos_plugin:
|
||||
LOG.error(_LE("No QoS service plugin found."))
|
||||
raise exc.GroupPolicyDeploymentError()
|
||||
return qos_plugin
|
||||
|
||||
@property
|
||||
def _group_policy_plugin(self):
|
||||
# REVISIT(rkukura): Need initialization method after all
|
||||
@ -231,6 +243,15 @@ class LocalAPI(object):
|
||||
# method will be invoked in the API layer.
|
||||
return obj
|
||||
|
||||
def _create_resource_qos(self, plugin, context, resource,
|
||||
param, attrs):
|
||||
action = 'create_' + resource
|
||||
obj_creator = getattr(plugin, action)
|
||||
if resource == "policy_bandwidth_limit_rule":
|
||||
resource = resource[7:] # in the body, policy_ should be removed
|
||||
obj = obj_creator(context, param, {resource: attrs})
|
||||
return obj
|
||||
|
||||
def _update_resource(self, plugin, context, resource, resource_id, attrs,
|
||||
do_notify=True):
|
||||
# REVISIT(rkukura): Check authorization?
|
||||
@ -246,6 +267,12 @@ class LocalAPI(object):
|
||||
obj_deleter = getattr(plugin, action)
|
||||
obj_deleter(context, resource_id)
|
||||
|
||||
def _delete_resource_qos(self, plugin, context, resource,
|
||||
resource_id, second_id):
|
||||
action = 'delete_' + resource
|
||||
obj_deleter = getattr(plugin, action)
|
||||
obj_deleter(context, resource_id, second_id)
|
||||
|
||||
def _get_resource(self, plugin, context, resource, resource_id):
|
||||
obj_getter = getattr(plugin, 'get_' + resource)
|
||||
obj = obj_getter(context, resource_id)
|
||||
@ -517,6 +544,41 @@ class LocalAPI(object):
|
||||
return self._get_resource(self._group_policy_plugin, plugin_context,
|
||||
'l2_policy', l2p_id)
|
||||
|
||||
def _get_qos_policy(self, plugin_context, qos_policy_id):
|
||||
return self._get_resource(self._qos_plugin, plugin_context,
|
||||
'policy', qos_policy_id)
|
||||
|
||||
def _create_qos_policy(self, plugin_context, attrs):
|
||||
return self._create_resource(self._qos_plugin, plugin_context,
|
||||
'policy', attrs)
|
||||
|
||||
def _delete_qos_policy(self, plugin_context, qos_policy_id):
|
||||
try:
|
||||
self._delete_resource(self._qos_plugin,
|
||||
plugin_context, 'policy', qos_policy_id)
|
||||
except n_exc.QosPolicyNotFound:
|
||||
LOG.warning(_LW('QoS Policy %s already deleted'), qos_policy_id)
|
||||
|
||||
def _get_qos_rules(self, plugin_context, filters=None):
|
||||
filters = filters or {}
|
||||
return self._get_resources(self._qos_plugin, plugin_context,
|
||||
'policy_bandwidth_limit_rules', filters)
|
||||
|
||||
def _create_qos_rule(self, plugin_context, qos_policy_id, attrs):
|
||||
return self._create_resource_qos(self._qos_plugin,
|
||||
plugin_context,
|
||||
'policy_bandwidth_limit_rule',
|
||||
qos_policy_id, attrs)
|
||||
|
||||
def _delete_qos_rule(self, plugin_context, rule_id, qos_policy_id):
|
||||
try:
|
||||
self._delete_resource_qos(self._qos_plugin,
|
||||
plugin_context,
|
||||
'policy_bandwidth_limit_rule',
|
||||
rule_id, qos_policy_id)
|
||||
except n_exc.QosRuleNotFound:
|
||||
LOG.warning(_LW('QoS Rule %s already deleted'), rule_id)
|
||||
|
||||
def _get_l2_policies(self, plugin_context, filters=None):
|
||||
filters = filters or {}
|
||||
return self._get_resources(self._group_policy_plugin, plugin_context,
|
||||
|
@ -1 +1 @@
|
||||
c460c5682e74
|
||||
da6a25bbcfa8
|
||||
|
@ -0,0 +1,48 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""initial_qos_via_nsp
|
||||
|
||||
Revision ID: da6a25bbcfa8
|
||||
Revises: c460c5682e74
|
||||
Create Date: 2017-03-27 00:00:00.000000
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'da6a25bbcfa8'
|
||||
down_revision = 'c460c5682e74'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'gpm_qos_policy_mappings',
|
||||
sa.Column('service_policy_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('qos_policy_id', sa.String(length=36), nullable=False),
|
||||
sa.PrimaryKeyConstraint('service_policy_id'),
|
||||
sa.ForeignKeyConstraint(['service_policy_id'],
|
||||
['gp_network_service_policies.id'],
|
||||
ondelete='CASCADE',
|
||||
name='gbp_qos_policy_mapping_nsp_fk'),
|
||||
sa.ForeignKeyConstraint(['qos_policy_id'],
|
||||
['qos_policies.id'],
|
||||
ondelete='RESTRICT',
|
||||
name='gbp_qos_policy_mapping_qosp_fk')
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table('gpm_qos_policy_mappings')
|
@ -210,6 +210,8 @@ gp_supported_protocols = [None, nlib_const.PROTO_NAME_TCP,
|
||||
gp_network_service_param_types = [
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_IP_SINGLE,
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_IP_POOL,
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX, # expects integer value
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST, # expects integer value
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_STRING]
|
||||
gp_network_service_param_keys = [
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE,
|
||||
@ -320,7 +322,9 @@ def _validate_network_svc_params(data, key_specs=None):
|
||||
d['type'])
|
||||
LOG.debug(msg)
|
||||
return msg
|
||||
if d['type'] != gp_constants.GP_NETWORK_SVC_PARAM_TYPE_STRING:
|
||||
if d['type'] not in (gp_constants.GP_NETWORK_SVC_PARAM_TYPE_STRING,
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
gp_constants.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST):
|
||||
if d['value'] not in gp_network_service_param_values:
|
||||
msg = _("Network service param value '%s' is not "
|
||||
"supported") % d['value']
|
||||
|
@ -34,6 +34,8 @@ GP_NETWORK_SVC_PARAM_VALUE = 'value'
|
||||
|
||||
GP_NETWORK_SVC_PARAM_TYPE_IP_SINGLE = 'ip_single'
|
||||
GP_NETWORK_SVC_PARAM_TYPE_IP_POOL = 'ip_pool'
|
||||
GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX = 'qos_maxrate'
|
||||
GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST = 'qos_burstrate'
|
||||
GP_NETWORK_SVC_PARAM_TYPE_STRING = 'string'
|
||||
|
||||
GP_NETWORK_SVC_PARAM_VALUE_SELF_SUBNET = 'self_subnet'
|
||||
|
@ -234,9 +234,13 @@ class MultipleRedirectActionsNotSupportedForPRS(GroupPolicyBadRequest):
|
||||
|
||||
|
||||
class InvalidNetworkServiceParameters(GroupPolicyBadRequest):
|
||||
message = _("Resource Mapping Driver currently supports only one "
|
||||
"parameter of type: ip_single and value: self_subnet and one "
|
||||
"parameter of type ip_single or ip_pool and value nat_pool")
|
||||
message = _("Resource Mapping Driver currently supports Network Service "
|
||||
"Parameters with the following types and values: "
|
||||
"type: 'ip_single' value: 'self_subnet'; "
|
||||
"type: 'ip_pool' value: 'nat_pool'; "
|
||||
"type: 'ip_single' value: 'nat_pool'; "
|
||||
"type: 'qos_maxrate' value: numerical of intended Kbps; "
|
||||
"type: 'qos_burstrate' value: numerical of intended Kbps.")
|
||||
|
||||
|
||||
class ESSubnetRequiredForNatPool(GroupPolicyBadRequest):
|
||||
|
@ -60,6 +60,24 @@ class PolicyTargetFloatingIPMapping(model_base.BASEV2):
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class ServicePolicyQosPolicyMapping(model_base.BASEV2):
|
||||
"""Mapping of a NSP to a Neutron QoS Policy."""
|
||||
__tablename__ = 'gpm_qos_policy_mappings'
|
||||
service_policy_id = sa.Column(
|
||||
sa.String(36),
|
||||
sa.ForeignKey('gp_network_service_policies.id',
|
||||
ondelete='CASCADE'),
|
||||
nullable=False,
|
||||
primary_key=True
|
||||
)
|
||||
qos_policy_id = sa.Column(
|
||||
sa.String(36),
|
||||
sa.ForeignKey('qos_policies.id',
|
||||
ondelete='RESTRICT'),
|
||||
nullable=False
|
||||
)
|
||||
|
||||
|
||||
class NetworkServicePolicyMappingMixin(object):
|
||||
|
||||
def _set_policy_ipaddress_mapping(self, session, service_policy_id,
|
||||
@ -132,3 +150,20 @@ class NetworkServicePolicyMappingMixin(object):
|
||||
policy_target_id=policy_target_id).all()
|
||||
for fip_mapping in fip_mappings:
|
||||
session.delete(fip_mapping)
|
||||
|
||||
def _get_nsp_qos_mapping(self, session, service_policy_id):
|
||||
with session.begin(subtransactions=True):
|
||||
return (session.query(ServicePolicyQosPolicyMapping).
|
||||
filter_by(service_policy_id=service_policy_id).first())
|
||||
|
||||
def _set_nsp_qos_mapping(self, session, service_policy_id, qos_policy_id):
|
||||
with session.begin(subtransactions=True):
|
||||
mapping = ServicePolicyQosPolicyMapping(
|
||||
service_policy_id=service_policy_id,
|
||||
qos_policy_id=qos_policy_id)
|
||||
session.add(mapping)
|
||||
|
||||
def _delete_nsp_qos_mapping(self, session, mapping):
|
||||
if mapping:
|
||||
with session.begin(subtransactions=True):
|
||||
session.delete(mapping)
|
||||
|
189
gbpservice/neutron/services/grouppolicy/drivers/resource_mapping.py
Normal file → Executable file
189
gbpservice/neutron/services/grouppolicy/drivers/resource_mapping.py
Normal file → Executable file
@ -928,17 +928,37 @@ class ImplicitResourceOperations(local_api.LocalAPI,
|
||||
def _validate_nsp_parameters(self, context):
|
||||
nsp = context.current
|
||||
nsp_params = nsp.get("network_service_params")
|
||||
supported_nsp_pars = {"ip_single": ["self_subnet", "nat_pool"],
|
||||
"ip_pool": "nat_pool"}
|
||||
if (nsp_params and len(nsp_params) > 2 or len(nsp_params) == 2 and
|
||||
nsp_params[0] == nsp_params[1]):
|
||||
|
||||
supported_static_nsp_pars = {
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_IP_SINGLE: [
|
||||
gconst.GP_NETWORK_SVC_PARAM_VALUE_SELF_SUBNET,
|
||||
gconst.GP_NETWORK_SVC_PARAM_VALUE_NAT_POOL],
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_IP_POOL: [
|
||||
gconst.GP_NETWORK_SVC_PARAM_VALUE_NAT_POOL]}
|
||||
|
||||
# for params without a static value - later evaluation needed:
|
||||
supported_flexible_nsp_params = (
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX)
|
||||
|
||||
# validate unique param types:
|
||||
types_inside = set((d['type'] for d in nsp_params))
|
||||
if len(types_inside) != len(nsp_params):
|
||||
raise exc.InvalidNetworkServiceParameters()
|
||||
|
||||
for params in nsp_params:
|
||||
type = params.get("type")
|
||||
value = params.get("value")
|
||||
if (type not in supported_nsp_pars or
|
||||
value not in supported_nsp_pars[type]):
|
||||
raise exc.InvalidNetworkServiceParameters()
|
||||
type_ = params.get("type")
|
||||
value_ = params.get("value")
|
||||
if (type_ not in supported_flexible_nsp_params):
|
||||
if (type_ not in supported_static_nsp_pars or
|
||||
value_ not in supported_static_nsp_pars[type_]):
|
||||
raise exc.InvalidNetworkServiceParameters()
|
||||
else:
|
||||
try:
|
||||
if int(value_) < 0:
|
||||
raise exc.InvalidNetworkServiceParameters()
|
||||
except ValueError:
|
||||
raise exc.InvalidNetworkServiceParameters()
|
||||
|
||||
def _validate_in_use_by_nsp(self, context):
|
||||
# We do not allow ES update for L3p when it is used by NSP
|
||||
@ -1338,6 +1358,88 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
|
||||
if l3p['tenant_id'] != context.current['tenant_id']:
|
||||
raise exc.CrossTenantL2PolicyL3PolicyNotSupported()
|
||||
|
||||
def _associate_qosp_to_pt(self, context):
|
||||
ptg_id = context.current['policy_target_group_id']
|
||||
ptg = context._plugin.get_policy_target_group(
|
||||
context._plugin_context, ptg_id)
|
||||
network_service_policy_id = ptg.get(
|
||||
"network_service_policy_id")
|
||||
if not network_service_policy_id:
|
||||
return
|
||||
|
||||
nsp = context._plugin.get_network_service_policy(
|
||||
context._plugin_context, network_service_policy_id)
|
||||
nsp_params = nsp.get("network_service_params")
|
||||
# Check if at least a QoS NSP p. is defined (a QoS policy was created)
|
||||
for nsp_parameter in nsp_params:
|
||||
if nsp_parameter["type"] in (
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST):
|
||||
|
||||
# get QoS Policy associated to NSP
|
||||
mapping = self._get_nsp_qos_mapping(
|
||||
context._plugin_context.session,
|
||||
network_service_policy_id)
|
||||
|
||||
# apply QoS policy to PT's Neutron port
|
||||
port_id = context.current['port_id']
|
||||
port = {attributes.PORT:
|
||||
{'qos_policy_id': mapping['qos_policy_id']}}
|
||||
self._core_plugin.update_port(context._plugin_context,
|
||||
port_id, port)
|
||||
break
|
||||
|
||||
def _disassociate_qosp_from_pt(self, context, pt_id):
|
||||
try:
|
||||
policy_target = context._plugin.get_policy_target(
|
||||
context._plugin_context, pt_id)
|
||||
except gp_ext.PolicyTargetNotFound:
|
||||
LOG.warning(_LW("Attempted to fetch deleted Service Target (QoS)"))
|
||||
else:
|
||||
port_id = policy_target['port_id']
|
||||
port = {attributes.PORT: {'qos_policy_id': None}}
|
||||
self._core_plugin.update_port(context._plugin_context,
|
||||
port_id, port)
|
||||
|
||||
def _cleanup_network_service_policy(self, context, ptg,
|
||||
ipaddress=None, fip_maps=None):
|
||||
super(ResourceMappingDriver, self)._cleanup_network_service_policy(
|
||||
context, ptg, ipaddress, fip_maps)
|
||||
for pt in ptg['policy_targets']:
|
||||
self._disassociate_qosp_from_pt(context, pt)
|
||||
|
||||
def _handle_network_service_policy(self, context):
|
||||
network_service_policy_id = context.current.get(
|
||||
"network_service_policy_id")
|
||||
if not network_service_policy_id:
|
||||
return
|
||||
super(ResourceMappingDriver, self)._handle_network_service_policy(
|
||||
context)
|
||||
nsp = context._plugin.get_network_service_policy(
|
||||
context._plugin_context, network_service_policy_id)
|
||||
nsp_params = nsp.get("network_service_params")
|
||||
|
||||
for nsp_parameter in nsp_params:
|
||||
if nsp_parameter["type"] in (
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST):
|
||||
|
||||
# get PTs/ports
|
||||
policy_targets = context.current['policy_targets']
|
||||
policy_targets = context._plugin.get_policy_targets(
|
||||
context._plugin_context, filters={'id': policy_targets})
|
||||
# get QoS Policy associated to NSP
|
||||
mapping = self._get_nsp_qos_mapping(
|
||||
context._plugin_context.session,
|
||||
nsp['id'])
|
||||
# apply QoS policy to each PT's Neutron port
|
||||
for pt in policy_targets:
|
||||
port_id = pt['port_id']
|
||||
port = {attributes.PORT:
|
||||
{'qos_policy_id': mapping['qos_policy_id']}}
|
||||
self._core_plugin.update_port(context._plugin_context,
|
||||
port_id, port)
|
||||
|
||||
@property
|
||||
def resource_owner_tenant_id(self):
|
||||
if not self._resource_owner_tenant_id:
|
||||
@ -1404,6 +1506,7 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
|
||||
self._assoc_ptg_sg_to_pt(context, context.current['id'],
|
||||
context.current['policy_target_group_id'])
|
||||
self._associate_fip_to_pt(context)
|
||||
self._associate_qosp_to_pt(context)
|
||||
if context.current.get('proxy_gateway'):
|
||||
self._set_proxy_gateway_routes(context, context.current)
|
||||
|
||||
@ -1890,6 +1993,48 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
|
||||
for sg in sg_list:
|
||||
self._delete_sg(context._plugin_context, sg)
|
||||
|
||||
@log.log_method_call
|
||||
def create_network_service_policy_precommit(self, context):
|
||||
self._validate_nsp_parameters(context)
|
||||
|
||||
@log.log_method_call
|
||||
def create_network_service_policy_postcommit(self, context):
|
||||
p = context.current['network_service_params']
|
||||
max = burst = 0
|
||||
setting_qos = False
|
||||
# assumes single value per parameter type, as the API currently states
|
||||
params = {p[n]['type']: p[n]['value'] for n in range(len(p))}
|
||||
# check for QoS param types..
|
||||
if gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX in params:
|
||||
max = params[gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX]
|
||||
setting_qos = True
|
||||
if gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST in params:
|
||||
burst = params[gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST]
|
||||
setting_qos = True
|
||||
# ..and create needed Neutron resources
|
||||
if setting_qos:
|
||||
qos_policy_id = self._create_implicit_qos_policy(context)
|
||||
nsp_id = context.current['id']
|
||||
self._create_implicit_qos_rule(context, qos_policy_id, max, burst)
|
||||
self._set_nsp_qos_mapping(context._plugin_context.session,
|
||||
nsp_id,
|
||||
qos_policy_id)
|
||||
|
||||
@log.log_method_call
|
||||
def delete_network_service_policy_precommit(self, context):
|
||||
nsp = context.current
|
||||
mapping = self._get_nsp_qos_mapping(context._plugin_context.session,
|
||||
nsp['id'])
|
||||
if mapping:
|
||||
qos_policy_id = mapping['qos_policy_id']
|
||||
context.current['qos_policy_id'] = qos_policy_id
|
||||
|
||||
@log.log_method_call
|
||||
def delete_network_service_policy_postcommit(self, context):
|
||||
qos_policy_id = context.current['qos_policy_id']
|
||||
if qos_policy_id:
|
||||
self._delete_ptg_qos_policy(context, qos_policy_id)
|
||||
|
||||
def create_external_segment_precommit(self, context):
|
||||
if context.current['subnet_id']:
|
||||
subnet = self._get_subnet(context._plugin_context,
|
||||
@ -2896,3 +3041,29 @@ class ResourceMappingDriver(api.PolicyDriver, ImplicitResourceOperations,
|
||||
master_mac = master_port['mac_address']
|
||||
master_ips = [x['ip_address'] for x in master_port['fixed_ips']]
|
||||
return master_mac, master_ips
|
||||
|
||||
def _create_implicit_qos_policy(self, context):
|
||||
attrs = {
|
||||
'name': 'gbp_' + context.current['name'],
|
||||
'description': 'Group-Based Policy QoS policy',
|
||||
'tenant_id': context.current['tenant_id']}
|
||||
qos_policy = self._create_qos_policy(context._plugin_context, attrs)
|
||||
qos_policy_id = qos_policy['id']
|
||||
return qos_policy_id
|
||||
|
||||
def _delete_ptg_qos_policy(self, context, qos_policy_id):
|
||||
qos_rules = self._get_qos_rules(context._plugin_context, qos_policy_id)
|
||||
with context._plugin_context.session.begin(subtransactions=True):
|
||||
for qos_rule in qos_rules:
|
||||
self._delete_qos_rule(context._plugin_context,
|
||||
qos_rule['id'], qos_policy_id)
|
||||
self._delete_qos_policy(context._plugin_context, qos_policy_id)
|
||||
|
||||
def _create_implicit_qos_rule(self, context, qos_policy_id, max, burst):
|
||||
attrs = {
|
||||
'max_kbps': max,
|
||||
'max_burst_kbps': burst}
|
||||
qos_rule = self._create_qos_rule(context._plugin_context,
|
||||
qos_policy_id, attrs)
|
||||
qos_rule_id = qos_rule['id']
|
||||
return qos_rule_id
|
||||
|
@ -18,6 +18,7 @@ import webob.exc
|
||||
|
||||
import mock
|
||||
from neutron.api import extensions
|
||||
from neutron.api.rpc.callbacks.producer import registry
|
||||
from neutron import context
|
||||
from neutron.db import api as db_api
|
||||
from neutron import manager
|
||||
@ -351,6 +352,8 @@ class GroupPolicyDbTestCase(GroupPolicyDBTestBase,
|
||||
|
||||
extensions.append_api_extensions_path(
|
||||
gbpservice.neutron.extensions.__path__)
|
||||
service_plugins['flavors_plugin_name'] =\
|
||||
'neutron.services.flavors.flavors_plugin.FlavorsPlugin'
|
||||
super(GroupPolicyDbTestCase, self).setUp(
|
||||
plugin=core_plugin, ext_mgr=ext_mgr,
|
||||
service_plugins=service_plugins
|
||||
@ -373,6 +376,7 @@ class GroupPolicyDbTestCase(GroupPolicyDBTestBase,
|
||||
|
||||
def tearDown(self):
|
||||
self._unset_notification_mocks()
|
||||
registry.clear()
|
||||
super(GroupPolicyDbTestCase, self).tearDown()
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,
|
||||
test_l3.L3NatTestCaseMixin):
|
||||
|
||||
def setUp(self, core_plugin=None, l3_plugin=None, gp_plugin=None,
|
||||
service_plugins=None, sc_plugin=None):
|
||||
service_plugins=None, sc_plugin=None, qos_plugin=None):
|
||||
if not gp_plugin:
|
||||
gp_plugin = DB_GP_PLUGIN_KLASS
|
||||
if not service_plugins:
|
||||
@ -53,6 +53,8 @@ class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,
|
||||
'flavors_plugin.FlavorsPlugin',
|
||||
'servicechain_plugin': sc_plugin or SC_PLUGIN_KLASS}
|
||||
service_plugins['l3_plugin_name'] = l3_plugin or "router"
|
||||
if qos_plugin:
|
||||
service_plugins['qos_plugin_name'] = qos_plugin
|
||||
super(GroupPolicyMappingDbTestCase, self).setUp(
|
||||
core_plugin=core_plugin, gp_plugin=gp_plugin,
|
||||
service_plugins=service_plugins
|
||||
|
@ -95,7 +95,7 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
|
||||
_extension_path = None
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None, ml2_options=None,
|
||||
l3_plugin=None, sc_plugin=None, **kwargs):
|
||||
l3_plugin=None, sc_plugin=None, qos_plugin=None, **kwargs):
|
||||
core_plugin = core_plugin or ML2PLUS_PLUGIN
|
||||
if not l3_plugin:
|
||||
l3_plugin = "apic_aim_l3"
|
||||
@ -123,7 +123,7 @@ class AIMBaseTestCase(test_nr_base.CommonNeutronBaseTestCase,
|
||||
super(AIMBaseTestCase, self).setUp(
|
||||
policy_drivers=policy_drivers, core_plugin=core_plugin,
|
||||
ml2_options=ml2_opts, l3_plugin=l3_plugin,
|
||||
sc_plugin=sc_plugin)
|
||||
sc_plugin=sc_plugin, qos_plugin=qos_plugin)
|
||||
aim_model_base.Base.metadata.create_all(self.engine)
|
||||
self.db_session = db_api.get_session()
|
||||
self.initialize_db_config(self.db_session)
|
||||
|
@ -101,7 +101,7 @@ class ApicMappingTestCase(
|
||||
|
||||
def setUp(self, sc_plugin=None, nat_enabled=True,
|
||||
pre_existing_l3out=False, default_agent_conf=True,
|
||||
ml2_options=None, single_tenant_mode=False):
|
||||
ml2_options=None, single_tenant_mode=False, qos_plugin=None):
|
||||
self.saved_apicapi = sys.modules["apicapi"]
|
||||
sys.modules["apicapi"] = mock.Mock()
|
||||
if default_agent_conf:
|
||||
@ -142,7 +142,7 @@ class ApicMappingTestCase(
|
||||
nova_client.return_value = vm
|
||||
super(ApicMappingTestCase, self).setUp(
|
||||
policy_drivers=['implicit_policy', 'apic', 'chain_mapping'],
|
||||
ml2_options=ml2_opts, sc_plugin=sc_plugin)
|
||||
ml2_options=ml2_opts, sc_plugin=sc_plugin, qos_plugin=qos_plugin)
|
||||
engine = db_api.get_engine()
|
||||
model_base.BASEV2.metadata.create_all(engine)
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
|
@ -33,7 +33,8 @@ class ExtensionDriverTestBase(test_plugin.GroupPolicyPluginTestCase):
|
||||
_extension_path = os.path.dirname(os.path.abspath(test_ext.__file__))
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None,
|
||||
l3_plugin=None, ml2_options=None, sc_plugin=None):
|
||||
l3_plugin=None, ml2_options=None,
|
||||
sc_plugin=None, qos_plugin=None):
|
||||
config.cfg.CONF.set_override('extension_drivers',
|
||||
self._extension_drivers,
|
||||
group='group_policy')
|
||||
@ -42,7 +43,8 @@ class ExtensionDriverTestBase(test_plugin.GroupPolicyPluginTestCase):
|
||||
'api_extensions_path', self._extension_path)
|
||||
super(ExtensionDriverTestBase, self).setUp(
|
||||
core_plugin=core_plugin, l3_plugin=l3_plugin,
|
||||
ml2_options=ml2_options, sc_plugin=sc_plugin)
|
||||
ml2_options=ml2_options, sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
|
||||
|
||||
class ExtensionDriverTestCase(ExtensionDriverTestBase):
|
||||
|
@ -60,7 +60,7 @@ def get_status_for_test(self, context):
|
||||
class GroupPolicyPluginTestBase(tgpmdb.GroupPolicyMappingDbTestCase):
|
||||
|
||||
def setUp(self, core_plugin=None, l3_plugin=None, gp_plugin=None,
|
||||
ml2_options=None, sc_plugin=None):
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None):
|
||||
if not gp_plugin:
|
||||
gp_plugin = GP_PLUGIN_KLASS
|
||||
ml2_opts = ml2_options or {'mechanism_drivers': ['openvswitch'],
|
||||
@ -71,7 +71,8 @@ class GroupPolicyPluginTestBase(tgpmdb.GroupPolicyMappingDbTestCase):
|
||||
super(GroupPolicyPluginTestBase, self).setUp(core_plugin=core_plugin,
|
||||
l3_plugin=l3_plugin,
|
||||
gp_plugin=gp_plugin,
|
||||
sc_plugin=sc_plugin)
|
||||
sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
|
||||
def _create_l2_policy_on_shared(self, **kwargs):
|
||||
l3p = self.create_l3_policy(shared=True)['l3_policy']
|
||||
|
@ -34,7 +34,7 @@ CORE_PLUGIN = ('gbpservice.neutron.tests.unit.services.grouppolicy.'
|
||||
class CommonNeutronBaseTestCase(test_plugin.GroupPolicyPluginTestBase):
|
||||
|
||||
def setUp(self, policy_drivers=None, core_plugin=None, l3_plugin=None,
|
||||
ml2_options=None, sc_plugin=None):
|
||||
ml2_options=None, sc_plugin=None, qos_plugin=None):
|
||||
core_plugin = core_plugin or ML2PLUS_PLUGIN
|
||||
policy_drivers = policy_drivers or ['neutron_resources']
|
||||
config.cfg.CONF.set_override('policy_drivers',
|
||||
@ -46,7 +46,8 @@ class CommonNeutronBaseTestCase(test_plugin.GroupPolicyPluginTestBase):
|
||||
super(CommonNeutronBaseTestCase, self).setUp(core_plugin=core_plugin,
|
||||
l3_plugin=l3_plugin,
|
||||
ml2_options=ml2_options,
|
||||
sc_plugin=sc_plugin)
|
||||
sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
engine = db_api.get_engine()
|
||||
model_base.BASEV2.metadata.create_all(engine)
|
||||
res = mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.'
|
||||
|
@ -20,6 +20,7 @@ import mock
|
||||
import netaddr
|
||||
from neutron import context as nctx
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db.qos import models as qos_models
|
||||
from neutron.extensions import external_net as external_net
|
||||
from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron import manager
|
||||
@ -70,8 +71,9 @@ CORE_PLUGIN = ('gbpservice.neutron.tests.unit.services.grouppolicy.'
|
||||
class ResourceMappingTestCase(test_plugin.GroupPolicyPluginTestCase):
|
||||
|
||||
def setUp(self, policy_drivers=None,
|
||||
core_plugin=n_test_plugin.PLUGIN_NAME, ml2_options=None,
|
||||
sc_plugin=None):
|
||||
core_plugin=n_test_plugin.PLUGIN_NAME,
|
||||
ml2_options=None,
|
||||
sc_plugin=None, qos_plugin=None):
|
||||
policy_drivers = policy_drivers or ['implicit_policy',
|
||||
'resource_mapping',
|
||||
'chain_mapping']
|
||||
@ -81,9 +83,16 @@ class ResourceMappingTestCase(test_plugin.GroupPolicyPluginTestCase):
|
||||
sc_cfg.cfg.CONF.set_override('servicechain_drivers',
|
||||
['dummy'], group='servicechain')
|
||||
config.cfg.CONF.set_override('allow_overlapping_ips', True)
|
||||
|
||||
ml2_opts = ml2_options or {
|
||||
'mechanism_drivers': ['openvswitch'],
|
||||
'extension_drivers': ['qos', 'port_security']
|
||||
}
|
||||
super(ResourceMappingTestCase, self).setUp(core_plugin=core_plugin,
|
||||
ml2_options=ml2_options,
|
||||
sc_plugin=sc_plugin)
|
||||
ml2_options=ml2_opts,
|
||||
sc_plugin=sc_plugin,
|
||||
qos_plugin=qos_plugin)
|
||||
|
||||
engine = db_api.get_engine()
|
||||
model_base.BASEV2.metadata.create_all(engine)
|
||||
res = mock.patch('neutron.db.l3_db.L3_NAT_dbonly_mixin.'
|
||||
@ -184,6 +193,25 @@ class ResourceMappingTestCase(test_plugin.GroupPolicyPluginTestCase):
|
||||
return (resource_mapping.ResourceMappingDriver.
|
||||
_get_policy_rule_set_sg_mapping(ctx.session, prs_id))
|
||||
|
||||
def _get_nsp_qosp_mapping(self, nsp_id):
|
||||
ctx = nctx.get_admin_context()
|
||||
with ctx.session.begin(subtransactions=True):
|
||||
return (ctx.session.query(
|
||||
nsp_manager.ServicePolicyQosPolicyMapping).
|
||||
filter_by(service_policy_id=nsp_id).first())
|
||||
|
||||
def _get_qos_policy(self, qos_policy_id):
|
||||
ctx = nctx.get_admin_context()
|
||||
with ctx.session.begin(subtransactions=True):
|
||||
return (ctx.session.query(qos_models.QosPolicy).
|
||||
filter_by(id=qos_policy_id).first())
|
||||
|
||||
def _get_qos_rules(self, qos_policy_id):
|
||||
ctx = nctx.get_admin_context()
|
||||
with ctx.session.begin(subtransactions=True):
|
||||
return (ctx.session.query(qos_models.QosBandwidthLimitRule).
|
||||
filter_by(qos_policy_id=qos_policy_id).all())
|
||||
|
||||
def _create_ssh_allow_rule(self):
|
||||
return self._create_tcp_allow_rule('22')
|
||||
|
||||
@ -3839,6 +3867,10 @@ class TestPolicyRule(ResourceMappingTestCase):
|
||||
|
||||
class TestNetworkServicePolicy(ResourceMappingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
qos_plugin = 'qos'
|
||||
super(TestNetworkServicePolicy, self).setUp(qos_plugin=qos_plugin)
|
||||
|
||||
def test_create_nsp_multiple_ptgs(self):
|
||||
nsp = self.create_network_service_policy(
|
||||
network_service_params=[
|
||||
@ -3880,6 +3912,32 @@ class TestNetworkServicePolicy(ResourceMappingTestCase):
|
||||
{"type": "ip_single", "value": "self_subnet", "name": "vip"},
|
||||
{"type": "ip_single", "value": "self_subnet", "name": "vip"}],
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.create_network_service_policy(
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
"value": "abcd", "name": "qos"}],
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.create_network_service_policy(
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
|
||||
"value": "efgh", "name": "qos"}],
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.create_network_service_policy(
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
"value": "1000", "name": "qos"},
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
"value": "2000", "name": "qos"}
|
||||
],
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
self.create_network_service_policy(
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
|
||||
"value": "1000", "name": "qos"},
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
|
||||
"value": "2000", "name": "qos"}
|
||||
],
|
||||
expected_res_status=webob.exc.HTTPBadRequest.code)
|
||||
|
||||
def test_nsp_cleanup_on_unset(self):
|
||||
ptg = self.create_policy_target_group(
|
||||
@ -4290,6 +4348,161 @@ class TestNetworkServicePolicy(ResourceMappingTestCase):
|
||||
self._verify_update_ptg_with_nsp(ptg['id'], nsp2['id'], subnet)
|
||||
self._verify_update_ptg_with_nsp(ptg['id'], nsp['id'], subnet)
|
||||
|
||||
def test_nsp_creation_for_qos_both_rates(self):
|
||||
nsp = self._create_nsp_with_qos_both_rates()
|
||||
qos_rules = self._create_nsp(nsp)
|
||||
self._verify_qos_rule(qos_rules, 1337, 2674)
|
||||
|
||||
def test_nsp_creation_for_qos_maxrate(self):
|
||||
nsp = self._create_nsp_with_qos_maxrate()
|
||||
qos_rules = self._create_nsp(nsp)
|
||||
self._verify_qos_rule(qos_rules, 1337)
|
||||
|
||||
def test_nsp_deletion_for_qos_maxrate(self):
|
||||
nsp = self._create_nsp_with_qos_maxrate()
|
||||
|
||||
# before deleting anything, get all needed IDs
|
||||
mapping = self._get_nsp_qosp_mapping(nsp['id'])
|
||||
qos_policy_id = mapping['qos_policy_id']
|
||||
|
||||
self.delete_network_service_policy(
|
||||
nsp['id'],
|
||||
expected_res_status=webob.exc.HTTPNoContent.code)
|
||||
|
||||
self.show_network_service_policy(
|
||||
nsp['id'],
|
||||
expected_res_status=webob.exc.HTTPNotFound.code)
|
||||
|
||||
mapping = self._get_nsp_qosp_mapping(nsp['id'])
|
||||
qosp = self._get_qos_policy(qos_policy_id)
|
||||
qos_rules = self._get_qos_rules(qos_policy_id)
|
||||
|
||||
# verify everything is gone
|
||||
self.assertIsNone(mapping)
|
||||
self.assertIsNone(qosp)
|
||||
self.assertEqual([], qos_rules)
|
||||
|
||||
def test_create_ptg_with_ports_existing_nsp_qos(self):
|
||||
nsp = self._create_nsp_with_qos_both_rates()
|
||||
mapping = self._get_nsp_qosp_mapping(nsp['id'])
|
||||
ptg = self.create_policy_target_group(
|
||||
name="ptg1",
|
||||
network_service_policy_id=nsp['id'],
|
||||
expected_res_status=webob.exc.HTTPCreated.code
|
||||
)['policy_target_group']
|
||||
|
||||
pt1 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
pt2 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
|
||||
port1 = self._get_object('ports', pt1['port_id'],
|
||||
self.api)['port']
|
||||
port2 = self._get_object('ports', pt2['port_id'],
|
||||
self.api)['port']
|
||||
|
||||
# verify that the respective ports acquired the expected QoS Policy
|
||||
self.assertEqual(mapping['qos_policy_id'], port1['qos_policy_id'])
|
||||
self.assertEqual(mapping['qos_policy_id'], port2['qos_policy_id'])
|
||||
|
||||
def test_update_ptg_with_ports_add_nsp(self):
|
||||
nsp = self._create_nsp_with_qos_both_rates()
|
||||
mapping = self._get_nsp_qosp_mapping(nsp['id'])
|
||||
ptg = self.create_policy_target_group(
|
||||
name="ptg1")['policy_target_group']
|
||||
|
||||
pt1 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
pt2 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
|
||||
self.update_policy_target_group(
|
||||
ptg['id'],
|
||||
network_service_policy_id = nsp['id'],
|
||||
expected_res_status = webob.exc.HTTPOk.code)
|
||||
|
||||
port1 = self._get_object('ports', pt1['port_id'],
|
||||
self.api)['port']
|
||||
port2 = self._get_object('ports', pt2['port_id'],
|
||||
self.api)['port']
|
||||
|
||||
# verify that the respective ports acquired the expected QoS Policy
|
||||
self.assertEqual(mapping['qos_policy_id'], port1['qos_policy_id'])
|
||||
self.assertEqual(mapping['qos_policy_id'], port2['qos_policy_id'])
|
||||
|
||||
def test_update_ptg_with_ports_remove_nsp(self):
|
||||
nsp = self._create_nsp_with_qos_both_rates()
|
||||
ptg = self.create_policy_target_group(
|
||||
name="ptg1",
|
||||
network_service_policy_id=nsp['id'],
|
||||
expected_res_status=webob.exc.HTTPCreated.code
|
||||
)['policy_target_group']
|
||||
|
||||
pt1 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
pt2 = self.create_policy_target(
|
||||
policy_target_group_id=ptg['id'])['policy_target']
|
||||
|
||||
self.update_policy_target_group(
|
||||
ptg['id'],
|
||||
network_service_policy_id=None,
|
||||
expected_res_status=webob.exc.HTTPOk.code)
|
||||
|
||||
port1 = self._get_object('ports', pt1['port_id'],
|
||||
self.api)['port']
|
||||
port2 = self._get_object('ports', pt2['port_id'],
|
||||
self.api)['port']
|
||||
|
||||
# verify that the respective ports don't have a QoS Policy assigned
|
||||
self.assertIsNone(port1['qos_policy_id'])
|
||||
self.assertIsNone(port2['qos_policy_id'])
|
||||
|
||||
def _create_nsp(self, nsp):
|
||||
# check if mapping was successfully created
|
||||
mapping = self._get_nsp_qosp_mapping(nsp['id'])
|
||||
qos_policy_id = mapping['qos_policy_id']
|
||||
|
||||
# check if qos policy is correctly created
|
||||
qosp = self._get_qos_policy(qos_policy_id)
|
||||
self._verify_qos_policy(qosp)
|
||||
|
||||
# check if respective qos rule is correctly created
|
||||
qos_rules = self._get_qos_rules(qos_policy_id)
|
||||
return qos_rules
|
||||
|
||||
def _create_nsp_with_qos_maxrate(self):
|
||||
return self.create_network_service_policy(
|
||||
name="testname",
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
"value": "1337",
|
||||
"name": "maxrate_only"}],
|
||||
expected_res_status=webob.exc.HTTPCreated.code)[
|
||||
'network_service_policy']
|
||||
|
||||
def _create_nsp_with_qos_both_rates(self):
|
||||
return self.create_network_service_policy(
|
||||
name="testname",
|
||||
network_service_params=[
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_BURST,
|
||||
"value": "2674",
|
||||
"name": "burstrate"},
|
||||
{"type": gconst.GP_NETWORK_SVC_PARAM_TYPE_QOS_MAX,
|
||||
"value": "1337",
|
||||
"name": "maxrate"}],
|
||||
expected_res_status=webob.exc.HTTPCreated.code)[
|
||||
'network_service_policy']
|
||||
|
||||
def _verify_qos_policy(self, qos_policy):
|
||||
self.assertEqual("gbp_testname", qos_policy['name'])
|
||||
|
||||
def _verify_qos_rule(self, qos_rules, max, burst=0):
|
||||
self.assertEqual(1, len(qos_rules))
|
||||
single_qos_rule = qos_rules[0]
|
||||
self.assertEqual(1337, single_qos_rule['max_kbps'])
|
||||
if burst:
|
||||
self.assertEqual(2674, single_qos_rule['max_burst_kbps'])
|
||||
|
||||
def _verify_update_ptg_with_nsp(self, ptg_id, nsp_id, ptg_subnet_no_nsp):
|
||||
ptg_subnet_id = ptg_subnet_no_nsp['id']
|
||||
initial_allocation_pool = ptg_subnet_no_nsp['allocation_pools']
|
||||
|
@ -51,12 +51,15 @@ class ResourceMappingStitchingPlumberGBPTestCase(
|
||||
'extension_drivers', ['proxy_group'], group='group_policy')
|
||||
cfg.CONF.set_override('node_plumber', 'stitching_plumber',
|
||||
group='node_composition_plugin')
|
||||
ml2_opts = {'mechanism_drivers': ['stitching_gbp']}
|
||||
ml2_opts = {'mechanism_drivers': ['stitching_gbp'],
|
||||
'extension_drivers': ['qos']}
|
||||
host_agents = mock.patch('neutron.plugins.ml2.driver_context.'
|
||||
'PortContext.host_agents').start()
|
||||
host_agents.return_value = [self.agent_conf]
|
||||
qos_plugin = 'qos'
|
||||
super(ResourceMappingStitchingPlumberGBPTestCase, self).setUp(
|
||||
sc_plugin=base.SC_PLUGIN_KLASS, ml2_options=ml2_opts)
|
||||
sc_plugin=base.SC_PLUGIN_KLASS, ml2_options=ml2_opts,
|
||||
qos_plugin=qos_plugin)
|
||||
|
||||
def get_plumbing_info(context):
|
||||
return info_mapping.get(context.current_profile['service_type'])
|
||||
|
126
gbpservice/tests/contrib/devstack/exercises/gbp_qos.sh
Executable file
126
gbpservice/tests/contrib/devstack/exercises/gbp_qos.sh
Executable file
@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# **gbp.sh**
|
||||
|
||||
# Sanity check that gbp started if enabled
|
||||
|
||||
echo "*********************************************************************"
|
||||
echo "Begin DevStack Exercise: $0"
|
||||
echo "*********************************************************************"
|
||||
|
||||
# Settings
|
||||
# ========
|
||||
|
||||
# This script exits on an error so that errors don't compound and you see
|
||||
# only the first error that occurred.
|
||||
set -o errexit
|
||||
|
||||
# Keep track of the current directory
|
||||
EXERCISE_DIR=$(cd $(dirname "$0") && pwd)
|
||||
TOP_DIR=$(cd $EXERCISE_DIR/..; pwd)
|
||||
|
||||
# Import common functions
|
||||
source $TOP_DIR/functions
|
||||
|
||||
# Import configuration
|
||||
source $TOP_DIR/openrc
|
||||
|
||||
# Import exercise configuration
|
||||
source $TOP_DIR/exerciserc
|
||||
|
||||
source $TOP_DIR/openrc demo demo
|
||||
|
||||
# Print the commands being run so that we can see the command that triggers
|
||||
# an error. It is also useful for following allowing as the install occurs.
|
||||
set -o xtrace
|
||||
|
||||
function confirm_server_active {
|
||||
local VM_UUID=$1
|
||||
if ! timeout $ACTIVE_TIMEOUT sh -c "while ! nova show $VM_UUID | grep status | grep -q ACTIVE; do sleep 1; done"; then
|
||||
echo "server '$VM_UUID' did not become active!"
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
# Create allow action that can used in several rules
|
||||
gbp policy-action-create allow --action-type allow
|
||||
|
||||
# Create ICMP rule
|
||||
gbp policy-classifier-create icmp-traffic --protocol icmp --direction bi
|
||||
gbp policy-rule-create ping-policy-rule --classifier icmp-traffic --actions allow
|
||||
|
||||
# ICMP policy-rule-set
|
||||
gbp policy-rule-set-create icmp-policy-rule-set --policy-rules ping-policy-rule
|
||||
|
||||
# ====== PROJECT OPERATION ======
|
||||
# PTGs creation
|
||||
gbp group-create limited
|
||||
gbp group-create unlimited
|
||||
|
||||
# PT creation
|
||||
PORT1=$(gbp policy-target-create port1-pt --policy-target-group limited | awk "/port_id/ {print \$4}")
|
||||
PORT2=$(gbp policy-target-create port2-pt --policy-target-group limited | awk "/port_id/ {print \$4}")
|
||||
PORT3=$(gbp policy-target-create port3-pt --policy-target-group unlimited | awk "/port_id/ {print \$4}")
|
||||
PORT4=$(gbp policy-target-create port4-pt --policy-target-group unlimited | awk "/port_id/ {print \$4}")
|
||||
|
||||
PORT1_VM_UUID=`nova boot --flavor m1.tiny --image $DEFAULT_IMAGE_NAME --nic port-id=$PORT1 port1-vm | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
|
||||
die_if_not_set $LINENO PORT1_VM_UUID "Failure launching port1-vm"
|
||||
confirm_server_active $PORT1_VM_UUID
|
||||
|
||||
PORT2_VM_UUID=`nova boot --flavor m1.tiny --image $DEFAULT_IMAGE_NAME --nic port-id=$PORT2 port2-vm | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
|
||||
die_if_not_set $LINENO PORT2_VM_UUID "Failure launching port2-vm"
|
||||
confirm_server_active $PORT2_VM_UUID
|
||||
|
||||
PORT3_VM_UUID=`nova boot --flavor m1.tiny --image $DEFAULT_IMAGE_NAME --nic port-id=$PORT3 port3-vm | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
|
||||
die_if_not_set $LINENO PORT3_VM_UUID "Failure launching port3-vm"
|
||||
confirm_server_active $PORT3_VM_UUID
|
||||
|
||||
PORT4_VM_UUID=`nova boot --flavor m1.tiny --image $DEFAULT_IMAGE_NAME --nic port-id=$PORT4 port4-vm | grep ' id ' | cut -d"|" -f3 | sed 's/ //g'`
|
||||
die_if_not_set $LINENO PORT4_VM_UUID "Failure launching port4-vm"
|
||||
confirm_server_active $PORT4_VM_UUID
|
||||
|
||||
####CHECKPOINT: No traffic flows between groups and no QoS applied
|
||||
|
||||
# policy-rule-set Association
|
||||
gbp group-update limited --consumed-policy-rule-sets "icmp-policy-rule-set"
|
||||
gbp group-update unlimited --provided-policy-rule-sets "icmp-policy-rule-set"
|
||||
|
||||
####CHECKPOINT: ICMP now flows between each group, but still no QoS applied
|
||||
|
||||
# Create Network Service Policy that includes QoS parameters
|
||||
gbp network-service-policy-create --network-service-params type=qos_burstrate,name=qos_burstrate,value=500 --network-service-params type=qos_maxrate,name=qos_maxrate,value=8000 "qos"
|
||||
|
||||
# Limit every PT in the limited PTG by associating the "qos" NSP created right before
|
||||
gbp group-update limited --network-service-policy "qos"
|
||||
|
||||
####CHECKPOINT: Both port1-pt and port2-pt will not be able to exceed 8 Mbps with a burst rate of 500 Kb
|
||||
|
||||
nova delete port4-vm
|
||||
nova delete port3-vm
|
||||
nova delete port2-vm
|
||||
nova delete port1-vm
|
||||
|
||||
if ! timeout $TERMINATE_TIMEOUT sh -c "while nova list | grep -q ACTIVE; do sleep 1; done"; then
|
||||
die $LINENO "Some VMs failed to shutdown"
|
||||
fi
|
||||
|
||||
gbp policy-target-delete port4-pt
|
||||
gbp policy-target-delete port3-pt
|
||||
gbp policy-target-delete port2-pt
|
||||
gbp policy-target-delete port1-pt
|
||||
|
||||
gbp group-delete unlimited
|
||||
gbp group-delete limited
|
||||
|
||||
gbp policy-rule-set-delete icmp-policy-rule-set
|
||||
|
||||
gbp policy-rule-delete ping-policy-rule
|
||||
|
||||
gbp policy-classifier-delete icmp-traffic
|
||||
|
||||
gbp policy-action-delete allow
|
||||
|
||||
set +o xtrace
|
||||
echo "*********************************************************************"
|
||||
echo "SUCCESS: End DevStack Exercise: $0"
|
||||
echo "*********************************************************************"
|
@ -6,7 +6,7 @@ RABBIT_PASSWORD=abc123
|
||||
SERVICE_PASSWORD=$ADMIN_PASSWORD
|
||||
SERVICE_TOKEN=abc123
|
||||
|
||||
Q_SERVICE_PLUGIN_CLASSES=neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,group_policy,ncp
|
||||
Q_SERVICE_PLUGIN_CLASSES=neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,group_policy,ncp,qos
|
||||
|
||||
|
||||
# Using group-policy branches
|
||||
@ -125,6 +125,9 @@ quota_security_group_rule = -1
|
||||
quota_router = -1
|
||||
quota_floatingip = -1
|
||||
|
||||
[agent]
|
||||
extensions = qos
|
||||
|
||||
[[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
|
||||
[ml2]
|
||||
extension_drivers = port_security
|
||||
extension_drivers = qos,port_security
|
||||
|
@ -6,7 +6,7 @@ RABBIT_PASSWORD=abc123
|
||||
SERVICE_PASSWORD=$ADMIN_PASSWORD
|
||||
SERVICE_TOKEN=abc123
|
||||
|
||||
Q_SERVICE_PLUGIN_CLASSES=neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,group_policy,ncp
|
||||
Q_SERVICE_PLUGIN_CLASSES=neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,group_policy,ncp,qos
|
||||
|
||||
|
||||
# Using group-policy branches
|
||||
@ -97,3 +97,10 @@ quota_security_group = -1
|
||||
quota_security_group_rule = -1
|
||||
quota_router = -1
|
||||
quota_floatingip = -1
|
||||
|
||||
[agent]
|
||||
extensions = qos
|
||||
|
||||
[[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
|
||||
[ml2]
|
||||
extension_drivers = qos
|
||||
|
Loading…
Reference in New Issue
Block a user