Integration of Neutron Classifier and QoS DSCP¶
https://bugs.launchpad.net/neutron/+bug/1476527
Neutron Classifier(NC) is a service plugin which provides an API to define traffic classes for consumption by other Neutron services.
Quality of service(QoS) is an OpenStack Neutron extension which provides administrators with a service to specify and enforce SLAs based on drop priority, bandwidth policing and bandwidth guarantees at a port level.
By introducing the consumption of NC’s Classification Group resources into QoS’s Rule resources we can allow for a more targeted form of SLA allowing admins to limit, prioritize or guarantee traffic based on traffic classes rather than on a Neutron Port level. This spec will focus on drop priority based on traffic class per port.
Problem Description¶
Neutron’s Quality of Service extension allows for SLAs to be enforced at a Neutron port level, however it does not provide a resource to allow administrators to define SLAs for the different traffic classes entering or leaving the port.
This results in an all or nothing approach to traffic class SLAs, either all the traffic leaving a port recieves the same DSCP mark or the traffic retains its default DSCP mark assigned by the workload.
Example Use Cases:
Assigning a higher drop priority for traffic leaving TCP port YY than other traffic leaving the Neutron port.
Overriding an existing DSCP mark assigned by the instance destined for IPv6 address XX with UDP port YY specifically.
Proposed Change¶
In order to consume the classification resources of the Neutron Classifier in a way that does not involve the introduction of external dependencies in Neutron, the Neutron Classifier service plugin will require it to be migrated into the core Neutron repository.
Details of the resource models and API can be found here ‘NC API spec’_
The modifications required to Neutron’s QoS extension is minimal, it involves the introduction of a two variables to the DSCP Marking rule. The first variable takes an ID for a classification group (a collection of individual classifications) which the QoS backends can consume to target DSCP Marks to traffic classes as opposed to marking all traffic leaving the port.
The other, a priority value, will be a positive integer ranging from 0-1000 in ascending priority. This will allow admins to decide which DSCP marking rules should be applied in a case where 2 DSPC rule classifications overlap. eg:
openstack network qos policy create example_policy
openstack network qos rule create example_policy --type dscp-marking \
--dscp-mark 22
openstack network classification create ethernet --ethertype 0x0800 \
eth_ipv4
openstack network classification create ipv4 --protocol 6 \
ipv4_tcp
openstack network classification create tcp --dst-port-max 80 \
tcp_80
openstack network classification group create class_1 \
--classification tcp_80 ipv4_tcp eth_ipv4
openstack network qos rule create example_policy --type dscp-marking \
--dscp-mark 28 --classification-group-id class_1 \
--classification-priority 100
openstack network classification create ipv4 --dst-addr 10.0.0.5 \
ipv4_dst
openstack network classification group create class_2 \
--classification ipv4_dst eth_ipv4
openstack network qos rule create example_policy --type dscp-marking \
--dscp-mark 24 --classification-group-id class_2 \
--classification-priority 600
openstack network classification create ethernet --ethertype 0x86DD \
eth_ipv6
openstack network classification create ipv6 --next-header 17 \
--dst-addr 0:0:0:0:0:ffff:a00:5 ipv6_dst
openstack network classification create udp --dst-port-max 21 \
udp_21
openstack network classification group create class_3 \
--classification eth_ipv6 ipv6_dst udp_21
openstack network qos rule create example_policy --type dscp-marking \
--dscp-mark 8 --classification-group-id class_3 \
--classification-priority 158
openstack network classification create ethernet --ethertype 0x0806 \
eth_arp
openstack network classification group create class_4 \
--classification eth_arp
openstack network qos rule create example_policy --type dscp-marking \
--dscp-mark 40 --classification-group-id class_4 \
--classification-priority 999
openstack network qos rule list example_policy
+--------------------------------------+--------------------------------------+--------------+----------+-----------------+----------+-----------+-----------+--------------------------------------+----------+
| ID | QoS Policy ID | Type | Max Kbps | Max Burst Kbits | Min Kbps | DSCP mark | Direction | Classification Group | Priority |
+--------------------------------------+--------------------------------------+--------------+----------+-----------------+----------+-----------+-----------+--------------------------------------+----------+
| 153e4957-5c50-47f5-b934-c30eb3ada180 | 812c3e2e-cc86-4842-b41b-6b69419a123b | dscp_marking | | | | 40 | | ae0de79d-c26a-4090-8ec2-0d1ae62797f2 | 999 |
| 184d7156-ab93-444e-96eb-f91cf8c9160e | 812c3e2e-cc86-4842-b41b-6b69419a123b | dscp_marking | | | | 8 | | b0fd0ca7-8596-4053-b69b-630016acfd28 | 158 |
| 385970f8-9a6c-4fe2-a328-95630138c9ca | 812c3e2e-cc86-4842-b41b-6b69419a123b | dscp_marking | | | | 28 | | 6adb4199-e42f-4fa9-8ab6-2bf64e2ea68d | 100 |
| 7fa581ac-8b82-4d64-9032-52b1ce9f3509 | 812c3e2e-cc86-4842-b41b-6b69419a123b | dscp_marking | | | | 22 | | | 0 |
| d1c5268b-302c-498b-bfd7-484e19fd28af | 812c3e2e-cc86-4842-b41b-6b69419a123b | dscp_marking | | | | 24 | | cd0a1ea2-0cdf-42b0-a4fc-7f581d4a4be2 | 600 |
+--------------------------------------+--------------------------------------+--------------+----------+-----------------+----------+-----------+-----------+--------------------------------------+----------+
Without a clear priority between the above classes there are instances where classes class_1, class_3, class_4 and class_5 will also match class_2. Depending on the backend this can result in unpredictible behavior without a clear prioritization mechanism between traffic classes.
To accomodate these changes, modifications will need to be made in the backend to allow drivers to retrieve the classification definitions from the service plugin. This will be achieved through the extension of the agent extensions API. By extending this API all extensions will have access to classifications without having to introduce any additional resources.
Data Model Impact¶
In order to provide consistent functionality on upgrade the classification_group_id defaults to None, meaning a DSCP marking rule will behave the same way it did before the introduction of this feature.
The proposed change would result in the model looking like this:
Attribute Name |
Type |
Access |
Default Value |
Validation/ Conversion |
Description |
---|---|---|---|---|---|
id |
string (UUID) |
RO, all |
N/A |
uuid |
QoSRule identifier |
qos_policy_id |
string (UUID) |
RO, all |
N/A |
uuid |
QoSPolicy reference UUID |
dscp_mark |
integer (enum) |
RW, tenant |
N/A |
0 and 56, except 2-6, 42, 44, and 50-54 |
DSCP Mark |
classification_group_id |
string (UUID) |
RW, tenant |
None |
uuid |
Classification Group reference UUID |
classification_priority |
integer |
RW, tenant |
0 |
A positive integer |
Positive integer to denote prioity of this rule |
Another modification is the removal of the of the unique contraint on the qos_policy_id. This has been replaced by a unique constraint on the qos_policy_id and classification_group_id as a tuple. This is to allow multiple DSCP rules with different classifications within the same policy, this includes when the classification_group_id is None. As a result, a QosPolicy can only have a single DSCPMarkingRule with classification_group_id set to None.
Neutron DB table: QosDSCPMarkingRule
id: primary key
qos_policy_id: foreign key to QosPolicy.id
dscp_mark: Integer within a defined set of values.
classification_group_id: foreign key to ClassificationGroup.id
classification_priority: positive integer between 0 and 1000
Before delving into further detail in regards to the data model, API and how classifications can be used, a few points need to be clarified:
1 Classification is of a single type, e.g. either Ethernet, IP, HTTP, or another supported at the time of a specific CCF release. The definition, i.e. fields to match on, depends on the type specified.
To clarify, Classification Types define the set of possible fields and values for a Classification (essentially, an instance of that Classification Type). Classification Types are defined in code, where Classifications are created via the REST API as instances of those types.
Not all supported fields need to be defined - only the ones required by the Consuming Service - which it should validate on consumption.
There are also Classification Groups, which allow Classifications or other Classification Groups to be grouped together using boolean operators. CGs are the resources that will end up being consumed by Consuming Services.
From the Consuming Service’s point of view, Classifications can only be read, not created or deleted. They need to have been previously created using the User-facing Classifications API.
As Neutron Classifier is unable to force an update action to it’s consumers and as users may be unaware of all the consumers of a classification, the classification “definition” and “type” fields and the classification group “classifications”, “classification_groups” and “operator” fields cannot be updated after creation. ie. A classification is consumed by both QoS and Security Groups with a user only knowing about QoS consuming the classification.
The initial model of the CCF will include the following Classification Types: Ethernet, IPv4, IPv6, TCP and UDP, which when combined are sufficient to provide any 5-tuple classification.
The following table presents the attributes of a Classification Group (asterisk on RW means that the attribute is non-updatable):
Attribute Name
Type
Access CRUD
Default Value
Validation/ Conversion
Description
id
string (UUID)
RO, all
generated
uuid
Identity
project_id
string (UUID)
RO, project
from auth token
uuid
Project ID
name
string
RW, project
None
string
Name of Classification Group
description
string
RW, project
None
string
Human-readable description
shared
bool
RW, project
False
boolean
Shared with other projects
operator
string (values)
RW*, project
“and”
- [“and”,
“or”]
Boolean connective: AND/OR
classification_groups
list
RW*, project
[]
List of Classification Groups included
classifications
list
RW* project
[]
List of Classifications included
Consuming Services will consume Classification Groups, and not atomic Classifications, any Classification needs to be grouped in a Classification Group to be consumed individually. As such, the “operator” field is to be ignored for Classification Groups that only contain 1 Classification inside.
The following table presents the attributes of Classifications of any of the types stated in this spec (asterisk on RW means that the attribute is non-updatable):
Attribute Name
Type
Access
Default Value
Validation/ Conversion
Description
id
string (UUID)
RO, all
generated
uuid
Identity
project_id
string (UUID)
RO, project
from auth token
uuid
Project ID
name
string
RW, project
None
string
Name of Classification
description
string
RW, project
None
string
Human-readable description
type
string
RW*, project
from enum of types
The type of the Classification
definition
type-specific attributes will go here, given their volume I won’t detail them unless requested.
Classification Groups and Classifications of every type will be stored as the
following tables and relationships (with table name prefix ccf_
):
+---------------------+
|classification_groups|
+---------------------+
|id |*
|cg_id +--------+
|name | |
|description | |
|project_id | |
|shared +--------+
|operator |1
+---------------------+
|1
|
|*
+------------------------------+
|classification_groups_mapping |
+------------------------------+
|cg_id |
|classification_id |
+------------------------------+
|1
+--------------------+ | +--------------------+
|ipv4_classifications| | |ipv6_classifications|
+--------------------+ | +--------------------+
|classification_id | | |classification_id |
|ihl |1 | 1|traffic_class |
|diffserv +--------+ | +--------+traffic_class_mask |
|diffserv_mask | | | | |length |
|length | | | | |next_header |
|flags | | | | |hops |
|flags_mask | | | | |src_addr |
|ttl | |1 |1 1| |dst_addr |
|protocol | +---------------------+ +--------------------+
|src_addr | |classifications |
|dst_addr | +---------------------+
|options | |id |
|options_mask | |name |
+--------------------+ |description |
|project_id |
|shared | +-------------------+
|type | |tcp_classifications|
+---------------------+ +-------------------+
1| 1| |1 |classification_id |
+-------------------+ | | | |src_port |
|udp_classifications| | | | |dst_port |
+-------------------+ | | | |flags |
|classification_id |1 | | | 1|flags_mask |
|src_port +---------+ | +--------+window |
|dst_port | 1| |data_offset |
|length | +------------------------+ |option_kind |
|window_size | |ethernet_classifications| +-------------------+
+-------------------+ +------------------------+
|classification_id |
|preamble |
|src_addr |
|dst_addr |
|ethertype |
+------------------------+
Masking fields allow the user to specify which individual bits of the respective main field should be looked up during classification.
Classification Types are used to select the appropriate model of the Classification and consequently what table it will be stored in.
Classification Groups get stored in a single table and can point to other Classification Groups, to allow mixing boolean operators.
operator
in Classification Group: specifies the boolean operator used
to connect all the child Classifications and Classification Groups of that
group. This can be either AND or OR.
OR: Logical OR, the classifications contained in these groups are inter-changable within the traffic class ie. ipv4 dst_addr y.y.y.y or ipv6 dst_addr Z::ZZZZ.
AND: logical AND, the classifications or classification groups are used to create a traffic class, the composition of this classification can be made up of Classifications or OR operand ClassificationGroups.
REST API Impact¶
Proposed attribute:
SUB_RESOURCE_ATTRIBUTE_MAP = {
'dscp_marking_rules':{
'parent': {'collection_name': 'policies',
'member_name': 'policy'},
'parameters': dict(QOS_RULE_COMMON_FIELDS,
**{'dscp_mark': {
'allow_post': True, 'allow_put': True,
'convert_to': attr.convert_to_int,
'is_visible': True, 'default': None,
'validate': {'type:values': common_constants.
VALID_DSCP_MARKS}
},
'classification_group_id': {
'allow_post': True,
'allow_put': True,
'is_visible': True,
'default': None,
'validate': {'type:uuid_or_none': None}
},
'classification_priority': {
'allow_post': True,
'allow_put': True,
'convert_to': attr.convert_to_int,
'is_visible': True,
'default': 0,
'validate': {'type:values': range(1000)}
}
}
Sample REST calls:
GET /v2.0/qos/policies
Response:
{
"policy": {
"name": "AF32",
"project_id": "<project-id>",
"id": "<id>",
"description": "This policy marks DSCP outgoing AF32 traffic for DTV Control",
"shared": "False"
}
}
GET /v2.0/qos/policies/<policy-uuid>
Response:
{
"policy": {
"tenant_id": "<tenant-id>",
"id": "<id>",
"name": "AF32",
"description": "This policy marks DSCP outgoing AF32 traffic for DTV Control",
"shared": False,
"dscp_marking_rules": [{
"id": "<id>",
"policy_id": "<policy-uuid>",
"dscp_mark": 16,
"classification_group_id": "<classification_group_id>",
"classification_priority": 800
}]
}
}
POST /v2.0/qos/policies/<policy-uuid>/dscp-marking-rules/
{
"dscp_marking_rule": {
"dscp_mark": 16
}
}
Response:
{
"dscp_marking_rule":{
"id": "<id>",
"policy_id": "<policy-uuid>",
"dscp_mark": 16,
"classification_group_id": None,
"classification_priority": 0
}
}
GET /v2.0/classification_groups/<classification_group_id>
{
"classification_group":{
"id": "<classification_group_id>",
"project_id": "<project_id>",
"name": "ipv4-group"
"description": "",
"classification": [
{
"id":"<classification_id>",
"name":"ipv4-dscp"
"project_id":"<project_id>",
"c_type": "ipv4",
"description": "",
"shared":false,
}
],
"classification_group": [],
"operator": "AND",
"shared": false,
}
}
PUT /v2.0/qos/policies/<policy-uuid>/dscp-marking-rules/<rule-uuid>
{
"dscp_marking_rule": {
"dscp_mark": 8,
"classification_group_id": "<classification_group_id>"
}
}
Response:
{
"dscp_marking_rule":{
"id": "<id>",
"policy_id": "<policy-uuid>",
"dscp_mark": 8,
"classification_group_id": "<classification_group_id>",
"classification_priority": 0
}
}
Documentation Impact¶
User Documentation¶
Existing Networking Guide will be updated for this feature.
Existing CLI guide will be updated for this feature.
Developer Documentation¶
Existing QoS devref document will be updated for this feature. A New Document will be drafted for the Neutron Classifier API also.
API Documentation¶
Existing QoS API documentation will be updated for this feature. A New Document will be drafted for the Neutron Classifier API also.