From aeaa1eec9ccf3570f82aecaafa8dd64efd2e67f2 Mon Sep 17 00:00:00 2001 From: Deepthi Kandavara Jayarama Date: Fri, 5 Jan 2018 09:18:00 -0800 Subject: [PATCH] [QOS] Scenario tests based on new tempest design Currently includes tests for below scenarios - Test bandwidth-limit rule for both network and port - Test dscp rule for both network and port - Test qos policy with both rules for both network and port - Resolved the conflict with fwaas change Change-Id: I1d7dc355632ea05dcbb80cdf1ea41fbc380fb5bc --- vmware_nsx_tempest/lib/feature_manager.py | 142 ++++- vmware_nsx_tempest/lib/traffic_manager.py | 127 +++++ vmware_nsx_tempest/services/nsx_client.py | 44 ++ .../services/openstack_network_clients.py | 114 ++++ vmware_nsx_tempest/tests/scenario/test_qos.py | 494 ++++++++++++++++++ 5 files changed, 920 insertions(+), 1 deletion(-) create mode 100644 vmware_nsx_tempest/tests/scenario/test_qos.py diff --git a/vmware_nsx_tempest/lib/feature_manager.py b/vmware_nsx_tempest/lib/feature_manager.py index 6e65acb..36653f7 100644 --- a/vmware_nsx_tempest/lib/feature_manager.py +++ b/vmware_nsx_tempest/lib/feature_manager.py @@ -38,9 +38,12 @@ LOG = constants.log.getLogger(__name__) CONF = config.CONF +RULE_TYPE_BANDWIDTH_LIMIT = "bandwidth_limit" +RULE_TYPE_DSCP_MARK = "dscp_marking" + # It includes feature related function such CRUD Mdproxy, L2GW or QoS -class FeatureManager(traffic_manager.TrafficManager): +class FeatureManager(traffic_manager.IperfManager): @classmethod def setup_clients(cls): """Create various client connections. Such as NSXv3 and L2 Gateway. @@ -86,6 +89,24 @@ class FeatureManager(traffic_manager.TrafficManager): net_client.region, net_client.endpoint_type, **_params) + cls.qos_policy_client = openstack_network_clients.QosPoliciesClient( + net_client.auth_provider, + net_client.service, + net_client.region, + net_client.endpoint_type, + **_params) + cls.qos_bw_client = openstack_network_clients.QosBWLimitClient( + net_client.auth_provider, + net_client.service, + net_client.region, + net_client.endpoint_type, + **_params) + cls.qos_dscp_client = openstack_network_clients.QosDscpClient( + net_client.auth_provider, + net_client.service, + net_client.region, + net_client.endpoint_type, + **_params) # # FwaasV2 base class @@ -482,3 +503,122 @@ class FeatureManager(traffic_manager.TrafficManager): if port_info['port']['device_owner'] == "network:router_interface": return port_info['port']['id'] return None + + # + # QoS base class. To get basics of QoS. + # + def get_qos_policy_id(self, policy_id_or_name): + policies = self.qos_policy_client.list_policies(name=policy_id_or_name) + policy_list = policies['policies'] + if len(policy_list) > 0: + return policy_list[0]['id'] + return policy_id_or_name + + def create_qos_policy(self, name, description, shared, **kwargs): + result = self.qos_policy_client.create_policy( + name=name, + description=description, + shared=shared, + **kwargs + ) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.qos_policy_client.delete_policy, + result['policy']['id']) + return result.get('policy', result) + + def delete_qos_policy(self, policy_id): + result = self.qos_policy_client.delete_policy(policy_id) + return result.get('policy', result) + + def list_qos_policies(self, **filters): + result = self.qos_policy_client.list_policies(**filters) + return result.get('policies', result) + + def update_qos_policy(self, policy_id, **kwargs): + result = self.qos_policy_client.update_policy(policy_id, **kwargs) + return result.get('policy', result) + + def show_qos_policy(self, policy_id, **fields): + result = self.qos_policy_client.show_policy(policy_id, **fields) + return result.get('policy', result) + + # + # QoS bandwidth_limit + # + def create_bandwidth_limit_rule(self, policy_id, + max_kbps, max_burst_kbps, + **kwargs): + result = self.qos_bw_client.create_bandwidth_limit_rule( + policy_id, + max_kbps=max_kbps, max_burst_kbps=max_burst_kbps, + **kwargs) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.qos_bw_client.delete_bandwidth_limit_rule, + result['bandwidth_limit_rule']['id'], policy_id) + return result.get('bandwidth_limit_rule', result) + + def delete_bandwidth_limit_rule(self, rule_id, policy_id): + result = self.qos_bw_client.delete_bandwidth_limit_rule( + rule_id, policy_id) + return result.get('bandwidth_limit_rule', result) + + def update_bandwidth_limit_rule(self, rule_id, policy_id_or_name, + **kwargs): + policy_id = self.get_qos_policy_id(policy_id_or_name) + result = self.qos_bw_client.update_bandwidth_limit_rule( + rule_id, policy_id, **kwargs) + return result.get('bandwidth_limit_rule', result) + + def list_bandwidth_limit_rules(self, policy_id, **filters): + result = self.qos_bw_client.list_bandwidth_limit_rules( + policy_id, **filters) + return result.get('bandwidth_limit_rules', result) + + def show_bandwidth_limit_rule(self, rule_id, policy_id, + **fields): + result = self.qos_bw_client.show_bandwidth_limit_rule( + rule_id, policy_id) + return result.get('bandwidth_limit_rule', result) + + # + # QoS DSCP Marking Rule + # + def create_dscp_marking_rule(self, policy_id, dscp_mark, + **kwargs): + policy_id = self.get_qos_policy_id(policy_id) + kwargs['dscp_mark'] = dscp_mark + result = self.qos_dscp_client.create_dscp_marking_rule( + policy_id, **kwargs) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.qos_dscp_client.delete_dscp_marking_rule, + result['dscp_marking_rule']['id'], policy_id) + return result.get('dscp_marking_rule', result) + + def delete_dscp_marking_rule(self, rule_id, policy_id_or_name): + policy_id = self.get_qos_policy_id(policy_id_or_name) + result = self.qos_dscp_client.delete_dscp_marking_rule(rule_id, + policy_id) + return result.get('dscp_marking_rule', result) + + def update_dscp_marking_rule(self, rule_id, policy_id_or_name, + **kwargs): + policy_id = self.get_qos_policy_id(policy_id_or_name) + result = self.qos_dscp_client.update_dscp_marking_rule( + rule_id, policy_id, **kwargs) + return result.get('dscp_marking_rule', result) + + def list_dscp_marking_rules(self, policy_id_or_name, **filters): + policy_id = self.get_qos_policy_id(policy_id_or_name) + result = self.qos_dscp_client.list_dscp_marking_rules( + policy_id, **filters) + return result.get('dscp_marking_rules', result) + + def show_dscp_marking_rule(self, rule_id, policy_id_or_name, **fields): + policy_id = self.get_qos_policy_id(policy_id_or_name) + result = self.qos_dscp_client.show_dscp_marking_rule( + rule_id, policy_id, **fields) + return result.get('dscp_marking_rule', result) + + def list_rule_types(self): + result = self.types_client.list_rule_types() + return result.get('rule_types', result) diff --git a/vmware_nsx_tempest/lib/traffic_manager.py b/vmware_nsx_tempest/lib/traffic_manager.py index ca57414..84d25b9 100644 --- a/vmware_nsx_tempest/lib/traffic_manager.py +++ b/vmware_nsx_tempest/lib/traffic_manager.py @@ -238,3 +238,130 @@ class TrafficManager(appliance_manager.ApplianceManager): raise exceptions.TimeoutException("Timed out while waiting to " "execute cmd %s on server. " % cmd) + + +class IperfManager(TrafficManager): + + traffic = 'tcp' + + def set_iperf_server(self, ssh_source, traffic_type): + # set up iperf server on VM + LOG.info("Check if iperf is installed") + cmd = ('iperf -v 2>&1 || true') + response = ssh_source.exec_command(cmd) + if "command not found" in response: + raise Exception('IPERF not installed') + LOG.info("Setting up iperf server") + if traffic_type == 'udp': + self.traffic = 'udp' + cmd = ('iperf -p 49162 -s -u > /dev/null 2>&1 &') + else: + cmd = ('iperf -p 49162 -s > /dev/null 2>&1 &') + ssh_source.exec_command(cmd) + + def set_iperf_client(self, ssh_source, destination_ip, + traffic_send_rate='1', traffic_duration='1'): + """set up iperf client""" + if self.traffic == 'udp': + cmd = ('iperf -p 49162 -c %s -b %sM -t 1 -u | grep %%' + % (unicode(destination_ip), unicode(traffic_send_rate))) + else: + cmd = ('iperf -p 49162 -c %s -b %sM -t 1 | grep %%' + % (unicode(destination_ip), unicode(traffic_send_rate))) + output = ssh_source.exec_command(cmd) + if output is None or float(output.split()[7]) < 0: + LOG.error('Incorrect IPERF output %s' % output) + return -1 + else: + return output.split()[7] + + def kill_iperf_process(self, ssh_source): + """To kill iperf process on server""" + cmd = ('ps -ef | grep iperf ') + output = ssh_source.exec_command(cmd) + for line in output.splitlines(): + if 'iperf -p 49162 -s' in line: + LOG.info("Killing iperf process") + iperf_process_id = line.split()[1] + cmd = ('kill %s' % (unicode(iperf_process_id))) + ssh_source.exec_command(cmd) + + def kill_tcpdump_process(self, ssh_source): + """To kill tcpdump process""" + cmd = ('ps -ef | grep tcpdump') + output = ssh_source.exec_command(cmd) + for line in output.splitlines(): + if 'tcpdump -ni eth0 -w' in line: + LOG.info("Killing TCPDUMP process") + tcpdump_process_id = line.split()[1] + cmd = ('kill %s' % (unicode(tcpdump_process_id))) + ssh_source.exec_command(cmd) + + def use_iperf_send_traffic( + self, src_server, dst_server, send_rate, traffic_type): + """To send iperf traffic between src server and dst server + and capture the received traffic at the destination + """ + src_ssh_source = self._get_remote_client( + ip_address=src_server["floating_ips"][0]["floating_ip_address"], + use_password=True) + dst_ssh_source = self._get_remote_client( + ip_address=dst_server["floating_ips"][0]["floating_ip_address"], + use_password=True) + # set up iperf server on destination VM + self.set_iperf_server(dst_ssh_source, traffic_type) + # set up iperf client on source VM + dst_fixed_ip = dst_server['floating_ips'][0]['fixed_ip_address'] + traffic_send_rate = send_rate + bandwidth_value = self.set_iperf_client(src_ssh_source, + dst_fixed_ip, traffic_send_rate) + # kill the iperf process on destination VM + self.kill_iperf_process(dst_ssh_source) + return bandwidth_value + + def capture_iperf_traffic_dscp( + self, src_server, dst_server, + send_dscp, interface, traffic_type): + """To send iperf traffic between src server and dst server + capture the dscp value of ip packet received + """ + src_ssh_source = self._get_remote_client( + ip_address=src_server["floating_ips"][0]["floating_ip_address"], + use_password=True) + dst_ssh_source = self._get_remote_client( + ip_address=dst_server["floating_ips"][0]["floating_ip_address"], + use_password=True) + timestamp = time.strftime("%Y-%m-%d_%H:%M:%S") + dscp_filename = 'dscp_' + timestamp + '.pcap' + # To capture packets from interface + cmd = ('echo \"%s\" | sudo -S tcpdump -ni %s' + ' -w %s > /dev/null 2>&1 &' + % (CONF.validation.image_ssh_password, + interface, dscp_filename)) + dst_ssh_source.exec_command(cmd) + # set up iperf server on destination VM + self.set_iperf_server(dst_ssh_source, traffic_type) + # set up iperf client on source VM + dst_fixed_ip = dst_server['floating_ips'][0]['fixed_ip_address'] + self.set_iperf_client(src_ssh_source, + dst_fixed_ip) + # Kill iperf process on destination VM + self.kill_iperf_process(dst_ssh_source) + # kill tcpdump process on destination VM + self.kill_tcpdump_process(src_ssh_source) + # To copy pcap (packet capture) file from destination VM to external VM + cmd = ('sshpass -p \"%s\" scp -o StrictHostKeyChecking=no' + ' %s@%s:/home/%s/%s .' + % (CONF.validation.image_ssh_password, + CONF.validation.image_ssh_user, + dst_server['floating_ips'][0]['floating_ip_address'], + CONF.validation.image_ssh_user, dscp_filename)) + try: + subprocess.check_call(cmd, shell=True, executable='/bin/bash', + stderr=subprocess.STDOUT) + except Exception as e: + message = ('Failed to copy file from VM.' + 'Error: %(error)s' % {'error': e}) + LOG.exception(message) + raise + return dscp_filename diff --git a/vmware_nsx_tempest/services/nsx_client.py b/vmware_nsx_tempest/services/nsx_client.py index 046bbf1..1e60c0e 100644 --- a/vmware_nsx_tempest/services/nsx_client.py +++ b/vmware_nsx_tempest/services/nsx_client.py @@ -44,3 +44,47 @@ class NSXClient(object): if self.backend == "nsxv3": return self.nsx.get_bridge_cluster_info( *args, **kwargs) + + def get_qos_switching_profile(self, policy_name): + """ + Retrieve attributes of a given nsx switching profile + """ + if self.backend == "nsxv3": + qos_policies = self.nsx.get_switching_profiles() + nsx_policy = self.nsx.get_nsx_resource_by_name(qos_policies, + policy_name) + qos_policy = self.nsx.get_switching_profile(nsx_policy['id']) + return qos_policy + else: + #TODO(dkandavarajay) define else for NSXV + pass + + def get_qos_bandwidth_rule(self, nsx_policy_id): + """ + Retrieve attributes of a given nsx qos bandwidth-rule + """ + if self.backend == "nsxv3": + sw_profiles = self.nsx.get_switching_profile(nsx_policy_id) + shaper_cfg = sw_profiles['shaper_configuration'] + for cfg in shaper_cfg: + if cfg['resource_type'] == 'IngressRateShaper': + avg_bw = cfg['average_bandwidth_mbps'] + peak_bw = cfg['peak_bandwidth_mbps'] + max_burst = cfg['burst_size_bytes'] + return avg_bw, peak_bw, max_burst + else: + #TODO(dkandavarajay) define else for NSXV + pass + + def get_qos_dscp_rule(self, nsx_policy_id): + """ + Retrieve attributes of a given nsx qos bandwidth-rule + """ + if self.backend == "nsxv3": + sw_profiles = self.nsx.get_switching_profile(nsx_policy_id) + shaper_cfg = sw_profiles['dscp'] + return shaper_cfg['priority'] + else: + #TODO(dkandavarajay) define else for NSXV + pass + return None diff --git a/vmware_nsx_tempest/services/openstack_network_clients.py b/vmware_nsx_tempest/services/openstack_network_clients.py index 8f74949..ce491c1 100644 --- a/vmware_nsx_tempest/services/openstack_network_clients.py +++ b/vmware_nsx_tempest/services/openstack_network_clients.py @@ -184,3 +184,117 @@ class FwaasV2Client(base.BaseNetworkClient): def delete_firewall_v2_group(self, group_id): uri = self.resource_group_object_path % group_id return self.delete_resource(uri) + + +class QosBWLimitClient(base.BaseNetworkClient): + """ + Request resources via API for QosBandwidthLimitClient + Qos bandwidth-limit create request + Qos bandwidth-limit update request + Qos bandwidth-limit show request + Qos bandwidth-limit delete request + Qos bandwidth-limit list all request + """ + resource = 'bandwidth_limit_rule' + resource_plural = 'bandwidth_limit_rules' + path = 'qos/policies' + resource_base_path = '/%s/%%s/bandwidth_limit_rules' % path + resource_object_path = '/%s/%%s/bandwidth_limit_rules/%%s' % path + + def create_bandwidth_limit_rule(self, policy_id, **kwargs): + uri = self.resource_base_path % policy_id + post_data = {self.resource: kwargs} + return self.create_resource(uri, post_data) + + def update_bandwidth_limit_rule(self, rule_id, policy_id, **kwargs): + uri = self.resource_object_path % (policy_id, rule_id) + post_data = {self.resource: kwargs} + return self.update_resource(uri, post_data) + + def show_bandwidth_limit_rule(self, rule_id, policy_id, **fields): + uri = self.resource_object_path % (policy_id, rule_id) + return self.show_resource(uri, **fields) + + def delete_bandwidth_limit_rule(self, rule_id, policy_id): + uri = self.resource_object_path % (policy_id, rule_id) + return self.delete_resource(uri) + + def list_bandwidth_limit_rules(self, policy_id, **filters): + uri = self.resource_base_path % policy_id + return self.list_resources(uri, **filters) + + +class QosDscpClient(base.BaseNetworkClient): + """ + Request resources via API for QosBandwidthLimitClient + Qos dscp-marking create request + Qos dscp-marking update request + Qos dscp-marking show request + Qos dscp-marking delete request + Qos dscp-marking list all request + """ + resource = 'dscp_marking_rule' + resource_plural = 'dscp_marking_rules' + path = 'qos/policies' + resource_base_path = '/%s/%%s/dscp_marking_rules' % path + resource_object_path = '/%s/%%s/dscp_marking_rules/%%s' % path + + def create_dscp_marking_rule(self, policy_id, **kwargs): + uri = self.resource_base_path % policy_id + post_data = {self.resource: kwargs} + return self.create_resource(uri, post_data) + + def update_dscp_marking_rule(self, rule_id, policy_id, **kwargs): + uri = self.resource_object_path % (policy_id, rule_id) + post_data = {self.resource: kwargs} + return self.update_resource(uri, post_data) + + def show_dscp_marking_rule(self, rule_id, policy_id, **fields): + uri = self.resource_object_path % (policy_id, rule_id) + return self.show_resource(uri, **fields) + + def delete_dscp_marking_rule(self, rule_id, policy_id): + uri = self.resource_object_path % (policy_id, rule_id) + return self.delete_resource(uri) + + def list_dscp_marking_rules(self, policy_id, **filters): + uri = self.resource_base_path % policy_id + return self.list_resources(uri, **filters) + + +class QosPoliciesClient(base.BaseNetworkClient): + """ + Request resources via API for QosPolicyClient + Qos policy create request + Qos policy update request + Qos policy show request + Qos policy delete request + Qos policy list all request + """ + resource = 'policy' + resource_plural = 'policies' + path = 'qos/policies' + resource_base_path = '/%s' % path + resource_object_path = '/%s/%%s' % path + + def create_policy(self, **kwargs): + uri = self.resource_base_path + post_data = {self.resource: kwargs} + return self.create_resource(uri, post_data) + + def update_policy(self, policy_id, **kwargs): + uri = self.resource_object_path % policy_id + post_data = {self.resource: kwargs} + return self.update_resource(uri, post_data) + + def show_policy(self, policy_id, **fields): + uri = self.resource_object_path % policy_id + return self.show_resource(uri, **fields) + + def delete_policy(self, policy_id): + uri = self.resource_object_path % policy_id + return self.delete_resource(uri) + + def list_policies(self, **filters): + uri = self.resource_base_path + return self.list_resources(uri, **filters) diff --git a/vmware_nsx_tempest/tests/scenario/test_qos.py b/vmware_nsx_tempest/tests/scenario/test_qos.py new file mode 100644 index 0000000..2fae1b2 --- /dev/null +++ b/vmware_nsx_tempest/tests/scenario/test_qos.py @@ -0,0 +1,494 @@ +# Copyright 2017 VMware Inc +# All Rights Reserved. +# +# 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_log import log as logging + +from subprocess import PIPE +from subprocess import Popen +from subprocess import STDOUT + +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest import test + +from vmware_nsx_tempest.lib import feature_manager +from vmware_nsx_tempest.services import nsx_client + +import time + +CONF = config.CONF +CONF.validation.auth_method = 'None' + +LOG = logging.getLogger(__name__) + +DSCP_MARK = 12 +DSCP_MARK_UPDATED = 16 +BW_VALUE_KBPS = 1024 +BW_VALUE_MBPS = 1 +UPDATED_BW_VALUE_KBPS = 2048 +UPDATED_BW_VALUE_MBPS = 2 +MAX_BURST_KBPS = 1024000 +MAX_BURST_MBPS = 1 + + +class TestQosOps(feature_manager.FeatureManager): + + @classmethod + def skip_checks(cls): + super(TestQosOps, cls).skip_checks() + if not (CONF.network.project_networks_reachable or + CONF.network.public_network_id): + msg = ('Either project_networks_reachable must be "true", or ' + 'public_network_id must be defined.') + raise cls.skipException(msg) + if not CONF.network.public_network_cidr: + msg = "public_network_cidr must be defined in network section." + raise cls.skipException(msg) + if not test.is_extension_enabled('qos', 'network'): + msg = "q-qos extension not enabled." + raise cls.skipException(msg) + + @classmethod + def setup_credentials(cls): + cls.set_network_resources() + cls.admin_mgr = cls.get_client_manager('admin') + super(TestQosOps, cls).setup_credentials() + + @classmethod + def setup_clients(cls): + """ + Create various client connections. Such as NSX. + """ + super(TestQosOps, cls).setup_clients() + cls.nsx_client = nsx_client.NSXClient( + CONF.network.backend, + CONF.nsxv3.nsx_manager, + CONF.nsxv3.nsx_user, + CONF.nsxv3.nsx_password) + + def define_security_groups(self): + self.qos_sg = self.create_topology_empty_security_group( + namestart="qos_sg_") + # Common rules to allow the following traffic + # 1. Egress ICMP IPv4 any any + # 2. Egress ICMP IPv6 any any + # 3. Ingress ICMP IPv4 from public network + # 4. Ingress TCP 22 (SSH) from public network + common_ruleset = [dict(direction='egress', protocol='icmp'), + dict(direction='egress', protocol='icmp', + ethertype='IPv6'), + dict(direction='egress', protocol='tcp', + port_range_min=22, port_range_max=22), + dict(direction='egress', protocol='udp'), + dict(direction='ingress', protocol='tcp', + port_range_min=22, port_range_max=22), + dict(direction='ingress', protocol='udp'), + dict(direction='ingress', protocol='icmp')] + for rule in common_ruleset: + self.add_security_group_rule(self.qos_sg, rule) + + def check_show_policy(self, policy_id, rule_type=None, + rule_bw=None, rule_dscp=None): + retrieved_policy = self.show_qos_policy(policy_id) + policy_rules = retrieved_policy['rules'] + if rule_type == 'bw': + self.assertEqual(1, len(policy_rules)) + self.assertEqual(rule_bw['id'], policy_rules[0]['id']) + self.assertEqual(feature_manager.RULE_TYPE_BANDWIDTH_LIMIT, + policy_rules[0]['type']) + elif rule_type == 'dscp': + self.assertEqual(1, len(policy_rules)) + self.assertEqual(rule_dscp['id'], policy_rules[0]['id']) + self.assertEqual(feature_manager.RULE_TYPE_DSCP_MARK, + policy_rules[0]['type']) + elif rule_type == 'bw+dscp': + self.assertEqual(2, len(policy_rules)) + self.assertEqual(rule_bw['id'], policy_rules[0]['id']) + self.assertEqual(rule_dscp['id'], policy_rules[1]['id']) + self.assertEqual(feature_manager.RULE_TYPE_BANDWIDTH_LIMIT, + policy_rules[0]['type']) + self.assertEqual(feature_manager.RULE_TYPE_DSCP_MARK, + policy_rules[1]['type']) + + def deploy_qos_ops_topology(self): + router_qos = self.create_topology_router("router_qos") + # Qos network + network_qos = self.create_topology_network("network_qos") + self.create_topology_subnet("subnet_qos", network_qos, + router_id=router_qos["id"]) + return network_qos + + def create_qos_bw_setup(self, bw_value_kbps, burst_kbps=0): + name = data_utils.rand_name('test-qos-policy-') + policy = self.create_qos_policy(name, + description='bandwidth_rule', + shared=False) + rule = self.create_bandwidth_limit_rule( + policy_id=policy['id'], max_kbps=bw_value_kbps, + max_burst_kbps=burst_kbps) + # Test 'show rule' + retrieved_rule = self.show_bandwidth_limit_rule( + rule['id'], policy['id']) + self.assertEqual(rule['id'], retrieved_rule['id']) + self.assertEqual(bw_value_kbps, retrieved_rule['max_kbps']) + self.assertEqual(burst_kbps, retrieved_rule['max_burst_kbps']) + + network = self.deploy_qos_ops_topology() + # Test 'list rules' + rules = self.list_bandwidth_limit_rules(policy['id']) + rules_ids = [r['id'] for r in rules] + self.assertIn(rule['id'], rules_ids) + + # Test 'show policy' + self.check_show_policy(policy_id=policy['id'], rule_type='bw', + rule_bw=rule) + + #Verify backend + nsx_policy = self.nsx_client.get_qos_switching_profile(policy['name']) + #verify bandwidth-limit rule at the backend + avg_bw, peak_bw, max_burst = self.nsx_client.get_qos_bandwidth_rule( + nsx_policy['id']) + #check the values at the backend + msg = 'Backend bw-limit rule values are incorrect' + self.assertEqual(avg_bw, BW_VALUE_MBPS, msg) + self.assertEqual(peak_bw, BW_VALUE_MBPS * 2, msg) + self.assertEqual(max_burst, 0, msg) + return dict(network_qos=network, + policy_id=policy['id']) + + def create_qos_dscp_setup(self, dscp_mark): + name = data_utils.rand_name('test-qos-policy-') + policy = self.create_qos_policy(name, + description='dscp_rule', + shared=False) + # add dscp rule + rule = self.create_dscp_marking_rule( + policy_id=policy['id'], dscp_mark=dscp_mark) + + # Test 'show rule' + retrieved_rule = self.show_dscp_marking_rule( + rule['id'], policy['id']) + self.assertEqual(rule['id'], retrieved_rule['id']) + self.assertEqual(DSCP_MARK, retrieved_rule['dscp_mark']) + + network = self.deploy_qos_ops_topology() + # Test 'list rules' + rules = self.list_dscp_marking_rules(policy['id']) + rules_ids = [r['id'] for r in rules] + self.assertIn(rule['id'], rules_ids) + + # Test 'show policy' + self.check_show_policy(policy_id=policy['id'], + rule_type='dscp', rule_dscp=rule) + #Verify backend + nsx_policy = self.nsx_client.get_qos_switching_profile(policy['name']) + #verify dscp rule at the backend + dscp_value = self.nsx_client.get_qos_dscp_rule( + nsx_policy['id']) + #check the values at the backend + msg = 'Backend DSCP value is incorrect' + self.assertEqual(dscp_value, DSCP_MARK, msg) + return dict(network_qos=network, + policy_id=policy['id']) + + def create_qos_bw_dscp_setup(self, bw_value_kbps, dscp_mark, burst_kbps=0): + name = data_utils.rand_name('test-qos-policy-') + policy = self.create_qos_policy(name, + description='bw_dscp_rule', + shared=False) + # add bw rule + self.create_bandwidth_limit_rule( + policy_id=policy['id'], max_kbps=bw_value_kbps, + max_burst_kbps=burst_kbps) + # add dscp rule + self.create_dscp_marking_rule( + policy_id=policy['id'], dscp_mark=dscp_mark) + network = self.deploy_qos_ops_topology() + #Verify backend + nsx_policy = self.nsx_client.get_qos_switching_profile(policy['name']) + #verify bandwidth-limit rule at the backend + avg_bw, peak_bw, max_burst = self.nsx_client.get_qos_bandwidth_rule( + nsx_policy['id']) + #check the values at the backend + msg = 'Backend bw-limit rule values are incorrect' + self.assertEqual(avg_bw, BW_VALUE_MBPS, msg) + self.assertEqual(peak_bw, BW_VALUE_MBPS * 2, msg) + self.assertEqual(max_burst, 0, msg) + #verify dscp rule at the backend + dscp_value = self.nsx_client.get_qos_dscp_rule( + nsx_policy['id']) + #check the values at the backend + msg = 'Backend DSCP value is incorrect' + self.assertEqual(dscp_value, DSCP_MARK, msg) + return dict(network_qos=network, + policy_id=policy['id']) + + def create_vms(self, network): + #Obtain image id of debian_vmdk used for qos testing + image_id = self.get_glance_image_id('debian') + qos_src_vm = self.create_topology_instance( + "qos_src_vm", [network], + security_groups=[{'name': self.qos_sg['name']}], + create_floating_ip=True, image_id=image_id) + qos_dst_vm = self.create_topology_instance( + "qos_dst_vm", [network], + security_groups=[{'name': self.qos_sg['name']}], + create_floating_ip=True, image_id=image_id) + return (qos_src_vm, qos_dst_vm) + + def check_internal_connectivity(self, network): + src_server_floatingip = self.topology_servers["qos_src_vm"][ + "floating_ip"] + src_server = self.topology_servers["qos_src_vm"] + self.check_vm_internal_connectivity(network, + src_server_floatingip, src_server) + dst_server_floatingip = self.topology_servers["qos_dst_vm"][ + "floating_ip"] + dst_server = self.topology_servers["qos_dst_vm"] + self.check_vm_internal_connectivity(network, + dst_server_floatingip, dst_server) + + def verify_bandwidth_rule(self, max_mbps, max_burst=0): + """Check if traffic received is greater than configured value + For example if configured value is 5Mbps and sending rate is 6Mbps + Traffic should be capped below 5.5 which includes default burst + """ + send_rate = max_mbps + max_burst + 1 + bw_value = self.use_iperf_send_traffic( + src_server=self.topology_servers["qos_src_vm"], + dst_server=self.topology_servers["qos_dst_vm"], + send_rate=send_rate, traffic_type='udp') + if bw_value == -1: + raise Exception('Incorrect IPERF output received') + elif float(bw_value) - (float(max_mbps) + float(max_burst)) > 0.5: + LOG.info("Traffic received: {bw}".format(bw=bw_value)) + raise Exception('Traffic is not limited by bw-limit rule') + elif((float(max_mbps) + float(max_burst)) - float(bw_value)) > 0.5: + LOG.info("Traffic received: {bw}".format(bw=bw_value)) + raise Exception('Traffic is limited below configured value') + + def verify_dscp_rule(self, dscp_value): + """Check if traffic received is marked with configured dscp value + """ + dscp_filename = self.capture_iperf_traffic_dscp( + src_server=self.topology_servers["qos_src_vm"], + dst_server=self.topology_servers["qos_dst_vm"], + traffic_type='udp', send_dscp='0', interface='eth0') + """Check the entire file to see if any UDP packets are sent without configured + dscp value.Example capture all UDP packets with DSCP value !=12""" + src_vm_ip_dict = self.topology_servers['qos_src_vm']['floating_ips'][0] + filter_string = ('tshark -r %s -Y ' + '\"ip.dsfield.dscp != %s && udp.dstport == 49162 ' + '&& ip.src == %s && ip.dst == %s\"' % + (dscp_filename, str(dscp_value), + src_vm_ip_dict['fixed_ip_address'], + self.topology_servers['qos_dst_vm'])) + p = Popen(filter_string, shell=True, stdout=PIPE, stderr=STDOUT) + stdout, stderr = p.communicate() + LOG.info(stdout) + LOG.info(stderr) + #output should not contain packet trace + if "Source port:" in stdout: + raise Exception('Traffic is not being marked with correct DSCP') + + +class QosBandwidthLimitRuleTest(TestQosOps): + + @decorators.idempotent_id('68fa3170-b61c-4e69-b0b7-6cbe34b57724') + def test_qos_bw_rule_network(self): + """ + Test bandwidth_limit rule by sending traffic between two instances + and verifying if egress traffic is being bandwidth-limited + """ + self.define_security_groups() + qos_bw_dict = self.create_qos_bw_setup(bw_value_kbps=BW_VALUE_KBPS) + self.admin_mgr.networks_client.update_network( + qos_bw_dict['network_qos']['id'], + qos_policy_id=qos_bw_dict['policy_id']) + updated_network = self.admin_mgr.networks_client.show_network( + qos_bw_dict['network_qos']['id']) + qos_network = updated_network.get('network', updated_network) + self.assertEqual( + qos_bw_dict['policy_id'], qos_network['qos_policy_id']) + self.create_vms(qos_bw_dict['network_qos']) + #sleep to ensure VMs have finished complete bootup + time.sleep(120) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS) + + @decorators.idempotent_id('bf687826-ec76-4655-90a0-cc8f5316eaaf') + def test_qos_bw_rule_network_with_burst(self): + """ + Test bandwidth_limit rule by sending traffic between two instances + and verifying if egress traffic is being bandwidth-limited + """ + self.define_security_groups() + qos_bw_dict = self.create_qos_bw_setup(bw_value_kbps=BW_VALUE_KBPS, + burst_kbps=MAX_BURST_KBPS) + self.admin_mgr.networks_client.update_network( + qos_bw_dict['network_qos']['id'], + qos_policy_id=qos_bw_dict['policy_id']) + updated_network = self.admin_mgr.networks_client.show_network( + qos_bw_dict['network_qos']['id']) + qos_network = updated_network.get('network', updated_network) + self.assertEqual( + qos_bw_dict['policy_id'], qos_network['qos_policy_id']) + self.create_vms(qos_bw_dict['network_qos']) + #sleep to ensure VMs have finished complete bootup + time.sleep(120) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS, + max_burst=MAX_BURST_MBPS) + + @decorators.idempotent_id('531c7476-6cee-4224-9b23-8e67e4c30703') + def test_qos_bw_rule_port(self): + """ + Test bandwidth_limit rule by sending traffic between two instances + and verifying if egress traffic is being bandwidth-limited + """ + self.define_security_groups() + qos_bw_dict = self.create_qos_bw_setup(bw_value_kbps=BW_VALUE_KBPS) + qos_src_vm, qos_dst_vm = self.create_vms(qos_bw_dict['network_qos']) + self.os_admin.ports_client.update_port( + qos_src_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dict['policy_id']) + self.os_admin.ports_client.update_port( + qos_dst_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dict['policy_id']) + #sleep to ensure VMs have finished complete bootup + time.sleep(120) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS) + + @decorators.idempotent_id('ae40717f-6a08-4e9c-86d0-87e45375c844') + def test_qos_bw_rule_port_with_burst(self): + """ + Test bandwidth_limit rule by sending traffic between two instances + and verifying if egress traffic is being bandwidth-limited + """ + self.define_security_groups() + qos_bw_dict = self.create_qos_bw_setup(bw_value_kbps=BW_VALUE_KBPS, + burst_kbps=MAX_BURST_KBPS) + qos_src_vm, qos_dst_vm = self.create_vms(qos_bw_dict['network_qos']) + self.os_admin.ports_client.update_port( + qos_src_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dict['policy_id']) + self.os_admin.ports_client.update_port( + qos_dst_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dict['policy_id']) + #sleep to ensure VMs have finished complete bootup + time.sleep(120) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS, + max_burst=MAX_BURST_MBPS) + + +class QosDSCPRuleTest(TestQosOps): + + @decorators.idempotent_id('40995e11-9231-406e-b3d7-b36dd362a94b') + def test_qos_dscp_mark_network(self): + """ + Test qos dscp rule by sending traffic between two instance + and verifying if egress traffic is marked with dscp value + """ + self.define_security_groups() + qos_dscp_dict = self.create_qos_dscp_setup(dscp_mark=DSCP_MARK) + self.admin_mgr.networks_client.update_network( + qos_dscp_dict['network_qos']['id'], + qos_policy_id=qos_dscp_dict['policy_id']) + updated_network = self.admin_mgr.networks_client.show_network( + qos_dscp_dict['network_qos']['id']) + qos_network = updated_network.get('network', updated_network) + self.assertEqual( + qos_dscp_dict['policy_id'], qos_network['qos_policy_id']) + self.create_vms(qos_dscp_dict['network_qos']) + #sleep to ensure VMs have finished complete bootup + time.sleep(240) + #check dscp rule + self.verify_dscp_rule(dscp_value=DSCP_MARK) + + @decorators.idempotent_id('4c5dc539-2878-4235-8880-35927b7a0c33') + def test_qos_dscp_mark_port(self): + """ + Test qos dscp rule by sending traffic between two instance + and verifying if egress traffic is marked with dscp value + """ + self.define_security_groups() + qos_dscp_dict = self.create_qos_dscp_setup(dscp_mark=DSCP_MARK) + qos_src_vm, qos_dst_vm = self.create_vms(qos_dscp_dict['network_qos']) + self.os_admin.ports_client.update_port( + qos_src_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_dscp_dict['policy_id']) + self.os_admin.ports_client.update_port( + qos_dst_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_dscp_dict['policy_id']) + #sleep to ensure VMs have finished complete bootup + time.sleep(240) + #check dscp rule + self.verify_dscp_rule(dscp_value=DSCP_MARK) + + +class QosPolicyRuleTest(TestQosOps): + + @decorators.idempotent_id('3566016a-31cc-4905-b217-98844caad4a9') + def test_qos_bw_dscp_rule_network(self): + """ + Test qos and bw dscp rule by sending traffic between two instance + and verifying if traffic is rate-limited and marked with dscp value + """ + self.define_security_groups() + qos_bw_dscp_dict = self.create_qos_bw_dscp_setup( + bw_value_kbps=BW_VALUE_KBPS, + dscp_mark=DSCP_MARK) + self.admin_mgr.networks_client.update_network( + qos_bw_dscp_dict['network_qos']['id'], + qos_policy_id=qos_bw_dscp_dict['policy_id']) + updated_network = self.admin_mgr.networks_client.show_network( + qos_bw_dscp_dict['network_qos']['id']) + qos_network = updated_network.get('network', updated_network) + self.assertEqual( + qos_bw_dscp_dict['policy_id'], qos_network['qos_policy_id']) + self.create_vms(qos_bw_dscp_dict['network_qos']) + #sleep to ensure VMs have finished complete bootup + time.sleep(240) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS) + #check dscp rule + self.verify_dscp_rule(dscp_value=DSCP_MARK) + + @decorators.idempotent_id('c545c322-b37e-45e2-af22-b160a5320594') + def test_qos_bw_dscp_rule_port(self): + """ + Test qos and bw dscp rule by sending traffic between two instance + and verifying if traffic is rate-limited and marked with dscp value + """ + self.define_security_groups() + qos_bw_dscp_dict = self.create_qos_bw_dscp_setup( + bw_value_kbps=BW_VALUE_KBPS, + dscp_mark=DSCP_MARK) + qos_src_vm, qos_dst_vm = self.create_vms( + qos_bw_dscp_dict['network_qos']) + self.os_admin.ports_client.update_port( + qos_src_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dscp_dict['policy_id']) + self.os_admin.ports_client.update_port( + qos_dst_vm['floating_ips'][0]['port_id'], + qos_policy_id=qos_bw_dscp_dict['policy_id']) + #sleep to ensure VMs have finished complete bootup + time.sleep(240) + #check bandwidth rule + self.verify_bandwidth_rule(max_mbps=BW_VALUE_MBPS) + #check dscp rule + self.verify_dscp_rule(dscp_value=DSCP_MARK)