Merge remote-tracking branch 'origin/feature/qos' into merge-branch
Note to reviewers: gerrit diff for merge patches is very limited, and
leaving comments in global section won't scale, so please comment here:
https://etherpad.openstack.org/p/qos-merge-back-review
This merge commit introduces QoS feature into Liberty release of
Neutron.
The feature is documented in: doc/source/devref/quality_of_service.rst
included with the merge patch.
It includes:
- QoS API service plugin with QoS policy and QoS bandwidth limit
(egress) rule support;
- core plugin mechanism to determine supported rule types, with its ML2
implementation;
- new agent extension manager;
- QoS agent extension with pluggable backend QoS drivers (Open vSwitch
and SR-IOV support is included).
To extend network and port core resources with qos_policy_id attribute,
a new ML2 extension driver (qos) was introduced that relies on the QoS
core resource extension (the idea is that eventually we'll get a core
resource extension manager that can be directly reused by core plugins).
Agent-server interaction is based on:
- get_device_details() method that is extended with qos_policy_id;
- a new push/pull mechanism that allows agents and servers to
communicate using oslo.versionedobjects based objects sent on the
wire.
The merge includes the following types of test coverage:
- unit tests;
- functional tests for OVS agent, QoS agent extension, and low level
ovs_lib changes;
- API tests to cover port/network qos_policy_id attribute and new QoS
resources.
The client changes can be found at:
* https://review.openstack.org/189655
* https://review.openstack.org/198277
The team also prepared fullstack test but it needs to wait for client
merge before it can pass in the gate:
* https://review.openstack.org/202492
Gerrit does not show diff for merge changes that did not result in any
conflict, so to facilitate review, rely on the following steps:
- fetch the patch locally
- git fetch origin
- git diff origin/master...
This merge also disables qos extension API tests until the service is
enabled in master gate.
Local changes apart from conflicts:
- updated down_revision for qos migration to reflect master expand head;
- disabled qos API tests with gate_hook.sh until we have it enabled in
master gate;
- bumped oslo.versionedobjects requirement to reflect what is in
openstack/requirements' global-requirements.txt
DocImpact
APIImpact
Partially-Implements: blueprint quantum-qos-api
Partially-Implements: blueprint ml2-qos
Partially-Implements: blueprint ml2-qos-ovs-bwlimiting
Partially-Implements: blueprint ml2-sriov-qos-with-bwlimiting
Change-Id: I92916d0e391791187e9a25ff172fb4b3504857b1
diff --git a/neutron/tests/tempest/services/network/json/network_client.py b/neutron/tests/tempest/services/network/json/network_client.py
index 4badd96..3fb233e 100644
--- a/neutron/tests/tempest/services/network/json/network_client.py
+++ b/neutron/tests/tempest/services/network/json/network_client.py
@@ -11,6 +11,8 @@
# under the License.
import time
+import urllib
+
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse
@@ -65,7 +67,10 @@
'metering_label_rules': 'metering',
'firewall_rules': 'fw',
'firewall_policies': 'fw',
- 'firewalls': 'fw'
+ 'firewalls': 'fw',
+ 'policies': 'qos',
+ 'bandwidth_limit_rules': 'qos',
+ 'rule_types': 'qos',
}
service_prefix = service_resource_prefix_map.get(
plural_name)
@@ -90,7 +95,8 @@
'ikepolicy': 'ikepolicies',
'ipsec_site_connection': 'ipsec-site-connections',
'quotas': 'quotas',
- 'firewall_policy': 'firewall_policies'
+ 'firewall_policy': 'firewall_policies',
+ 'qos_policy': 'policies'
}
return resource_plural_map.get(resource_name, resource_name + 's')
@@ -620,3 +626,88 @@
self.expected_success(200, resp.status)
body = json.loads(body)
return service_client.ResponseBody(resp, body)
+
+ def list_qos_policies(self, **filters):
+ if filters:
+ uri = '%s/qos/policies?%s' % (self.uri_prefix,
+ urllib.urlencode(filters))
+ else:
+ uri = '%s/qos/policies' % self.uri_prefix
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return service_client.ResponseBody(resp, body)
+
+ def create_qos_policy(self, name, description, shared, tenant_id=None):
+ uri = '%s/qos/policies' % self.uri_prefix
+ post_data = {'policy': {
+ 'name': name,
+ 'description': description,
+ 'shared': shared
+ }}
+ if tenant_id is not None:
+ post_data['policy']['tenant_id'] = tenant_id
+ resp, body = self.post(uri, self.serialize(post_data))
+ body = self.deserialize_single(body)
+ self.expected_success(201, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def update_qos_policy(self, policy_id, **kwargs):
+ uri = '%s/qos/policies/%s' % (self.uri_prefix, policy_id)
+ post_data = self.serialize({'policy': kwargs})
+ resp, body = self.put(uri, post_data)
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def create_bandwidth_limit_rule(self, policy_id, max_kbps, max_burst_kbps):
+ uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
+ self.uri_prefix, policy_id)
+ post_data = self.serialize(
+ {'bandwidth_limit_rule': {
+ 'max_kbps': max_kbps,
+ 'max_burst_kbps': max_burst_kbps}
+ })
+ resp, body = self.post(uri, post_data)
+ self.expected_success(201, resp.status)
+ body = json.loads(body)
+ return service_client.ResponseBody(resp, body)
+
+ def list_bandwidth_limit_rules(self, policy_id):
+ uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
+ self.uri_prefix, policy_id)
+ resp, body = self.get(uri)
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def show_bandwidth_limit_rule(self, policy_id, rule_id):
+ uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ resp, body = self.get(uri)
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def update_bandwidth_limit_rule(self, policy_id, rule_id, **kwargs):
+ uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ post_data = {'bandwidth_limit_rule': kwargs}
+ resp, body = self.put(uri, json.dumps(post_data))
+ body = self.deserialize_single(body)
+ self.expected_success(200, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def delete_bandwidth_limit_rule(self, policy_id, rule_id):
+ uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
+ self.uri_prefix, policy_id, rule_id)
+ resp, body = self.delete(uri)
+ self.expected_success(204, resp.status)
+ return service_client.ResponseBody(resp, body)
+
+ def list_qos_rule_types(self):
+ uri = '%s/qos/rule-types' % self.uri_prefix
+ resp, body = self.get(uri)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return service_client.ResponseBody(resp, body)