From: Jakub Libosvar Date: Wed, 15 Jul 2015 10:46:35 +0000 (+0000) Subject: Add QoS fullstack test X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=1b94f3f3d9c84a20f46000c0801eeb8bad84a6fb;p=openstack-build%2Fneutron-build.git Add QoS fullstack test 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 --- diff --git a/neutron/tests/fullstack/resources/client.py b/neutron/tests/fullstack/resources/client.py index d51fc3df5..b781c8e14 100644 --- a/neutron/tests/fullstack/resources/client.py +++ b/neutron/tests/fullstack/resources/client.py @@ -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'] diff --git a/neutron/tests/fullstack/resources/config.py b/neutron/tests/fullstack/resources/config.py index c39ed2bc5..c20d24087 100644 --- a/neutron/tests/fullstack/resources/config.py +++ b/neutron/tests/fullstack/resources/config.py @@ -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') diff --git a/neutron/tests/fullstack/resources/environment.py b/neutron/tests/fullstack/resources/environment.py index 152b7a447..a70aa9136 100644 --- a/neutron/tests/fullstack/resources/environment.py +++ b/neutron/tests/fullstack/resources/environment.py @@ -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): diff --git a/neutron/tests/fullstack/resources/machine.py b/neutron/tests/fullstack/resources/machine.py index 355332220..170678a88 100644 --- a/neutron/tests/fullstack/resources/machine.py +++ b/neutron/tests/fullstack/resources/machine.py @@ -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 index 000000000..e44e63db1 --- /dev/null +++ b/neutron/tests/fullstack/test_qos.py @@ -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)