]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add QoS fullstack test
authorJakub Libosvar <libosvar@redhat.com>
Wed, 15 Jul 2015 10:46:35 +0000 (10:46 +0000)
committerJakub Libosvar <libosvar@redhat.com>
Tue, 15 Sep 2015 15:42:26 +0000 (15:42 +0000)
Test the qos policy and rule CRUD lifecycle with port. Future plans are
to add similar testing with ports belonging to network with set qos
policy.

Change-Id: Iebe9b3e9d612d3533381a8cf4d0b9c587f8fda42

neutron/tests/fullstack/resources/client.py
neutron/tests/fullstack/resources/config.py
neutron/tests/fullstack/resources/environment.py
neutron/tests/fullstack/resources/machine.py
neutron/tests/fullstack/test_qos.py [new file with mode: 0644]

index d51fc3df5e326b6cfc566c5c127f38c4eb09a1c0..b781c8e1419c9f4aa9244b857dea459b37410767 100644 (file)
@@ -77,15 +77,58 @@ class ClientFixture(fixtures.Fixture):
 
         return self._create_resource(resource_type, spec)
 
-    def create_port(self, tenant_id, network_id, hostname):
-        return self._create_resource(
-            'port',
-            {'network_id': network_id,
-             'tenant_id': tenant_id,
-             'binding:host_id': hostname})
+    def create_port(self, tenant_id, network_id, hostname, qos_policy_id=None):
+        spec = {
+            'network_id': network_id,
+            'tenant_id': tenant_id,
+            'binding:host_id': hostname,
+        }
+        if qos_policy_id:
+            spec['qos_policy_id'] = qos_policy_id
+        return self._create_resource('port', spec)
 
     def add_router_interface(self, router_id, subnet_id):
         body = {'subnet_id': subnet_id}
         self.client.add_interface_router(router=router_id, body=body)
         self.addCleanup(_safe_method(self.client.remove_interface_router),
                         router=router_id, body=body)
+
+    def create_qos_policy(self, tenant_id, name, description, shared):
+        policy = self.client.create_qos_policy(
+            body={'policy': {'name': name,
+                             'description': description,
+                             'shared': shared,
+                             'tenant_id': tenant_id}})
+
+        def detach_and_delete_policy():
+            qos_policy_id = policy['policy']['id']
+            ports_with_policy = self.client.list_ports(
+                qos_policy_id=qos_policy_id)['ports']
+            for port in ports_with_policy:
+                self.client.update_port(
+                    port['id'],
+                    body={'port': {'qos_policy_id': None}})
+            self.client.delete_qos_policy(qos_policy_id)
+
+        # NOTE: We'll need to add support for detaching from network once
+        # create_network() supports qos_policy_id.
+        self.addCleanup(_safe_method(detach_and_delete_policy))
+
+        return policy['policy']
+
+    def create_bandwidth_limit_rule(self, tenant_id, qos_policy_id, limit=None,
+                                    burst=None):
+        rule = {'tenant_id': tenant_id}
+        if limit:
+            rule['max_kbps'] = limit
+        if burst:
+            rule['max_burst_kbps'] = burst
+        rule = self.client.create_bandwidth_limit_rule(
+            policy=qos_policy_id,
+            body={'bandwidth_limit_rule': rule})
+
+        self.addCleanup(_safe_method(self.client.delete_bandwidth_limit_rule),
+                        rule['bandwidth_limit_rule']['id'],
+                        qos_policy_id)
+
+        return rule['bandwidth_limit_rule']
index c39ed2bc5aea0c55ecdd1f6cf68771e496b14f15..c20d24087bfe4e95ba05e9a73e4322819822f983 100644 (file)
@@ -103,6 +103,10 @@ class NeutronConfigFixture(ConfigFixture):
         super(NeutronConfigFixture, self).__init__(
             env_desc, host_desc, temp_dir, base_filename='neutron.conf')
 
+        service_plugins = ['router']
+        if env_desc.qos:
+            service_plugins.append('qos')
+
         self.config.update({
             'DEFAULT': {
                 'host': self._generate_host(),
@@ -112,8 +116,7 @@ class NeutronConfigFixture(ConfigFixture):
                 'api_paste_config': self._generate_api_paste(),
                 'policy_file': self._generate_policy_json(),
                 'core_plugin': 'neutron.plugins.ml2.plugin.Ml2Plugin',
-                'service_plugins': ('neutron.services.l3_router.'
-                                    'l3_router_plugin.L3RouterPlugin'),
+                'service_plugins': ','.join(service_plugins),
                 'auth_strategy': 'noauth',
                 'verbose': 'True',
                 'debug': 'True',
@@ -179,6 +182,9 @@ class ML2ConfigFixture(ConfigFixture):
             },
         })
 
+        if env_desc.qos:
+            self.config['ml2']['extension_drivers'] = 'qos'
+
 
 class OVSConfigFixture(ConfigFixture):
 
@@ -214,6 +220,9 @@ class OVSConfigFixture(ConfigFixture):
             self.config['ovs']['bridge_mappings'] = (
                 self._generate_bridge_mappings())
 
+        if env_desc.qos:
+            self.config['agent']['extensions'] = 'qos'
+
     def _generate_bridge_mappings(self):
         return 'physnet1:%s' % base.get_rand_device_name(prefix='br-eth')
 
index 152b7a4473cdc79f429448acc4db92cf8656dfff..a70aa9136167e12f44be422c55a91ece30916abf 100644 (file)
@@ -34,9 +34,10 @@ class EnvironmentDescription(object):
 
     Does the setup, as a whole, support tunneling? How about l2pop?
     """
-    def __init__(self, network_type='vxlan', l2_pop=True):
+    def __init__(self, network_type='vxlan', l2_pop=True, qos=False):
         self.network_type = network_type
         self.l2_pop = l2_pop
+        self.qos = qos
 
     @property
     def tunneling_enabled(self):
index 3553322203deb3c9f436c3aa3972a48842b9c8b1..170678a888bc9a041985ec87571888429540a5dc 100644 (file)
@@ -20,21 +20,24 @@ from neutron.tests.common import net_helpers
 
 
 class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
-    def __init__(self, host, network_id, tenant_id, safe_client):
+    def __init__(self, host, network_id, tenant_id, safe_client,
+                 neutron_port=None):
         super(FakeFullstackMachine, self).__init__()
         self.bridge = host.ovs_agent.br_int
         self.host_binding = host.hostname
         self.tenant_id = tenant_id
         self.network_id = network_id
         self.safe_client = safe_client
+        self.neutron_port = neutron_port
 
     def _setUp(self):
         super(FakeFullstackMachine, self)._setUp()
 
-        self.neutron_port = self.safe_client.create_port(
-            network_id=self.network_id,
-            tenant_id=self.tenant_id,
-            hostname=self.host_binding)
+        if not self.neutron_port:
+            self.neutron_port = self.safe_client.create_port(
+                network_id=self.network_id,
+                tenant_id=self.tenant_id,
+                hostname=self.host_binding)
         self.neutron_port_id = self.neutron_port['id']
         mac_address = self.neutron_port['mac_address']
 
diff --git a/neutron/tests/fullstack/test_qos.py b/neutron/tests/fullstack/test_qos.py
new file mode 100644 (file)
index 0000000..e44e63d
--- /dev/null
@@ -0,0 +1,119 @@
+# Copyright 2015 Red Hat, Inc.
+#
+#    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.
+
+from oslo_utils import uuidutils
+
+from neutron.agent.linux import utils
+from neutron.services.qos import qos_consts
+from neutron.tests.fullstack import base
+from neutron.tests.fullstack.resources import environment
+from neutron.tests.fullstack.resources import machine
+
+
+BANDWIDTH_LIMIT = 500
+BANDWIDTH_BURST = 100
+
+
+def _wait_for_rule_applied(vm, limit, burst):
+    utils.wait_until_true(
+        lambda: vm.bridge.get_egress_bw_limit_for_port(
+            vm.port.name) == (limit, burst))
+
+
+def _wait_for_rule_removed(vm):
+    # No values are provided when port doesn't have qos policy
+    _wait_for_rule_applied(vm, None, None)
+
+
+class TestQoSWithOvsAgent(base.BaseFullStackTestCase):
+
+    def setUp(self):
+        host_desc = [environment.HostDescription(l3_agent=False)]
+        env_desc = environment.EnvironmentDescription(qos=True)
+        env = environment.Environment(env_desc, host_desc)
+        super(TestQoSWithOvsAgent, self).setUp(env)
+
+    def _create_qos_policy(self):
+        return self.safe_client.create_qos_policy(
+            self.tenant_id, 'fs_policy', 'Fullstack testing policy',
+            shared='False')
+
+    def _prepare_vm_with_qos_policy(self, limit, burst):
+        qos_policy = self._create_qos_policy()
+        qos_policy_id = qos_policy['id']
+
+        rule = self.safe_client.create_bandwidth_limit_rule(
+            self.tenant_id, qos_policy_id, limit, burst)
+        # Make it consistent with GET reply
+        qos_policy['rules'].append(rule)
+        rule['type'] = qos_consts.RULE_TYPE_BANDWIDTH_LIMIT
+        rule['qos_policy_id'] = qos_policy_id
+
+        port = self.safe_client.create_port(
+            self.tenant_id, self.network['id'],
+            self.environment.hosts[0].hostname,
+            qos_policy_id)
+
+        vm = self.useFixture(
+            machine.FakeFullstackMachine(
+                self.environment.hosts[0],
+                self.network['id'],
+                self.tenant_id,
+                self.safe_client,
+                neutron_port=port))
+
+        return vm, qos_policy
+
+    def test_qos_policy_rule_lifecycle(self):
+        new_limit = BANDWIDTH_LIMIT + 100
+        new_burst = BANDWIDTH_BURST + 50
+
+        self.tenant_id = uuidutils.generate_uuid()
+        self.network = self.safe_client.create_network(self.tenant_id,
+                                                       'network-test')
+        self.subnet = self.safe_client.create_subnet(
+            self.tenant_id, self.network['id'],
+            cidr='10.0.0.0/24',
+            gateway_ip='10.0.0.1',
+            name='subnet-test',
+            enable_dhcp=False)
+
+        # Create port with qos policy attached
+        vm, qos_policy = self._prepare_vm_with_qos_policy(BANDWIDTH_LIMIT,
+                                                          BANDWIDTH_BURST)
+        _wait_for_rule_applied(vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST)
+        qos_policy_id = qos_policy['id']
+        rule = qos_policy['rules'][0]
+
+        # Remove rule from qos policy
+        self.client.delete_bandwidth_limit_rule(rule['id'], qos_policy_id)
+        _wait_for_rule_removed(vm)
+
+        # Create new rule
+        new_rule = self.safe_client.create_bandwidth_limit_rule(
+            self.tenant_id, qos_policy_id, new_limit, new_burst)
+        _wait_for_rule_applied(vm, new_limit, new_burst)
+
+        # Update qos policy rule id
+        self.client.update_bandwidth_limit_rule(
+            new_rule['id'], qos_policy_id,
+            body={'bandwidth_limit_rule': {'max_kbps': BANDWIDTH_LIMIT,
+                                           'max_burst_kbps': BANDWIDTH_BURST}})
+        _wait_for_rule_applied(vm, BANDWIDTH_LIMIT, BANDWIDTH_BURST)
+
+        # Remove qos policy from port
+        self.client.update_port(
+            vm.neutron_port['id'],
+            body={'port': {'qos_policy_id': None}})
+        _wait_for_rule_removed(vm)