From bdad6faa1fafe7cd20ea96aa70a52178d62add63 Mon Sep 17 00:00:00 2001 From: Manuel Buil Date: Mon, 27 Aug 2018 18:11:11 +0200 Subject: Decouple tacker from tests Support n-sfc too and abstract a bit the MANO layer so that other MANO components can be inserted into the test Change-Id: I3fb59fbf40b4207bf1721092cd8ff0559e1d9d90 Signed-off-by: Manuel Buil --- requirements.txt | 2 + sfc/lib/cleanup.py | 40 +++- sfc/lib/config.py | 2 + sfc/lib/odl_utils.py | 17 -- sfc/lib/openstack_utils.py | 134 ++++++++++- sfc/tests/functest/config.yaml | 4 + sfc/tests/functest/run_sfc_tests.py | 8 +- sfc/tests/functest/sfc_chain_deletion.py | 11 +- .../sfc_one_chain_two_service_functions.py | 11 +- sfc/tests/functest/sfc_parent_function.py | 236 +++++++++++++----- sfc/tests/functest/sfc_symmetric_chain.py | 61 +---- sfc/tests/functest/sfc_two_chains_SSH_and_HTTP.py | 10 +- sfc/unit_tests/unit/lib/test_cleanup.py | 84 ++++--- sfc/unit_tests/unit/lib/test_odl_utils.py | 40 ---- sfc/unit_tests/unit/lib/test_openstack_utils.py | 264 ++++++++++++++++++++- 15 files changed, 688 insertions(+), 236 deletions(-) diff --git a/requirements.txt b/requirements.txt index 3442097f..36189ea1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,8 @@ xmltodict>=0.10.1 # MIT python-keystoneclient>=3.8.0 # Apache-2.0 python-novaclient>=9.1.0 # Apache-2.0 python-tackerclient>=0.8.0 # Apache-2.0 +python-neutronclient>=6.9.0 +networking-sfc>=7.0.0 PyYAML>=3.12 # MIT opnfv snaps diff --git a/sfc/lib/cleanup.py b/sfc/lib/cleanup.py index e614867d..ee3ad689 100644 --- a/sfc/lib/cleanup.py +++ b/sfc/lib/cleanup.py @@ -75,6 +75,7 @@ def delete_vims(): # Creators is a list full of SNAPs objects def delete_openstack_objects(creators): + logger.info("Deleting the openstack objects...") for creator in reversed(creators): try: creator.clean() @@ -97,26 +98,49 @@ def cleanup_odl(odl_ip, odl_port): delete_odl_ietf_access_lists(odl_ip, odl_port) -def cleanup(creators, odl_ip=None, odl_port=None): +def cleanup_nsfc_objects(): + ''' + cleanup the networking-sfc objects created for the test + ''' + # TODO Add n-sfc to snaps so that it can be removed through + # delete_openstack_objects + openstack_sfc = os_sfc_utils.OpenStackSFC() + openstack_sfc.delete_chain() + openstack_sfc.delete_port_groups() + + +def cleanup_tacker_objects(): + ''' + cleanup the tacker objects created for the test + ''' delete_vnffgs() delete_vnffgds() delete_vnfs() time.sleep(20) delete_vnfds() delete_vims() + + +def cleanup_mano_objects(mano): + ''' + Cleanup the mano objects (chains, classifiers, etc) + ''' + if mano == 'tacker': + cleanup_tacker_objects() + elif mano == 'no-mano': + cleanup_nsfc_objects() + + +def cleanup(creators, mano, odl_ip=None, odl_port=None): + cleanup_mano_objects(mano) delete_openstack_objects(creators) delete_untracked_security_groups() if odl_ip is not None and odl_port is not None: cleanup_odl(odl_ip, odl_port) -def cleanup_from_bash(odl_ip=None, odl_port=None): - delete_vnffgs() - delete_vnffgds() - delete_vnfs() - time.sleep(20) - delete_vnfds() - delete_vims() +def cleanup_from_bash(odl_ip=None, odl_port=None, mano='no-mano'): + cleanup_mano_objects(mano=mano) if odl_ip is not None and odl_port is not None: cleanup_odl(odl_ip, odl_port) diff --git a/sfc/lib/config.py b/sfc/lib/config.py index 541f6847..507142c9 100644 --- a/sfc/lib/config.py +++ b/sfc/lib/config.py @@ -88,6 +88,8 @@ class CommonConfig(object): "defaults.image_format", self.config_file) self.image_url = ft_utils.get_parameter_from_yaml( "defaults.image_url", self.config_file) + self.mano_component = ft_utils.get_parameter_from_yaml( + "defaults.mano_component", self.config_file) try: self.vnf_image_name = ft_utils.get_parameter_from_yaml( "defaults.vnf_image_name", self.config_file) diff --git a/sfc/lib/odl_utils.py b/sfc/lib/odl_utils.py index ebc6a873..85a8f86b 100644 --- a/sfc/lib/odl_utils.py +++ b/sfc/lib/odl_utils.py @@ -428,20 +428,3 @@ def check_vnffg_deletion(odl_ip, odl_port, ovs_logger, neutron_ports, 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/lib/openstack_utils.py b/sfc/lib/openstack_utils.py index b8deb144..06d19eaa 100644 --- a/sfc/lib/openstack_utils.py +++ b/sfc/lib/openstack_utils.py @@ -134,15 +134,17 @@ class OpenStackSFC: return sec_group def create_instance(self, vm_name, flavor_name, image_creator, network, - secgrp, av_zone): + secgrp, av_zone, ports): logger.info("Creating the instance {}...".format(vm_name)) - port_settings = PortConfig(name=vm_name + '-port', - network_name=network.name) + port_settings = [] + for port in ports: + port_settings.append(PortConfig(name=port, + network_name=network.name)) instance_settings = VmInstanceConfig( name=vm_name, flavor=flavor_name, security_group_names=str(secgrp.name), - port_settings=[port_settings], + port_settings=port_settings, availability_zone=av_zone) instance_creator = cr_inst.OpenStackVmInstance( @@ -249,11 +251,12 @@ class OpenStackSFC: return fips - def get_client_port(self, vm, vm_creator): + def get_instance_port(self, vm, vm_creator, port_name=None): ''' Get the neutron port id of the client ''' - port_name = vm.name + "-port" + if not port_name: + port_name = vm.name + "-port" port = vm_creator.get_port_by_name(port_name) if port is not None: return port @@ -273,6 +276,125 @@ class OpenStackSFC: for sg in sec_groups: neutron_utils.delete_security_group(self.neutron, sg) + def wait_for_vnf(self, vnf_creator): + ''' + Waits for VNF to become active + ''' + return vnf_creator.vm_active(block=True, poll_interval=5) + + def create_port_groups(self, vnf_ports, vm_instance): + ''' + Creates a networking-sfc port pair and group + ''' + logger.info("Creating the port pairs...") + port_pair = dict() + port_pair['name'] = vm_instance.name + '-connection-points' + port_pair['description'] = 'port pair for ' + vm_instance.name + + # In the symmetric testcase ingres != egress (VNF has 2 interfaces) + if len(vnf_ports) == 1: + port_pair['ingress'] = vnf_ports[0].id + port_pair['egress'] = vnf_ports[0].id + elif len(vnf_ports) == 2: + port_pair['ingress'] = vnf_ports[0].id + port_pair['egress'] = vnf_ports[1].id + else: + logger.error("Only SFs with one or two ports are supported") + raise Exception("Failed to create port pairs") + port_pair_info = \ + self.neutron.create_sfc_port_pair({'port_pair': port_pair}) + if not port_pair_info: + logger.warning("Chain creation failed due to port pair " + "creation failed for vnf %(vnf)s", + {'vnf': vm_instance.name}) + return None + + logger.info("Creating the port pair groups...") + port_pair_group = {} + port_pair_group['name'] = vm_instance.name + '-port-pair-group' + port_pair_group['description'] = \ + 'port pair group for ' + vm_instance.name + port_pair_group['port_pairs'] = [] + port_pair_group['port_pairs'].append(port_pair_info['port_pair']['id']) + ppg_config = {'port_pair_group': port_pair_group} + port_pair_group_info = \ + self.neutron.create_sfc_port_pair_group(ppg_config) + if not port_pair_group_info: + logger.warning("Chain creation failed due to port pair group " + "creation failed for vnf " + "%(vnf)", vm_instance.name) + return None + + return port_pair_group_info['port_pair_group']['id'] + + def create_chain(self, port_groups, neutron_port, port, protocol, + vnffg_name, symmetrical, server_port=None, + server_ip=None): + ''' + Create the classifier + ''' + logger.info("Creating the classifier...") + + if symmetrical: + sfc_classifier_params = {'name': vnffg_name + '-classifier', + 'destination_ip_prefix': server_ip, + 'logical_source_port': neutron_port, + 'logical_destination_port': server_port, + 'destination_port_range_min': port, + 'destination_port_range_max': port, + 'protocol': protocol} + else: + sfc_classifier_params = {'name': vnffg_name + '-classifier', + 'logical_source_port': neutron_port, + 'destination_port_range_min': port, + 'destination_port_range_max': port, + 'protocol': protocol} + + fc_config = {'flow_classifier': sfc_classifier_params} + fc_info = \ + self.neutron.create_sfc_flow_classifier(fc_config) + + logger.info("Creating the chain...") + port_chain = {} + port_chain['name'] = vnffg_name + '-port-chain' + port_chain['description'] = 'port-chain for SFC' + port_chain['port_pair_groups'] = port_groups + port_chain['flow_classifiers'] = [] + port_chain['flow_classifiers'].append(fc_info['flow_classifier']['id']) + if symmetrical: + port_chain['chain_parameters'] = {} + port_chain['chain_parameters']['symmetric'] = True + chain_config = {'port_chain': port_chain} + return self.neutron.create_sfc_port_chain(chain_config) + + def delete_port_groups(self): + ''' + Delete all port groups and port pairs + ''' + logger.info("Deleting the port groups...") + ppg_list = self.neutron.list_sfc_port_pair_groups()['port_pair_groups'] + for ppg in ppg_list: + self.neutron.delete_sfc_port_pair_group(ppg['id']) + + logger.info("Deleting the port pairs...") + pp_list = self.neutron.list_sfc_port_pairs()['port_pairs'] + for pp in pp_list: + self.neutron.delete_sfc_port_pair(pp['id']) + + def delete_chain(self): + ''' + Delete the classifiers and the chains + ''' + logger.info("Deleting the chain...") + pc_list = self.neutron.list_sfc_port_chains()['port_chains'] + for pc in pc_list: + self.neutron.delete_sfc_port_chain(pc['id']) + + logger.info("Deleting the classifiers...") + fc_list = self.neutron.list_sfc_flow_classifiers()['flow_classifiers'] + for fc in fc_list: + self.neutron.delete_sfc_flow_classifier(fc['id']) + # TACKER SECTION # def get_tacker_client_version(): diff --git a/sfc/tests/functest/config.yaml b/sfc/tests/functest/config.yaml index 0144352e..d595f0cf 100644 --- a/sfc/tests/functest/config.yaml +++ b/sfc/tests/functest/config.yaml @@ -25,6 +25,10 @@ defaults: vnfd-dir: "vnfd-templates" vnfd-default-params-file: "test-vnfd-default-params.yaml" + # mano_component can be [tacker, no-mano]. When no-mano, + # then networking-sfc is used + mano_component: "no-mano" + # [OPTIONAL] Only when deploying VNFs without the default image (vxlan_tool) # vnf_image_name: xxx # vnf_image_format: yyy diff --git a/sfc/tests/functest/run_sfc_tests.py b/sfc/tests/functest/run_sfc_tests.py index 6fe211bf..2f72acb5 100644 --- a/sfc/tests/functest/run_sfc_tests.py +++ b/sfc/tests/functest/run_sfc_tests.py @@ -176,7 +176,9 @@ class SfcFunctest(testcase.TestCase): result = {'status': 'FAILED'} creators = tc_instance.get_creators() if self.cleanup_flag is True: - sfc_cleanup.cleanup(creators, odl_ip=odl_ip, + sfc_cleanup.cleanup(creators, + COMMON_CONFIG.mano_component, + odl_ip=odl_ip, odl_port=odl_port) cleanup_run_flag = True end_time = time.time() @@ -198,7 +200,9 @@ class SfcFunctest(testcase.TestCase): self.details.update({test_name: dic}) if cleanup_run_flag is not True and self.cleanup_flag is True: - sfc_cleanup.cleanup(creators, odl_ip=odl_ip, + sfc_cleanup.cleanup(creators, + COMMON_CONFIG.mano_component, + odl_ip=odl_ip, odl_port=odl_port) self.stop_time = time.time() diff --git a/sfc/tests/functest/sfc_chain_deletion.py b/sfc/tests/functest/sfc_chain_deletion.py index 527dc822..3944cf90 100644 --- a/sfc/tests/functest/sfc_chain_deletion.py +++ b/sfc/tests/functest/sfc_chain_deletion.py @@ -32,11 +32,12 @@ class SfcChainDeletion(sfc_parent_function.SfcCommonTestCase): def run(self): logger.info("The test scenario %s is starting", __name__) - self.create_custom_vnfd(self.testcase_config.test_vnfd_red, - 'test-vnfd1') + self.register_vnf_template(self.testcase_config.test_vnfd_red, + 'test-vnfd1') self.create_vnf(self.vnfs[0], 'test-vnfd1', 'test-vim') - self.create_chain(self.testcase_config) + self.create_vnffg(self.testcase_config.test_vnffgd_red, 'red', + 'red_http', port=80, protocol='tcp', symmetric=False) t1 = threading.Thread(target=odl_utils.wait_for_classification_rules, args=(self.ovs_logger, self.compute_nodes, @@ -63,7 +64,9 @@ class SfcChainDeletion(sfc_parent_function.SfcCommonTestCase): self.remove_vnffg('red_http', 'red') self.check_deletion() - self.create_chain(self.testcase_config) + self.create_vnffg(self.testcase_config.test_vnffgd_red, 'blue', + 'blue_http', port=80, protocol='tcp', + symmetric=False, only_chain=True) t2 = threading.Thread(target=odl_utils.wait_for_classification_rules, args=(self.ovs_logger, self.compute_nodes, diff --git a/sfc/tests/functest/sfc_one_chain_two_service_functions.py b/sfc/tests/functest/sfc_one_chain_two_service_functions.py index 039cf625..38d6176f 100644 --- a/sfc/tests/functest/sfc_one_chain_two_service_functions.py +++ b/sfc/tests/functest/sfc_one_chain_two_service_functions.py @@ -29,16 +29,17 @@ class SfcOneChainTwoServiceTC(sfc_parent_function.SfcCommonTestCase): def run(self): logger.info("The test scenario %s is starting", __name__) - self.create_custom_vnfd(self.testcase_config.test_vnfd_red, - 'test-vnfd1') - self.create_custom_vnfd(self.testcase_config.test_vnfd_blue, - 'test-vnfd2') + + self.register_vnf_template(self.testcase_config.test_vnfd_red, + 'test-vnfd1') + self.register_vnf_template(self.testcase_config.test_vnfd_blue, + 'test-vnfd2') self.create_vnf(self.vnfs[0], 'test-vnfd1', 'test-vim') self.create_vnf(self.vnfs[1], 'test-vnfd2', 'test-vim') self.create_vnffg(self.testcase_config.test_vnffgd_red, 'red', - 'red_http') + 'red_http', port=80, protocol='tcp', symmetric=False) # Start measuring the time it takes to implement the # classification rules t1 = threading.Thread(target=odl_utils.wait_for_classification_rules, diff --git a/sfc/tests/functest/sfc_parent_function.py b/sfc/tests/functest/sfc_parent_function.py index 6af8e8ec..d93b2fbf 100644 --- a/sfc/tests/functest/sfc_parent_function.py +++ b/sfc/tests/functest/sfc_parent_function.py @@ -51,11 +51,15 @@ class SfcCommonTestCase(object): self.vnf_id = None self.client_floating_ip = None self.server_floating_ip = None - self.fips_sfs = None + self.fips_sfs = [] self.neutron_port = None + self.vnf_objects = dict() self.testcase_config = testcase_config self.vnfs = vnfs + # n-sfc variables + self.port_groups = [] + self.prepare_env(testcase_config, supported_installers, vnfs) def prepare_env(self, testcase_config, supported_installers, vnfs): @@ -117,9 +121,10 @@ class SfcCommonTestCase(object): self.controller_clients = test_utils.get_ssh_clients(controller_nodes) self.compute_clients = test_utils.get_ssh_clients(self.compute_nodes) - self.tacker_client = os_sfc_utils.get_tacker_client() - os_sfc_utils.register_vim(self.tacker_client, - vim_file=COMMON_CONFIG.vim_file) + if COMMON_CONFIG.mano_component == 'tacker': + self.tacker_client = os_sfc_utils.get_tacker_client() + os_sfc_utils.register_vim(self.tacker_client, + vim_file=COMMON_CONFIG.vim_file) self.ovs_logger = ovs_log.OVSLogger( os.path.join(COMMON_CONFIG.sfc_test_dir, 'ovs-logs'), @@ -170,13 +175,15 @@ class SfcCommonTestCase(object): openstack_sfc.create_instance(SERVER, COMMON_CONFIG.flavor, self.image_creator, self.network, self.sg, - self.test_topology['server']) + self.test_topology['server'], + [SERVER + '-port']) self.client_instance, self.client_creator = \ openstack_sfc.create_instance(CLIENT, COMMON_CONFIG.flavor, self.image_creator, self.network, self.sg, - self.test_topology['client']) + self.test_topology['client'], + [CLIENT + '-port']) logger.info('This test is run with the topology {0}'.format( self.test_topology['id'])) logger.info('Topology description: {0}'.format( @@ -186,6 +193,20 @@ class SfcCommonTestCase(object): logger.info("Server instance received private ip [{}]".format( self.server_ip)) + def register_vnf_template(self, test_case_name, template_name): + """ Register the template which defines the VNF + + :param test_case_name: the name of the test case + :param template_name: name of the template + """ + + if COMMON_CONFIG.mano_component == 'tacker': + self.create_custom_vnfd(test_case_name, template_name) + + elif COMMON_CONFIG.mano_component == 'no-mano': + # networking-sfc does not have the template concept + pass + def create_custom_vnfd(self, test_case_name, vnfd_name): """Create VNF Descriptor (VNFD) @@ -201,12 +222,14 @@ class SfcCommonTestCase(object): tosca_file=tosca_file, vnfd_name=vnfd_name) - def create_vnf(self, vnf_names, vnfd_name, vim_name): - """Create vnf + def create_vnf(self, vnf_name, vnfd_name=None, vim_name=None, + symmetric=False): + """Create custom vnf - :param vnf_names: names of available vnf(s) + :param vnf_name: name of the vnf :param vnfd_name: name of the vnfd template (tacker) :param vim_name: name of the vim (tacker) + :param symmetric: specifies whether this is part of the symmetric test :return: av zone """ @@ -215,14 +238,35 @@ class SfcCommonTestCase(object): logger.info('Topology description: {0}' .format(self.test_topology['description'])) - os_sfc_utils.create_vnf_in_av_zone( - self.tacker_client, vnf_names, vnfd_name, vim_name, - self.default_param_file, self.test_topology[vnf_names]) - - self.vnf_id = os_sfc_utils.wait_for_vnf(self.tacker_client, - vnf_name=vnf_names) - if self.vnf_id is None: - raise Exception('ERROR while booting vnfs') + if COMMON_CONFIG.mano_component == 'tacker': + os_sfc_utils.create_vnf_in_av_zone( + self.tacker_client, vnf_name, vnfd_name, vim_name, + self.default_param_file, self.test_topology[vnf_name]) + + self.vnf_id = os_sfc_utils.wait_for_vnf(self.tacker_client, + vnf_name=vnf_name) + if self.vnf_id is None: + raise Exception('ERROR while booting vnfs') + + elif COMMON_CONFIG.mano_component == 'no-mano': + av_zone = self.test_topology[vnf_name] + if symmetric: + ports = [vnf_name + '-port1', vnf_name + '-port2'] + else: + ports = [vnf_name + '-port'] + vnf_instance, vnf_creator = \ + openstack_sfc.create_instance(vnf_name, COMMON_CONFIG.flavor, + self.vnf_image_creator, + self.network, + self.sg, + av_zone, + ports) + + if not openstack_sfc.wait_for_vnf(vnf_creator): + raise Exception('ERROR while booting vnf %s' % vnf_name) + + self.creators.append(vnf_creator) + self.vnf_objects[vnf_name] = [vnf_creator, vnf_instance] def assign_floating_ip_client_server(self): """Assign floating IPs on the router about server and the client @@ -238,18 +282,28 @@ class SfcCommonTestCase(object): self.server_floating_ip = openstack_sfc.assign_floating_ip( self.router, self.server_instance, self.server_creator) - def assign_floating_ip_sfs(self, vnf_ip=None): + def assign_floating_ip_sfs(self): """Assign floating IPs to service function - :param vnf_ip: IP of vnf - optional :return: The list fips_sfs consist of the available IPs for service functions """ logger.info("Assigning floating IPs to service functions") - self.fips_sfs = openstack_sfc.assign_floating_ip_vnfs(self.router, - vnf_ip) + if COMMON_CONFIG.mano_component == 'tacker': + vnf_ip = os_sfc_utils.get_vnf_ip(self.tacker_client, + vnf_id=self.vnf_id) + self.fips_sfs = openstack_sfc.assign_floating_ip_vnfs(self.router, + vnf_ip) + elif COMMON_CONFIG.mano_component == 'no-mano': + for vnf in self.vnfs: + # creator object is in [0] and instance in [1] + vnf_instance = self.vnf_objects[vnf][1] + vnf_creator = self.vnf_objects[vnf][0] + sf_floating_ip = openstack_sfc.assign_floating_ip( + self.router, vnf_instance, vnf_creator) + self.fips_sfs.append(sf_floating_ip) def check_floating_ips(self): """Check the responsivness of the floating IPs @@ -338,37 +392,122 @@ class SfcCommonTestCase(object): :param par_vnffgd_name: The vnffgd name of network components :return: Remove the vnffg and vnffgd components """ + if COMMON_CONFIG.mano_component == 'tacker': + os_sfc_utils.delete_vnffg(self.tacker_client, + vnffg_name=par_vnffg_name) - os_sfc_utils.delete_vnffg(self.tacker_client, - vnffg_name=par_vnffg_name) + os_sfc_utils.delete_vnffgd(self.tacker_client, + vnffgd_name=par_vnffgd_name) - os_sfc_utils.delete_vnffgd(self.tacker_client, - vnffgd_name=par_vnffgd_name) + elif COMMON_CONFIG.mano_component == 'no-mano': + # TODO: If we had a testcase where only one chains must be removed + # we would need to add the logic. Now it removes all of them + openstack_sfc.delete_chain() - def create_vnffg(self, testcase_config_name, vnf_name, conn_name): + def create_vnffg(self, testcase_config_name, vnffgd_name, vnffg_name, + port=80, protocol='tcp', symmetric=False, + only_chain=False): """Create the vnffg components following the instructions from relevant templates. :param testcase_config_name: The config input of the test case - :param vnf_name: The name of the vnf - :param conn_name: Protocol type / name of the component + :param vnffgd_name: The name of the vnffgd template + :param vnffg_name: The name for the vnffg :return: Create the vnffg component """ - tosca_file = os.path.join(COMMON_CONFIG.sfc_test_dir, - COMMON_CONFIG.vnffgd_dir, - testcase_config_name) - - os_sfc_utils.create_vnffgd(self.tacker_client, - tosca_file=tosca_file, - vnffgd_name=vnf_name) - - self.neutron_port = openstack_sfc.get_client_port(self.client_instance, - self.client_creator) - os_sfc_utils.create_vnffg_with_param_file(self.tacker_client, vnf_name, - conn_name, - self.default_param_file, - self.neutron_port.id) + logger.info("Creating the vnffg...") + + if COMMON_CONFIG.mano_component == 'tacker': + tosca_file = os.path.join(COMMON_CONFIG.sfc_test_dir, + COMMON_CONFIG.vnffgd_dir, + testcase_config_name) + + os_sfc_utils.create_vnffgd(self.tacker_client, + tosca_file=tosca_file, + vnffgd_name=vnffgd_name) + + self.neutron_port = \ + openstack_sfc.get_instance_port(self.client_instance, + self.client_creator) + + if symmetric: + server_port = openstack_sfc.get_instance_port( + self.server_instance, + self.server_creator) + server_ip_prefix = self.server_ip + '/32' + + os_sfc_utils.create_vnffg_with_param_file( + self.tacker_client, + vnffgd_name, + vnffg_name, + self.default_param_file, + self.neutron_port.id, + server_port=server_port.id, + server_ip=server_ip_prefix) + + else: + os_sfc_utils.create_vnffg_with_param_file( + self.tacker_client, + vnffgd_name, + vnffg_name, + self.default_param_file, + self.neutron_port.id) + + elif COMMON_CONFIG.mano_component == 'no-mano': + if not only_chain: + for vnf in self.vnfs: + # creator object is in [0] and instance in [1] + vnf_instance = self.vnf_objects[vnf][1] + vnf_creator = self.vnf_objects[vnf][0] + if symmetric: + # VNFs have two ports + p1 = vnf_instance.name + '-port1' + neutron_port1 = \ + openstack_sfc.get_instance_port(vnf_instance, + vnf_creator, + port_name=p1) + p2 = vnf_instance.name + '-port2' + neutron_port2 = \ + openstack_sfc.get_instance_port(vnf_instance, + vnf_creator, + port_name=p2) + neutron_ports = [neutron_port1, neutron_port2] + + else: + neutron_port1 = \ + openstack_sfc.get_instance_port(vnf_instance, + vnf_creator) + neutron_ports = [neutron_port1] + + port_group = \ + openstack_sfc.create_port_groups(neutron_ports, + vnf_instance) + + self.port_groups.append(port_group) + self.neutron_port = \ + openstack_sfc.get_instance_port(self.client_instance, + self.client_creator) + + if symmetric: + # We must pass the server_port and server_ip in the symmetric + # case. Otherwise ODL does not work well + server_port = openstack_sfc.get_instance_port( + self.server_instance, + self.server_creator) + server_ip_prefix = self.server_ip + '/32' + openstack_sfc.create_chain(self.port_groups, + self.neutron_port.id, + port, protocol, vnffg_name, + symmetric, + server_port=server_port.id, + server_ip=server_ip_prefix) + + else: + openstack_sfc.create_chain(self.port_groups, + self.neutron_port.id, + port, protocol, vnffg_name, + symmetric) def present_results_http(self): """Check whether the connection between server and client using @@ -438,19 +577,6 @@ class SfcCommonTestCase(object): return results - def create_chain(self, testcase_config): - """Create a connection chain for the test scenario purposes - - :param testcase_config: The config input of the test case - :return: Create the proper chain for the specific test scenario - """ - - self.neutron_port = openstack_sfc.get_client_port(self.client_instance, - self.client_creator) - odl_utils.create_chain(self.tacker_client, self.default_param_file, - self.neutron_port, COMMON_CONFIG, - testcase_config) - def check_deletion(self): """Check that the deletion of the chain has been completed sucessfully. diff --git a/sfc/tests/functest/sfc_symmetric_chain.py b/sfc/tests/functest/sfc_symmetric_chain.py index 1b57a244..4593d8c6 100644 --- a/sfc/tests/functest/sfc_symmetric_chain.py +++ b/sfc/tests/functest/sfc_symmetric_chain.py @@ -8,13 +8,10 @@ # # http://www.apache.org/licenses/LICENSE-2.0 # -import os -import sys import threading import logging import urllib3 -import sfc.lib.openstack_utils as os_sfc_utils import sfc.lib.odl_utils as odl_utils import sfc.lib.config as sfc_config from sfc.tests.functest import sfc_parent_function @@ -24,7 +21,6 @@ logger = logging.getLogger(__name__) COMMON_CONFIG = sfc_config.CommonConfig() CLIENT = "client" SERVER = "server" -openstack_sfc = os_sfc_utils.OpenStackSFC() class SfcSymmetricChain(sfc_parent_function.SfcCommonTestCase): @@ -42,54 +38,21 @@ class SfcSymmetricChain(sfc_parent_function.SfcCommonTestCase): def run(self): logger.info("The test scenario %s is starting", __name__) - self.create_custom_vnfd(self.testcase_config.test_vnfd, 'test-vnfd1') - self.create_vnf(self.vnfs[0], 'test-vnfd1', 'test-vim') - - if self.vnf_id is None: - logger.error('ERROR while booting VNF') - sys.exit(1) - - tosca_file = os.path.join( - COMMON_CONFIG.sfc_test_dir, - COMMON_CONFIG.vnffgd_dir, - self.testcase_config.test_vnffgd) - os_sfc_utils.create_vnffgd( - self.tacker_client, - tosca_file=tosca_file, - vnffgd_name='test-vnffgd') - - client_port = openstack_sfc.get_client_port( - self.client_instance, - self.client_creator) - server_port = openstack_sfc.get_client_port( - self.server_instance, - self.server_creator) - - server_ip_prefix = self.server_ip + '/32' - - 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_vnffg_with_param_file( - self.tacker_client, - 'test-vnffgd', - 'test-vnffg', - default_param_file, - client_port.id, - server_port.id, - server_ip_prefix) + + self.register_vnf_template(self.testcase_config.test_vnfd, + 'test-vnfd1') + self.create_vnf(self.vnfs[0], 'test-vnfd1', 'test-vim', symmetric=True) + + self.create_vnffg(self.testcase_config.test_vnffgd, 'red-symmetric', + 'red_http', port=80, protocol='tcp', symmetric=True) + # Start measuring the time it takes to implement the classification # rules t1 = threading.Thread(target=wait_for_classification_rules, args=(self.ovs_logger, self.compute_nodes, - self.server_instance.compute_host, - server_port, + self.odl_ip, self.odl_port, self.client_instance.compute_host, - client_port, self.odl_ip, - self.odl_port,)) - + [self.neutron_port],)) try: t1.start() except Exception as e: @@ -98,9 +61,7 @@ class SfcSymmetricChain(sfc_parent_function.SfcCommonTestCase): logger.info("Assigning floating IPs to instances") self.assign_floating_ip_client_server() - vnf_ip = os_sfc_utils.get_vnf_ip(self.tacker_client, - vnf_id=self.vnf_id) - self.assign_floating_ip_sfs(vnf_ip) + self.assign_floating_ip_sfs() self.check_floating_ips() diff --git a/sfc/tests/functest/sfc_two_chains_SSH_and_HTTP.py b/sfc/tests/functest/sfc_two_chains_SSH_and_HTTP.py index b8fc2826..e30967b9 100644 --- a/sfc/tests/functest/sfc_two_chains_SSH_and_HTTP.py +++ b/sfc/tests/functest/sfc_two_chains_SSH_and_HTTP.py @@ -32,16 +32,16 @@ class SfcTwoChainsSSHandHTTP(sfc_parent_function.SfcCommonTestCase): logger.info("The test scenario %s is starting", __name__) - self.create_custom_vnfd(self.testcase_config.test_vnfd_red, - 'test-vnfd1') - self.create_custom_vnfd(self.testcase_config.test_vnfd_blue, - 'test-vnfd2') + self.register_vnf_template(self.testcase_config.test_vnfd_red, + 'test-vnfd1') + self.register_vnf_template(self.testcase_config.test_vnfd_blue, + 'test-vnfd2') self.create_vnf(self.vnfs[0], 'test-vnfd1', 'test-vim') self.create_vnf(self.vnfs[1], 'test-vnfd2', 'test-vim') self.create_vnffg(self.testcase_config.test_vnffgd_red, 'red', - 'red_http') + 'red_http', port=80, protocol='tcp', symmetric=False) t1 = threading.Thread(target=odl_utils.wait_for_classification_rules, args=(self.ovs_logger, self.compute_nodes, diff --git a/sfc/unit_tests/unit/lib/test_cleanup.py b/sfc/unit_tests/unit/lib/test_cleanup.py index 5ec4261e..b83e229f 100644 --- a/sfc/unit_tests/unit/lib/test_cleanup.py +++ b/sfc/unit_tests/unit/lib/test_cleanup.py @@ -262,8 +262,10 @@ class SfcCleanupTesting(unittest.TestCase): mock_log.assert_has_calls(log_calls) mock_del_vim.assert_has_calls(del_calls) + @patch('sfc.lib.cleanup.logger.info') @patch('sfc.lib.cleanup.logger.error') - def test_delete_openstack_objects_exception(self, mock_log): + def test_delete_openstack_objects_exception(self, mock_log_err, + mock_log_info): """ Check the proper functionality of the delete_openstack_objects @@ -283,11 +285,13 @@ class SfcCleanupTesting(unittest.TestCase): mock_creator_objs_list = [mock_creator_obj_one, mock_creator_obj_two] log_calls = [call('Unexpected error cleaning - %s', exception_two), - call('Unexpected error cleaning - %s', exception_one)] + call('Unexpected error cleaning - %s', exception_one), + call('Deleting the openstack objects...')] cleanup.delete_openstack_objects(mock_creator_objs_list) - mock_log.assert_has_calls(log_calls) + mock_log_err.assert_has_calls(log_calls[:2]) + mock_log_info.assert_has_calls(log_calls[2:]) @patch('sfc.lib.openstack_utils.OpenStackSFC', autospec=True) def test_delete_untracked_security_groups(self, @@ -314,53 +318,71 @@ class SfcCleanupTesting(unittest.TestCase): mock_del_odl_res.assert_has_calls(odl_res_calls) mock_del_odl_ietf.assert_called_once_with(self.odl_ip, self.odl_port) + @patch('sfc.lib.openstack_utils.OpenStackSFC', autospec=True) + def test_cleanup_nsfc_objects(self, mock_os_sfc): + mock_os_sfc_ins = mock_os_sfc.return_value + cleanup.cleanup_nsfc_objects() + mock_os_sfc_ins.delete_chain.assert_called_once() + mock_os_sfc_ins.delete_port_groups.assert_called_once() + @patch('time.sleep') - @patch('sfc.lib.cleanup.delete_openstack_objects') - @patch('sfc.lib.cleanup.cleanup_odl') - def test_cleanup(self, - mock_cleanup_odl, - mock_del_os_obj, - mock_time): + def test_cleanup_tacker_objects(self, mock_time): mock_dict = {'delete_vnffgs': DEFAULT, 'delete_vnffgds': DEFAULT, 'delete_vnfs': DEFAULT, 'delete_vnfds': DEFAULT, - 'delete_vims': DEFAULT, - 'delete_untracked_security_groups': DEFAULT} + 'delete_vims': DEFAULT} with patch.multiple('sfc.lib.cleanup', **mock_dict) as mock_values: - - cleanup.cleanup(['creator_one', 'creator_two'], - self.odl_ip, - self.odl_port) + cleanup.cleanup_tacker_objects() for key in mock_values: mock_values[key].assert_called_once() + + mock_time.assert_called_once_with(20) + + @patch('sfc.lib.cleanup.cleanup_tacker_objects') + def test_cleanup_mano_objects_tacker(self, mock_cleanup_tacker): + cleanup.cleanup_mano_objects('tacker') + mock_cleanup_tacker.assert_called_once() + + @patch('sfc.lib.cleanup.cleanup_nsfc_objects') + def test_cleanup_mano_objects_nsfc(self, mock_cleanup_nsfc): + cleanup.cleanup_mano_objects('no-mano') + mock_cleanup_nsfc.assert_called_once() + + @patch('sfc.lib.cleanup.delete_untracked_security_groups') + @patch('sfc.lib.cleanup.cleanup_mano_objects') + @patch('sfc.lib.cleanup.delete_openstack_objects') + @patch('sfc.lib.cleanup.cleanup_odl') + def test_cleanup(self, + mock_cleanup_odl, + mock_del_os_obj, + mock_cleanup_mano, + mock_untr_sec_grps): + + cleanup.cleanup(['creator_one', 'creator_two'], + 'mano', + self.odl_ip, + self.odl_port) + mock_cleanup_odl.assert_called_once_with(self.odl_ip, self.odl_port) mock_del_os_obj.assert_called_once_with(['creator_one', 'creator_two']) - mock_time.assert_called_once_with(20) + mock_cleanup_mano.assert_called_once_with('mano') + mock_untr_sec_grps.assert_called_once() - @patch('time.sleep') + @patch('sfc.lib.cleanup.cleanup_mano_objects') @patch('sfc.lib.cleanup.cleanup_odl') def test_cleanup_from_bash(self, mock_cleanup_odl, - mock_time): - - mock_dict = {'delete_vnffgs': DEFAULT, - 'delete_vnffgds': DEFAULT, - 'delete_vnfs': DEFAULT, - 'delete_vnfds': DEFAULT, - 'delete_vims': DEFAULT} - with patch.multiple('sfc.lib.cleanup', - **mock_dict) as mock_values: + mock_cleanup_mano): - cleanup.cleanup_from_bash(self.odl_ip, - self.odl_port) + cleanup.cleanup_from_bash(self.odl_ip, + self.odl_port, + 'mano') - for key in mock_values: - mock_values[key].assert_called_once() mock_cleanup_odl.assert_called_once_with(self.odl_ip, self.odl_port) - mock_time.assert_called_once_with(20) + mock_cleanup_mano.assert_called_once_with(mano='mano') diff --git a/sfc/unit_tests/unit/lib/test_odl_utils.py b/sfc/unit_tests/unit/lib/test_odl_utils.py index d151a1ca..17ad88f5 100644 --- a/sfc/unit_tests/unit/lib/test_odl_utils.py +++ b/sfc/unit_tests/unit/lib/test_odl_utils.py @@ -733,43 +733,3 @@ class SfcOdlUtilsTesting(unittest.TestCase): 'compute_nodes') assert mock_sleep.call_count == 2 mock_log.assert_not_called() - - @patch('os.path.join', autospec=True) - @patch('sfc.lib.odl_utils.os_sfc_utils.create_vnffg_with_param_file', - autospec=True) - @patch('sfc.lib.odl_utils.os_sfc_utils.create_vnffgd', autospec=True) - def test_create_chain(self, mock_create_vnffgd, - mock_create_vnffg_with_param_file, - mock_join): - """ - Checks the proper functionality of create_chain - function - """ - - mock_join.return_value = '/tosca_file' - mock_neutron = Mock() - mock_common_config = Mock() - mock_testcase_config = Mock() - mock_common_config.vnffgd_dir = 'mock_vnffgd_dir' - mock_common_config.sfc_test_dir = 'mock_sfc_test_dir' - mock_testcase_config.test_vnffgd_red = 'mock_test_vnffgd_red' - mock_neutron.id = 'mock_id' - - odl_utils.create_chain('tacker_client', - 'default_param_file', - mock_neutron, - mock_common_config, - mock_testcase_config) - - mock_join.assert_called_once_with('mock_sfc_test_dir', - 'mock_vnffgd_dir', - 'mock_test_vnffgd_red') - mock_create_vnffgd.assert_called_once_with('tacker_client', - tosca_file='/tosca_file', - vnffgd_name='red') - mock_create_vnffg_with_param_file.assert_called_once_with( - 'tacker_client', - 'red', - 'red_http', - 'default_param_file', - 'mock_id') diff --git a/sfc/unit_tests/unit/lib/test_openstack_utils.py b/sfc/unit_tests/unit/lib/test_openstack_utils.py index bc08a184..595f09b5 100644 --- a/sfc/unit_tests/unit/lib/test_openstack_utils.py +++ b/sfc/unit_tests/unit/lib/test_openstack_utils.py @@ -265,7 +265,8 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): """ vm_con_ins = mock_vm_instance_config.return_value - pc_ins = mock_port_config.return_value + pc_inss = ['pc_config1', 'pc_config2'] + mock_port_config.side_effect = pc_inss os_vm_ins = mock_os_vm_instance.return_value os_vm_ins_cre = os_vm_ins.create.return_value expected = (os_vm_ins_cre, os_vm_ins) @@ -277,19 +278,21 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): img_cre.image_settings = 'image_settings' log_calls = [call('Creating the instance vm_name...')] - + pc_calls = [call(name='port1', network_name='nw_name'), + call(name='port2', network_name='nw_name')] result = self.os_sfc.create_instance('vm_name', 'flavor_name', img_cre, network, secgrp, - 'av_zone') + 'av_zone', + ['port1', 'port2']) self.assertEqual(expected, result) mock_vm_instance_config.assert_called_once_with(name='vm_name', flavor='flavor_name', security_group_names='' 'sec_grp', - port_settings=[pc_ins], + port_settings=pc_inss, availability_zone='av' '_zone') mock_os_vm_instance.assert_called_once_with(self.os_creds, @@ -297,6 +300,7 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): 'image_settings') self.assertEqual([os_vm_ins], self.os_sfc.creators) mock_log.info.assert_has_calls(log_calls) + mock_port_config.assert_has_calls(pc_calls) @patch('sfc.lib.openstack_utils.nova_utils.get_hypervisor_hosts', autospec=True) @@ -510,9 +514,9 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): @patch('sfc.lib.openstack_utils.logger', autospec=True) @patch('sfc.lib.openstack_utils.cr_inst.OpenStackVmInstance', autospec=True) - def test_get_client_port_raised_exceptioin(self, - mock_os_vm, - mock_log): + def test_get_instance_port_raised_exceptioin(self, + mock_os_vm, + mock_log): """ Checks the proper functionality of get_client_port function when no port is returned @@ -527,7 +531,7 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): " with name mock_vm_name-port")] with self.assertRaises(Exception) as cm: - self.os_sfc.get_client_port(mock_vm, mock_os_vm_ins) + self.os_sfc.get_instance_port(mock_vm, mock_os_vm_ins) self.assertEqual(cm.exception.message, ErrorMSG) mock_log.error.assert_has_calls(log_calls) @@ -535,9 +539,9 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): @patch('sfc.lib.openstack_utils.logger', autospec=True) @patch('sfc.lib.openstack_utils.cr_inst.OpenStackVmInstance', autospec=True) - def test_get_client_port(self, - mock_os_vm, - mock_log): + def test_get_instance_port(self, + mock_os_vm, + mock_log): """ Checks the proper functionality of get_client_port function when no port is returned @@ -547,7 +551,7 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): mock_vm = Mock() mock_vm.name = 'mock_vm_name' mock_os_vm_ins.get_port_by_name.return_value = 'mock_port' - result = self.os_sfc.get_client_port(mock_vm, mock_os_vm_ins) + result = self.os_sfc.get_instance_port(mock_vm, mock_os_vm_ins) self.assertEqual('mock_port', result) @patch('sfc.lib.openstack_utils.neutron_utils.list_security_groups', @@ -569,9 +573,243 @@ class SfcOpenStackUtilsTesting(unittest.TestCase): mock_list_security_groups.assert_called_once_with(self.neutron) mock_delete_security_group.assert_has_calls(del_calls) + @patch('snaps.openstack.create_instance.OpenStackVmInstance', + autospec=True) + def test_wait_for_vnf(self, mock_osvminstance): + """ + Checks the method wait_for_vnf + """ -class SfcTackerSectionTesting(unittest.TestCase): + mock_osvm_ins = mock_osvminstance.return_value + mock_osvm_ins.vm_active.return_value = True + result = self.os_sfc.wait_for_vnf(mock_osvm_ins) + self.assertTrue(result) + + @patch('snaps.domain.vm_inst.VmInst', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_raises_exception(self, mock_log, mock_vm): + """ + Checks the create_port_groups when length of ports is greater than 2 + """ + mock_vm_ins = mock_vm.return_value + mock_vm_ins.name = 'vm' + log_calls_info = [call('Creating the port pairs...')] + log_calls_err = [call('Only SFs with one or two ports are supported')] + exception_message = "Failed to create port pairs" + vnf_ports = ['p1', 'p2', 'p3'] + with self.assertRaises(Exception) as cm: + self.os_sfc.create_port_groups(vnf_ports, mock_vm_ins) + self.assertEqual(exception_message, cm.exception.message) + mock_log.info.assert_has_calls(log_calls_info) + mock_log.error.assert_has_calls(log_calls_err) + + @patch('snaps.domain.network.Port', autospec=True) + @patch('snaps.domain.vm_inst.VmInst', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_returns_none_from_pp(self, mock_log, + mock_vm, + mock_port): + """ + Checks the create_port_groups when something goes wrong in port pair + creation + """ + mock_vm_ins = mock_vm.return_value + mock_vm_ins.name = 'vm' + log_calls_info = [call('Creating the port pairs...')] + log_calls_warn = [call('Chain creation failed due to port pair ' + 'creation failed for vnf %(vnf)s', {'vnf': 'vm'})] + mock_port_ins = mock_port.return_value + mock_port_ins2 = mock_port.return_value + mock_port_ins.id = '123abc' + mock_port_ins2.id = '456def' + self.neutron.create_sfc_port_pair.return_value = None + result = self.os_sfc.create_port_groups( + [mock_port_ins, mock_port_ins2], mock_vm_ins) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls_info) + mock_log.warning.assert_has_calls(log_calls_warn) + + @patch('snaps.domain.network.Port', autospec=True) + @patch('snaps.domain.vm_inst.VmInst', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_returns_none_from_ppg(self, mock_log, + mock_vm, + mock_port): + """ + Checks the create_port_groups when something goes wrong in port pair + group creation + """ + mock_vm_ins = mock_vm.return_value + mock_vm_ins.name = 'vm' + log_calls_info = [call('Creating the port pairs...'), + call('Creating the port pair groups...')] + log_calls_warn = [call('Chain creation failed due to port pair group ' + 'creation failed for vnf %(vnf)', 'vm')] + mock_port_ins = mock_port.return_value + mock_port_ins.id = '123abc' + self.neutron.create_sfc_port_pair.return_value = \ + {'port_pair': {'id': 'pp_id'}} + self.neutron.create_sfc_port_pair_group.return_value = None + result = self.os_sfc.create_port_groups([mock_port_ins], mock_vm_ins) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls_info) + mock_log.warning.assert_has_calls(log_calls_warn) + + @patch('snaps.domain.network.Port', autospec=True) + @patch('snaps.domain.vm_inst.VmInst', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_returns_id(self, mock_log, mock_osvm, + mock_port): + """ + Checks the create_port_groups when everything goes as expected + """ + + log_calls_info = [call('Creating the port pairs...'), + call('Creating the port pair groups...')] + mock_port_ins = mock_port.return_value + mock_port_ins.id = '123abc' + mock_osvm_ins = mock_osvm.return_value + mock_osvm_ins.name = 'vm' + expected_port_pair = {'name': 'vm-connection-points', + 'description': 'port pair for vm', + 'ingress': '123abc', + 'egress': '123abc'} + self.neutron.create_sfc_port_pair.return_value = \ + {'port_pair': {'id': 'pp_id'}} + self.neutron.create_sfc_port_pair_group.return_value = \ + {'port_pair_group': {'id': 'pp_id'}} + expected_port_pair_gr = {'name': 'vm-port-pair-group', + 'description': 'port pair group for vm', + 'port_pairs': ['pp_id']} + + self.os_sfc.create_port_groups([mock_port_ins], mock_osvm_ins) + self.neutron.create_sfc_port_pair.assert_has_calls( + [call({'port_pair': expected_port_pair})]) + self.neutron.create_sfc_port_pair_group.assert_has_calls( + [call({'port_pair_group': expected_port_pair_gr})]) + mock_log.info.assert_has_calls(log_calls_info) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_chain(self, mock_log): + """ + Checks the create_chain method + """ + + log_calls = [call('Creating the classifier...'), + call('Creating the chain...')] + port_groups = ['1a', '2b'] + neutron_port = 'neutron_port_id' + port = 80 + protocol = 'tcp' + vnffg_name = 'red_http' + symmetrical = False + self.neutron.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + self.neutron.create_sfc_port_chain.return_value = \ + {'port_chain': {'id': 'pc_id'}} + + expected_sfc_classifier_params = {'name': vnffg_name + '-classifier', + 'logical_source_port': neutron_port, + 'destination_port_range_min': port, + 'destination_port_range_max': port, + 'protocol': protocol} + expected_chain_config = {'name': vnffg_name + '-port-chain', + 'description': 'port-chain for SFC', + 'port_pair_groups': port_groups, + 'flow_classifiers': ['fc_id']} + + self.os_sfc.create_chain(port_groups, neutron_port, port, + protocol, vnffg_name, symmetrical) + + self.neutron.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + self.neutron.create_sfc_port_chain.assert_has_calls( + [call({'port_chain': expected_chain_config})]) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_chain_symmetric(self, mock_log): + """ + Checks the create_chain method + """ + + log_calls = [call('Creating the classifier...'), + call('Creating the chain...')] + port_groups = ['1a', '2b'] + neutron_port = 'neutron_port_id' + port = 80 + protocol = 'tcp' + vnffg_name = 'red_http' + symmetrical = True + serv_p = '123abc' + server_ip = '1.1.1.2' + self.neutron.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + self.neutron.create_sfc_port_chain.return_value = \ + {'port_chain': {'id': 'pc_id'}} + + expected_sfc_classifier_params = {'name': vnffg_name + '-classifier', + 'logical_source_port': neutron_port, + 'destination_port_range_min': port, + 'destination_port_range_max': port, + 'destination_ip_prefix': server_ip, + 'logical_destination_port': serv_p, + 'protocol': protocol} + expected_chain_config = {'name': vnffg_name + '-port-chain', + 'description': 'port-chain for SFC', + 'port_pair_groups': port_groups, + 'flow_classifiers': ['fc_id'], + 'chain_parameters': {'symmetric': True}} + + self.os_sfc.create_chain(port_groups, neutron_port, port, + protocol, vnffg_name, symmetrical, + server_port=serv_p, server_ip=server_ip) + + self.neutron.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + self.neutron.create_sfc_port_chain.assert_has_calls( + [call({'port_chain': expected_chain_config})]) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_port_groups(self, mock_log): + """ + Checks the delete_port_groups method + """ + log_calls = [call('Deleting the port groups...'), + call('Deleting the port pairs...')] + self.neutron.list_sfc_port_pair_groups.return_value = \ + {'port_pair_groups': [{'id': 'id_ppg1'}, {'id': 'id_ppg2'}]} + self.neutron.list_sfc_port_pairs.return_value = \ + {'port_pairs': [{'id': 'id_pp1'}, {'id': 'id_pp2'}]} + self.os_sfc.delete_port_groups() + + self.neutron.delete_sfc_port_pair_group.assert_has_calls( + [call('id_ppg1'), call('id_ppg2')]) + self.neutron.delete_sfc_port_pair.assert_has_calls( + [call('id_pp1'), call('id_pp2')]) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_chain(self, mock_log): + """ + Checks the delete_chain method + """ + log_calls = [call('Deleting the chain...'), + call('Deleting the classifiers...')] + self.neutron.list_sfc_port_chains.return_value = \ + {'port_chains': [{'id': 'id_pc1'}]} + self.neutron.list_sfc_flow_classifiers.return_value = \ + {'flow_classifiers': [{'id': 'id_fc1'}]} + self.os_sfc.delete_chain() + self.neutron.delete_sfc_port_chain.assert_has_calls([call('id_pc1')]) + self.neutron.delete_sfc_flow_classifier.assert_has_calls( + [call('id_fc1')]) + mock_log.info.assert_has_calls(log_calls) + + +class SfcTackerSectionTesting(unittest.TestCase): def setUp(self): self.patcher = patch.object(tacker_client, 'Client', autospec=True) self.mock_tacker_client = self.patcher.start().return_value -- cgit 1.2.3-korg