From 575a8c98517c587dfdccfad93a6ea119ad689629 Mon Sep 17 00:00:00 2001 From: Manuel Buil Date: Thu, 21 Dec 2017 17:26:05 +0100 Subject: Test the deletion of chain This is the flow of the test: 1 - Create a vnffg in tacker (i.e. chain + classifier) 2 - Remove the vnffg 3 - Check that the RSPs are removed from operation DB in ODL and the classifier rules are not present in the flows 4 - Create a new vnffg 5 - Test the new chain: 5.1 - Check HTTP traffic works 5.2 - Block HTTP traffic in VNF and check that it does not work anymore Change-Id: Ia077e58c5cbce4258a2f8a36f6b961eb923ff9c5 Signed-off-by: Manuel Buil --- sfc/lib/odl_utils.py | 85 ++++++- sfc/tests/functest/config.yaml | 13 + sfc/tests/functest/sfc_chain_deletion.py | 264 +++++++++++++++++++++ .../vnffgd-templates/test-deletion-vnffgd.yaml | 38 +++ .../test-deletion-vnffgd.yaml-queens | 38 +++ 5 files changed, 430 insertions(+), 8 deletions(-) create mode 100644 sfc/tests/functest/sfc_chain_deletion.py create mode 100644 sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml create mode 100644 sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml-queens diff --git a/sfc/lib/odl_utils.py b/sfc/lib/odl_utils.py index 90b6b54f..c8b0ebdd 100644 --- a/sfc/lib/odl_utils.py +++ b/sfc/lib/odl_utils.py @@ -108,14 +108,7 @@ def wait_for_classification_rules(ovs_logger, compute_nodes, odl_ip, odl_port, We know by experience that this process might take a while ''' try: - for compute_node in compute_nodes: - if compute_node.name in compute_client_name: - compute = compute_node - try: - compute - except NameError: - logger.debug("No compute where the client is was found") - raise Exception("No compute where the client is was found") + compute = find_compute(compute_client_name, compute_nodes) # Find the configured rsps in ODL. Its format is nsp_destPort promised_rsps = [] @@ -280,3 +273,79 @@ def delete_acl(clf_name, odl_ip, odl_port): odl_port, 'ietf-access-control-list:ipv4-acl', clf_name) + + +def find_compute(compute_client_name, compute_nodes): + for compute_node in compute_nodes: + if compute_node.name in compute_client_name: + compute = compute_node + try: + compute + except NameError: + logger.debug("No compute, where the client is, was found") + raise Exception("No compute, where the client is, was found") + + return compute + + +def check_vnffg_deletion(odl_ip, odl_port, ovs_logger, compute_client_name, + compute_nodes, retries=20): + ''' + First, RSPs are checked in the oprational datastore of ODL. Nothing should + should exist. As it might take a while for ODL to remove that, some + retries are needed. + + Secondly, we check that the classification rules are removed too + ''' + + retries_counter = retries + + # Check RSPs + while retries_counter > 0: + if (get_active_rsps(odl_ip, odl_port)): + retries_counter -= 1 + time.sleep(3) + else: + break + + if not retries_counter: + logger.debug("RSPs are still active in the MD-SAL") + return False + + # Get the compute where the client is running + try: + compute = find_compute(compute_client_name, compute_nodes) + except Exception as e: + logger.debug("There was an error getting the compute: e" % e) + + retries_counter = retries + + # Check classification flows + while retries_counter > 0: + if (actual_rsps_in_compute(ovs_logger, compute.ssh_client)): + retries_counter -= 1 + time.sleep(3) + else: + break + + if not retries_counter: + logger.debug("Classification flows still in the compute") + return False + + return True + +def create_chain(tacker_client, default_param_file, neutron_port, + COMMON_CONFIG, TESTCASE_CONFIG): + + tosca_file = os.path.join(COMMON_CONFIG.sfc_test_dir, + COMMON_CONFIG.vnffgd_dir, + TESTCASE_CONFIG.test_vnffgd_red) + + os_sfc_utils.create_vnffgd(tacker_client, + tosca_file=tosca_file, + vnffgd_name='red') + + os_sfc_utils.create_vnffg_with_param_file(tacker_client, 'red', + 'red_http', + default_param_file, + neutron_port.id) diff --git a/sfc/tests/functest/config.yaml b/sfc/tests/functest/config.yaml index 31e0a2ae..5bbba65f 100644 --- a/sfc/tests/functest/config.yaml +++ b/sfc/tests/functest/config.yaml @@ -66,3 +66,16 @@ testcases: test_vnfd: "test-symmetric-vnfd.yaml" allowed_source_port: 22222 blocked_source_port: 33333 + + sfc_chain_deletion: + enabled: false + order: 3 + description: "Verify if chains work correctly after deleting one" + net_name: example-net + subnet_name: example-subnet + router_name: example-router + subnet_cidr: "11.0.0.0/24" + secgroup_name: "example-sg" + secgroup_descr: "Example Security group" + test_vnfd_red: "test-one-chain-vnfd1.yaml" + test_vnffgd_red: "test-deletion-vnffgd.yaml" diff --git a/sfc/tests/functest/sfc_chain_deletion.py b/sfc/tests/functest/sfc_chain_deletion.py new file mode 100644 index 00000000..22d634e0 --- /dev/null +++ b/sfc/tests/functest/sfc_chain_deletion.py @@ -0,0 +1,264 @@ +#!/bin/python +# +# Copyright (c) 2015 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +import os +import sys +import threading +import logging + +import sfc.lib.openstack_utils as os_sfc_utils +import sfc.lib.odl_utils as odl_utils +import opnfv.utils.ovs_logger as ovs_log + +import sfc.lib.config as sfc_config +import sfc.lib.test_utils as test_utils +from sfc.lib.results import Results +from opnfv.deployment.factory import Factory as DeploymentFactory +import sfc.lib.topology_shuffler as topo_shuffler + + +logger = logging.getLogger(__name__) + +CLIENT = "client" +SERVER = "server" +COMMON_CONFIG = sfc_config.CommonConfig() +TESTCASE_CONFIG = sfc_config.TestcaseConfig('sfc_chain_deletion') + + +def main(): + deploymentHandler = DeploymentFactory.get_handler( + COMMON_CONFIG.installer_type, + COMMON_CONFIG.installer_ip, + COMMON_CONFIG.installer_user, + COMMON_CONFIG.installer_password, + COMMON_CONFIG.installer_key_file) + + installer_type = os.environ.get("INSTALLER_TYPE") + + supported_installers = ['fuel', 'apex', 'osa'] + + if installer_type not in supported_installers: + logger.error( + '\033[91mYour installer is not supported yet\033[0m') + sys.exit(1) + + installer_ip = os.environ.get("INSTALLER_IP") + if not installer_ip: + logger.error( + '\033[91minstaller ip is not set\033[0m') + logger.error( + '\033[91mexport INSTALLER_IP=\033[0m') + sys.exit(1) + + cluster = COMMON_CONFIG.installer_cluster + openstack_nodes = (deploymentHandler.get_nodes({'cluster': cluster}) + if cluster is not None + else deploymentHandler.get_nodes()) + + controller_nodes = [node for node in openstack_nodes + if node.is_controller()] + compute_nodes = [node for node in openstack_nodes + if node.is_compute()] + + odl_ip, odl_port = odl_utils.get_odl_ip_port(openstack_nodes) + + for compute in compute_nodes: + logger.info("This is a compute: %s" % compute.ip) + + results = Results(COMMON_CONFIG.line_length) + results.add_to_summary(0, "=") + results.add_to_summary(2, "STATUS", "SUBTEST") + results.add_to_summary(0, "=") + + openstack_sfc = os_sfc_utils.OpenStackSFC() + + custom_flv = openstack_sfc.create_flavor( + COMMON_CONFIG.flavor, + COMMON_CONFIG.ram_size_in_mb, + COMMON_CONFIG.disk_size_in_gb, + COMMON_CONFIG.vcpu_count) + if not custom_flv: + logger.error("Failed to create custom flavor") + sys.exit(1) + + tacker_client = os_sfc_utils.get_tacker_client() + + controller_clients = test_utils.get_ssh_clients(controller_nodes) + compute_clients = test_utils.get_ssh_clients(compute_nodes) + + ovs_logger = ovs_log.OVSLogger( + os.path.join(COMMON_CONFIG.sfc_test_dir, 'ovs-logs'), + COMMON_CONFIG.functest_results_dir) + + image_creator = openstack_sfc.register_glance_image( + COMMON_CONFIG.image_name, + COMMON_CONFIG.image_url, + COMMON_CONFIG.image_format, + 'public') + + network, router = openstack_sfc.create_network_infrastructure( + TESTCASE_CONFIG.net_name, + TESTCASE_CONFIG.subnet_name, + TESTCASE_CONFIG.subnet_cidr, + TESTCASE_CONFIG.router_name) + + sg = openstack_sfc.create_security_group(TESTCASE_CONFIG.secgroup_name) + + vnf_names = ['testVNF1', 'testVNF2'] + + topo_seed = topo_shuffler.get_seed() # change to None for nova av zone + testTopology = topo_shuffler.topology(vnf_names, openstack_sfc, + seed=topo_seed) + + logger.info('This test is run with the topology {0}' + .format(testTopology['id'])) + logger.info('Topology description: {0}' + .format(testTopology['description'])) + + client_instance, client_creator = openstack_sfc.create_instance( + CLIENT, COMMON_CONFIG.flavor, image_creator, network, sg, + av_zone=testTopology['client']) + + server_instance, server_creator = openstack_sfc.create_instance( + SERVER, COMMON_CONFIG.flavor, image_creator, network, sg, + av_zone=testTopology['server']) + + server_ip = server_instance.ports[0].ips[0]['ip_address'] + + os_sfc_utils.register_vim(tacker_client, vim_file=COMMON_CONFIG.vim_file) + + tosca_red = os.path.join(COMMON_CONFIG.sfc_test_dir, + COMMON_CONFIG.vnfd_dir, + TESTCASE_CONFIG.test_vnfd_red) + os_sfc_utils.create_vnfd(tacker_client, + tosca_file=tosca_red, + vnfd_name='test-vnfd1') + + default_param_file = os.path.join( + COMMON_CONFIG.sfc_test_dir, + COMMON_CONFIG.vnfd_dir, + COMMON_CONFIG.vnfd_default_params_file) + + os_sfc_utils.create_vnf_in_av_zone( + tacker_client, vnf_names[0], 'test-vnfd1', 'test-vim', + default_param_file, testTopology[vnf_names[0]]) + + vnf1_id = os_sfc_utils.wait_for_vnf(tacker_client, vnf_name=vnf_names[0]) + if vnf1_id is None: + logger.error('ERROR while booting vnfs') + sys.exit(1) + + neutron_port = openstack_sfc.get_client_port_id(client_instance) + odl_utils.create_chain(tacker_client, default_param_file, neutron_port, + COMMON_CONFIG, TESTCASE_CONFIG) + + # Start measuring the time it takes to implement the classification rules + t1 = threading.Thread(target=odl_utils.wait_for_classification_rules, + args=(ovs_logger, compute_nodes, odl_ip, + odl_port, openstack_sfc.get_compute_client(),)) + + try: + t1.start() + except Exception as e: + logger.error("Unable to start the thread that counts time %s" % e) + + logger.info("Assigning floating IPs to instances") + client_floating_ip = openstack_sfc.assign_floating_ip(router, + client_instance, + client_creator) + server_floating_ip = openstack_sfc.assign_floating_ip(router, + server_instance, + server_creator) + fips_sfs = openstack_sfc.assign_floating_ip_vnfs(router) + sf1_floating_ip = fips_sfs[0] + + fips = [client_floating_ip, server_floating_ip, sf1_floating_ip] + + for ip in fips: + logger.info("Checking connectivity towards floating IP [%s]" % ip) + if not test_utils.ping(ip, retries=50, retry_timeout=3): + logger.error("Cannot ping floating IP [%s]" % ip) + os_sfc_utils.get_tacker_items() + odl_utils.get_odl_items(odl_ip, odl_port) + sys.exit(1) + logger.info("Successful ping to floating IP [%s]" % ip) + + if not test_utils.check_ssh([sf1_floating_ip]): + logger.error("Cannot establish SSH connection to the SFs") + sys.exit(1) + + logger.info("Starting HTTP server on %s" % server_floating_ip) + if not test_utils.start_http_server(server_floating_ip): + logger.error('\033[91mFailed to start HTTP server on %s\033[0m' + % server_floating_ip) + sys.exit(1) + + logger.info("Wait for ODL to update the classification rules in OVS") + t1.join() + + os_sfc_utils.delete_vnffg(tacker_client, vnffg_name='red_http') + + os_sfc_utils.delete_vnffgd(tacker_client, vnffgd_name='red') + + if not odl_utils.check_vnffg_deletion(odl_ip, odl_port, ovs_logger, + openstack_sfc.get_compute_client(), + compute_nodes): + logger.debug("The chains were not correctly removed") + raise Exception("Chains not correctly removed, test failed") + + odl_utils.create_chain(tacker_client, default_param_file, neutron_port, + COMMON_CONFIG, TESTCASE_CONFIG) + + # Start measuring the time it takes to implement the classification rules + t2 = threading.Thread(target=odl_utils.wait_for_classification_rules, + args=(ovs_logger, compute_nodes, odl_ip, + odl_port, openstack_sfc.get_compute_client(),)) + try: + t2.start() + except Exception as e: + logger.error("Unable to start the thread that counts time %s" % e) + + logger.info("Starting SSH firewall on %s" % sf1_floating_ip) + test_utils.start_vxlan_tool(sf1_floating_ip) + + logger.info("Wait for ODL to update the classification rules in OVS") + t2.join() + + logger.info("Test HTTP") + if not test_utils.is_http_blocked(client_floating_ip, server_ip): + results.add_to_summary(2, "PASS", "HTTP works") + else: + error = ('\033[91mTEST 1 [FAILED] ==> HTTP BLOCKED\033[0m') + logger.error(error) + test_utils.capture_ovs_logs( + ovs_logger, controller_clients, compute_clients, error) + results.add_to_summary(2, "FAIL", "HTTP Blocked") + + logger.info("Stopping HTTP firewall on %s" % sf1_floating_ip) + test_utils.stop_vxlan_tool(sf1_floating_ip) + logger.info("Starting HTTP firewall on %s" % sf1_floating_ip) + test_utils.start_vxlan_tool(sf1_floating_ip, block="80") + + logger.info("Test HTTP again") + if test_utils.is_http_blocked(client_floating_ip, server_ip): + results.add_to_summary(2, "PASS", "HTTP Blocked") + else: + error = ('\033[91mTEST 2 [FAILED] ==> HTTP works\033[0m') + logger.error(error) + test_utils.capture_ovs_logs( + ovs_logger, controller_clients, compute_clients, error) + results.add_to_summary(2, "FAIL", "HTTP works") + + return results.compile_summary(), openstack_sfc.creators + + +if __name__ == '__main__': + logging.config.fileConfig(COMMON_CONFIG.functest_logging_api) + main() diff --git a/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml b/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml new file mode 100644 index 00000000..3f10e6b8 --- /dev/null +++ b/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml @@ -0,0 +1,38 @@ +--- +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0 +description: test-case2_HTTP Test + +topology_template: + description: topology-template-test2 + inputs: + net_src_port_id: + type: string + + node_templates: + Forwarding_path1: + type: tosca.nodes.nfv.FP.Tacker + description: creates path + properties: + id: 1 + policy: + type: ACL + criteria: + - network_src_port_id: {get_input: net_src_port_id} + - destination_port_range: 80-80 + - ip_proto: 6 + path: + - forwarder: test-vnfd1 + capability: CP1 + + groups: + VNFFG1: + type: tosca.groups.nfv.VNFFG + description: creates chain + properties: + vendor: tacker + version: 1.0 + number_of_endpoints: 1 + dependent_virtual_link: [VL1] + connection_point: [CP1] + constituent_vnfs: [test-vnfd1] + members: [Forwarding_path1] diff --git a/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml-queens b/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml-queens new file mode 100644 index 00000000..28b78ead --- /dev/null +++ b/sfc/tests/functest/vnffgd-templates/test-deletion-vnffgd.yaml-queens @@ -0,0 +1,38 @@ +--- +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0 +description: test-case2_HTTP Test + +topology_template: + description: topology-template-test2 + inputs: + net_src_port_id: + type: string + + node_templates: + Forwarding_path1: + type: tosca.nodes.nfv.FP.Tacker + description: creates path + properties: + id: 1 + policy: + type: ACL + criteria: + - network_src_port_id: {get_input: net_src_port_id} + destination_port_range: 80-80 + ip_proto: 6 + path: + - forwarder: test-vnfd1 + capability: CP1 + + groups: + VNFFG1: + type: tosca.groups.nfv.VNFFG + description: creates chain + properties: + vendor: tacker + version: 1.0 + number_of_endpoints: 1 + dependent_virtual_link: [VL1] + connection_point: [CP1] + constituent_vnfs: [test-vnfd1] + members: [Forwarding_path1] -- cgit 1.2.3-korg