diff options
Diffstat (limited to 'sfc/unit_tests/unit')
-rw-r--r-- | sfc/unit_tests/unit/__init__.py | 0 | ||||
-rw-r--r-- | sfc/unit_tests/unit/lib/test_cleanup.py | 469 | ||||
-rw-r--r-- | sfc/unit_tests/unit/lib/test_odl_utils.py | 817 | ||||
-rw-r--r-- | sfc/unit_tests/unit/lib/test_openstack_utils.py | 2504 | ||||
-rw-r--r-- | sfc/unit_tests/unit/lib/test_test_utils.py | 543 |
5 files changed, 4333 insertions, 0 deletions
diff --git a/sfc/unit_tests/unit/__init__.py b/sfc/unit_tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/sfc/unit_tests/unit/__init__.py diff --git a/sfc/unit_tests/unit/lib/test_cleanup.py b/sfc/unit_tests/unit/lib/test_cleanup.py new file mode 100644 index 00000000..e6f59d23 --- /dev/null +++ b/sfc/unit_tests/unit/lib/test_cleanup.py @@ -0,0 +1,469 @@ +#!/usr/bin/env python + +############################################################################### +# Copyright (c) 2018 Intracom Telecom and others. +# +# 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 unittest +import sfc.lib.cleanup as cleanup + +from mock import patch +from mock import call +from mock import DEFAULT +from mock import Mock + + +__author__ = "Dimitrios Markou <mardim@intracom-telecom.com>" + + +class SfcCleanupTesting(unittest.TestCase): + + def setUp(self): + self.odl_ip = '10.10.10.10' + self.odl_port = '8081' + self.patcher = patch('sfc.lib.openstack_utils.get_tacker_client') + self.mock_tacker_client = self.patcher.start() + self.mock_tacker_client.return_value = 'tacker_client_obj' + + def tearDown(self): + self.patcher.stop() + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.odl_utils.delete_odl_resource_elem') + @patch('sfc.lib.odl_utils.odl_resource_list_names') + @patch('sfc.lib.odl_utils.get_odl_resource_list') + def test_delete_odl_resource(self, mock_resource_list, + mock_resource_list_name, + mock_del_resource_elem, + mock_log): + """ + Checks if the functions which belong to the odl_utils + library are getting called. + """ + + resource = 'mock_resource' + log_calls = [call("Removing ODL resource: mock_resource/elem_one"), + call("Removing ODL resource: mock_resource/elem_two")] + + del_calls = [call(self.odl_ip, self.odl_port, resource, 'elem_one'), + call(self.odl_ip, self.odl_port, resource, 'elem_two')] + + mock_resource_list_name.return_value = ['elem_one', 'elem_two'] + mock_resource_list.return_value = ['rsrc_one', + 'rsrc_two', + 'rsrc_three'] + + cleanup.delete_odl_resources(self.odl_ip, self.odl_port, resource) + + mock_resource_list.assert_called_once_with(self.odl_ip, + self.odl_port, + resource) + mock_resource_list_name.assert_called_once_with( + resource, ['rsrc_one', 'rsrc_two', 'rsrc_three']) + mock_del_resource_elem.assert_has_calls(del_calls) + mock_log.assert_has_calls(log_calls) + + @patch('sfc.lib.odl_utils.get_odl_acl_list') + @patch('sfc.lib.odl_utils.odl_acl_types_names') + @patch('sfc.lib.odl_utils.delete_odl_acl') + def test_delete_odl_ietf_access_lists(self, + mock_del_acl, + mock_acl_types, + mock_get_acls): + """ + Ckecks the proper functionality of the delete_odl_ietf_access_lists + function + """ + + mock_acl_type_name_list = [('acl_type_one', 'name_one'), + ('acl_type_two', 'name_two')] + mock_get_acls.return_value = ['acl_one', 'acl_two'] + mock_acl_types.return_value = mock_acl_type_name_list + del_calls = [call(self.odl_ip, self.odl_port, key, value) + for key, value in mock_acl_type_name_list] + + cleanup.delete_odl_ietf_access_lists(self.odl_ip, self.odl_port) + + mock_get_acls.assert_called_once_with(self.odl_ip, self.odl_port) + mock_acl_types.assert_called_once_with(['acl_one', 'acl_two']) + mock_del_acl.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.list_vnfds', return_value=None) + def test_delete_vnfds_returned_list_is_none(self, mock_list_vnfds): + """ + Check the proper functionality of the delete_vnfds + function when the returned vnfds list is None + """ + + self.assertIsNone(cleanup.delete_vnfds()) + mock_list_vnfds.assert_called_once_with('tacker_client_obj') + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.openstack_utils.delete_vnfd') + @patch('sfc.lib.openstack_utils.list_vnfds') + def test_delete_vnfds_not_empty_list(self, + mock_list_vnfds, + mock_del_vnfd, + mock_log): + """ + Check the proper functionality of the delete_vnfds + function when the returned vnfds list is not empty + """ + + mock_list_vnfds.return_value = ['vnfd_one', 'vnfd_two'] + log_calls = [call("Removing vnfd: vnfd_one"), + call("Removing vnfd: vnfd_two")] + + del_calls = [call('tacker_client_obj', vnfd_id='vnfd_one'), + call('tacker_client_obj', vnfd_id='vnfd_two')] + + cleanup.delete_vnfds() + mock_list_vnfds.assert_called_once_with('tacker_client_obj') + mock_log.assert_has_calls(log_calls) + mock_del_vnfd.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.list_vnfs', return_value=None) + def test_delete_vnfs_returned_list_is_none(self, mock_list_vnfs): + """ + Check the proper functionality of the delete_vnfs + function when the returned vnfs list is None + """ + + self.assertIsNone(cleanup.delete_vnfs()) + mock_list_vnfs.assert_called_once_with('tacker_client_obj') + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.openstack_utils.delete_vnf') + @patch('sfc.lib.openstack_utils.list_vnfs') + def test_delete_vnfs_not_empty_list(self, + mock_list_vnfs, + mock_del_vnf, + mock_log): + """ + Check the proper functionality of the delete_vnfs + function when the returned vnfs list is not empty + """ + + mock_list_vnfs.return_value = ['vnf_one', 'vnf_two'] + log_calls = [call("Removing vnf: vnf_one"), + call("Removing vnf: vnf_two")] + + del_calls = [call('tacker_client_obj', vnf_id='vnf_one'), + call('tacker_client_obj', vnf_id='vnf_two')] + + cleanup.delete_vnfs() + mock_list_vnfs.assert_called_once_with('tacker_client_obj') + mock_log.assert_has_calls(log_calls) + mock_del_vnf.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.list_vnffgs', return_value=None) + def test_delete_vnffgs_returned_list_is_none(self, mock_list_vnffgs): + """ + Check the proper functionality of the delete_vnffgs + function when the returned vnffgs list is None + """ + + self.assertIsNone(cleanup.delete_vnffgs()) + mock_list_vnffgs.assert_called_once_with('tacker_client_obj') + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.openstack_utils.delete_vnffg') + @patch('sfc.lib.openstack_utils.list_vnffgs') + def test_delete_vnffgs_not_empty_list(self, + mock_list_vnffgs, + mock_del_vnffg, + mock_log): + """ + Check the proper functionality of the delete_vnffgs + function when the returned vnffgs list is not empty + """ + + mock_list_vnffgs.return_value = ['vnffg_one', 'vnffg_two'] + log_calls = [call("Removing vnffg: vnffg_two"), + call("Removing vnffg: vnffg_one")] + + del_calls = [call('tacker_client_obj', vnffg_id='vnffg_two'), + call('tacker_client_obj', vnffg_id='vnffg_one')] + + cleanup.delete_vnffgs() + mock_list_vnffgs.assert_called_once_with('tacker_client_obj') + mock_log.assert_has_calls(log_calls) + mock_del_vnffg.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.list_vnffgds', return_value=None) + def test_delete_vnffgds_returned_list_is_none(self, mock_list_vnffgds): + """ + Check the proper functionality of the delete_vnffgds + function when the returned vnffgds list is None + """ + + self.assertIsNone(cleanup.delete_vnffgds()) + mock_list_vnffgds.assert_called_once_with('tacker_client_obj') + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.openstack_utils.delete_vnffgd') + @patch('sfc.lib.openstack_utils.list_vnffgds') + def test_delete_vnffgds_not_empty_list(self, + mock_list_vnffgds, + mock_del_vnffgd, + mock_log): + """ + Check the proper functionality of the delete_vnffgds + function when the returned vnffgds list is not empty + """ + + mock_list_vnffgds.return_value = ['vnffgd_one', 'vnffgd_two'] + log_calls = [call("Removing vnffgd: vnffgd_one"), + call("Removing vnffgd: vnffgd_two")] + + del_calls = [call('tacker_client_obj', vnffgd_id='vnffgd_one'), + call('tacker_client_obj', vnffgd_id='vnffgd_two')] + + cleanup.delete_vnffgds() + mock_list_vnffgds.assert_called_once_with('tacker_client_obj') + mock_log.assert_has_calls(log_calls) + mock_del_vnffgd.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.list_vims', return_value=None) + def test_delete_vims_returned_list_is_none(self, mock_list_vims): + """ + Check the proper functionality of the delete_vims + function when the returned vims list is None + """ + + self.assertIsNone(cleanup.delete_vims()) + mock_list_vims.assert_called_once_with('tacker_client_obj') + + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.openstack_utils.delete_vim') + @patch('sfc.lib.openstack_utils.list_vims') + def test_delete_vims_not_empty_list(self, + mock_list_vims, + mock_del_vim, + mock_log): + """ + Check the proper functionality of the delete_vims + function when the returned vims list is not empty + """ + + mock_list_vims.return_value = ['vim_one', 'vim_two'] + log_calls = [call("Removing vim: vim_one"), + call("Removing vim: vim_two")] + + del_calls = [call('tacker_client_obj', vim_id='vim_one'), + call('tacker_client_obj', vim_id='vim_two')] + + cleanup.delete_vims() + mock_list_vims.assert_called_once_with('tacker_client_obj') + mock_log.assert_has_calls(log_calls) + mock_del_vim.assert_has_calls(del_calls) + + @patch('sfc.lib.openstack_utils.OpenStackSFC', autospec=True) + def test_delete_untracked_security_groups(self, + mock_obj): + instance = mock_obj.return_value + cleanup.delete_untracked_security_groups() + instance.delete_all_security_groups.assert_called_once() + + @patch('sfc.lib.cleanup.delete_odl_resources') + @patch('sfc.lib.cleanup.delete_odl_ietf_access_lists') + def test_cleanup_odl(self, + mock_del_odl_ietf, + mock_del_odl_res): + resources = ['service-function-forwarder'] + + odl_res_calls = [call(self.odl_ip, self.odl_port, item) + for item in resources] + + cleanup.cleanup_odl(self.odl_ip, self.odl_port) + + 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') + 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} + with patch.multiple('sfc.lib.cleanup', + **mock_dict) as mock_values: + 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.connection') + @patch('sfc.lib.cleanup.logger.info') + def test_delete_openstack_objects(self, mock_log, mock_conn): + """ + Checks the delete_chain method + """ + testcase_config = Mock() + conn = Mock() + mock_creator_obj_one = Mock() + mock_creator_obj_one.name = 'subnet_name' + mock_creator_obj_two = Mock() + mock_creator_obj_two.name = 'creator_name' + mock_creator_objs_list = [mock_creator_obj_one, mock_creator_obj_two] + + mock_conn.from_config.return_value = conn + testcase_config.subnet_name = mock_creator_obj_one.name + log_calls = [call('Deleting ' + mock_creator_obj_two.name), + call('Deleting ' + mock_creator_obj_one.name)] + + cleanup.delete_openstack_objects(testcase_config, + mock_creator_objs_list) + mock_creator_obj_one.delete.\ + assert_called_once_with(conn.session) + mock_creator_obj_two.delete.\ + assert_called_once_with(conn.session) + mock_log.assert_has_calls(log_calls) + + @patch('sfc.lib.cleanup.connection') + @patch('sfc.lib.cleanup.logger.info') + def test_delete_openstack_objects_router(self, mock_log, mock_conn): + """ + Checks the delete_chain method + """ + testcase_config = Mock() + conn = Mock() + mock_creator_obj = Mock() + mock_creator_obj.name = 'creator_name' + mock_creator_router = Mock() + mock_creator_router.name = 'router_name' + mock_creator_router.id = '1' + mock_creator_subnet = Mock() + mock_creator_subnet.name = 'subnet_name' + mock_creator_subnet.id = '2' + mock_creator_objs_list = [mock_creator_subnet, + mock_creator_router, + mock_creator_obj] + + mock_conn.from_config.return_value = conn + testcase_config.router_name = mock_creator_router.name + testcase_config.subnet_name = mock_creator_subnet.name + + conn.network.get_subnet.return_value = mock_creator_subnet + log_calls = [call('Deleting ' + mock_creator_obj.name), + call('Deleting ' + mock_creator_router.name), + call('Removing subnet from router'), + call('Deleting router'), + call('Deleting ' + mock_creator_subnet.name)] + + cleanup.delete_openstack_objects(testcase_config, + mock_creator_objs_list) + conn.network.remove_interface_from_router.\ + assert_called_once_with(mock_creator_router.id, + mock_creator_subnet.id) + conn.network.delete_router.\ + assert_called_once_with(mock_creator_router) + mock_creator_obj.delete.\ + assert_called_once_with(conn.session) + mock_creator_subnet.delete.\ + assert_called_once_with(conn.session) + mock_log.assert_has_calls(log_calls) + + @patch('sfc.lib.cleanup.connection') + @patch('sfc.lib.cleanup.logger.info') + @patch('sfc.lib.cleanup.logger.error') + def test_delete_openstack_objects_exception(self, mock_log_err, + mock_log_info, mock_conn): + """ + Check the proper functionality of the delete_openstack_objects + function when exception occurs. + """ + testcase_config = Mock() + conn = Mock() + mock_creator_obj_one = Mock() + mock_creator_obj_one.name = 'subnet_name' + mock_creator_obj_two = Mock() + mock_creator_obj_two.name = 'creator_name' + exception_one = Exception('First Boom!') + exception_two = Exception('Second Boom!') + attrs_list = [{'delete.side_effect': exception_one}, + {'delete.side_effect': exception_two}] + + mock_creator_obj_one.configure_mock(**attrs_list[0]) + mock_creator_obj_two.configure_mock(**attrs_list[1]) + + mock_creator_objs_list = [mock_creator_obj_one, mock_creator_obj_two] + mock_conn.from_config.return_value = conn + testcase_config.subnet_name = mock_creator_obj_one.name + + log_calls = [call('Deleting ' + mock_creator_obj_two.name), + call('Deleting ' + mock_creator_obj_one.name), + call('Unexpected error cleaning - %s', exception_two), + call('Unexpected error cleaning - %s', exception_one)] + + cleanup.delete_openstack_objects(testcase_config, + mock_creator_objs_list) + mock_creator_obj_one.delete.\ + assert_called_once_with(conn.session) + mock_creator_obj_two.delete.\ + assert_called_once_with(conn.session) + + mock_log_info.assert_has_calls(log_calls[:2]) + mock_log_err.assert_has_calls(log_calls[2:]) + + @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('testcase_config', ['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('testcase_config', + ['creator_one', 'creator_two']) + mock_cleanup_mano.assert_called_once_with('mano') + mock_untr_sec_grps.assert_called_once() + + @patch('sfc.lib.cleanup.cleanup_mano_objects') + @patch('sfc.lib.cleanup.cleanup_odl') + def test_cleanup_from_bash(self, + mock_cleanup_odl, + mock_cleanup_mano): + + cleanup.cleanup_from_bash(self.odl_ip, + self.odl_port, + 'mano') + + mock_cleanup_odl.assert_called_once_with(self.odl_ip, + self.odl_port) + 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 new file mode 100644 index 00000000..1dfcf1ed --- /dev/null +++ b/sfc/unit_tests/unit/lib/test_odl_utils.py @@ -0,0 +1,817 @@ +#!/usr/bin/env python + +############################################################################### +# Copyright (c) 2018 Venkata Harshavardhan Reddy Allu and others. +# +# 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 unittest + +from mock import call +from mock import Mock +from mock import patch + +import sfc.lib.odl_utils as odl_utils + +__author__ = "Harshavardhan Reddy <venkataharshavardhan_ven@srmuniv.edu.in>" + + +class SfcOdlUtilsTesting(unittest.TestCase): + + @patch('re.compile', autospec=True) + @patch('opnfv.utils.ovs_logger.OVSLogger', autospec=True) + def test_actual_rsps_in_compute(self, mock_ovs_log, mock_compile): + """ + Checks the proper functionality of actual_rsps_in_compute + function + """ + + match_calls = [call('msg_1'), call('msg_2')] + + mf = Mock() + mf.group.side_effect = ['msg_p_1', 'msg_p_2'] + mock_compile.return_value.match.side_effect = [mf, None] + mock_ovs_log.ofctl_dump_flows.return_value = '\nflow_rep\nmsg_1\nmsg_2' + + result = odl_utils.actual_rsps_in_compute(mock_ovs_log, 'compute_ssh') + + self.assertEqual(['msg_p_1|msg_p_2'], result) + mock_compile.return_value.match.assert_has_calls(match_calls) + mock_ovs_log.ofctl_dump_flows.assert_called_once_with('compute_ssh', + 'br-int', '101') + + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.get_rsp', autospec=True) + @patch('sfc.lib.odl_utils.get_odl_acl_list', autospec=True) + @patch('sfc.lib.odl_utils.get_rsps_from_netvirt_acl_actions', + autospec=True) + def test_get_active_rsps_on_ports(self, + mock_rsps_from_netvirt_acl_actions, + mock_odl_acl_list, + mock_get_rsp, + mock_log): + """ + Checks the proper functionality of get_active_rsps_on_ports + function + """ + + log_calls = [call('ACL acl_obj_one does not have an ACE')] + + port_one = Mock() + port_two = Mock() + port_one.id = 's_p' + port_two.id = 'd_p' + neutron_ports = [port_one, port_two] + + mock_rsps_from_netvirt_acl_actions.return_value = ['rsp_obj_one', + 'rsp_obj_two'] + + mock_get_rsp.side_effect = [{'of-matches': ['of-match-one'], + 'reverse-path': 'r-path-one'}, + {'of-matches': ['of-match-two']}] + + mock_odl_acl_list.return_value = {'access-lists': {'acl': [ + {'acl-name': 'acl_obj_one', + 'access-list-entries': {'ace': []}}, + {'acl-name': 'acl_obj_two', + 'access-list-entries': {'ace': [{'matches': { + 'destination-port-range': None}}]}}, + {'acl-name': 'acl_obj_three', + 'access-list-entries': {'ace': [{'matches': { + 'destination-port-range': {'lower-port': 22}, + 'netvirt-sfc-acl:source-port-uuid': 's_p_uuid', + 'netvirt-sfc-acl:destination-port-uuid': 'd_p_uuid'}}]}}, + {'acl-name': 'acl_obj_four', + 'access-list-entries': {'ace': [{'matches': { + 'destination-port-range': {'lower-port': 22}, + 'netvirt-sfc-acl:source-port-uuid': 's_p', + 'netvirt-sfc-acl:destination-port-uuid': 'd_p'}, + 'actions': 'm_actions'}]}}]}} + + expected = [{'of-matches': ['of-match-two', 'tp_dst=22']}, + {'of-matches': ['of-match-one', 'tp_src=22'], + 'reverse-path': 'r-path-one'}] + + result = odl_utils.get_active_rsps_on_ports('odl_ip', + 'odl_port', + neutron_ports) + + self.assertEqual(sorted(expected), sorted(result)) + mock_log.warn.assert_has_calls(log_calls) + mock_rsps_from_netvirt_acl_actions.assert_called_once_with('odl_ip', + 'odl_port', + 'm_actions') + + @patch('sfc.lib.odl_utils.get_odl_resource_elem', autospec=True) + def test_get_rsps_from_netvirt_acl_actions(self, mock_odl_resource_elem): + """ + Checks the proper functionality of get_rsps_from_netvirt_acl_actions + function + """ + + netv = {'netvirt-sfc-acl:rsp-name': 'rsp-name', + 'netvirt-sfc-acl:sfp-name': 'sfp-name'} + + sfp_state = {'sfp-rendered-service-path': [{'name': 'sfp-rsp-one'}, + {'name': 'sfp-rsp-two'}]} + + mock_odl_resource_elem.return_value = sfp_state + rsp_names = ['rsp-name', 'sfp-rsp-one', 'sfp-rsp-two'] + + result = odl_utils.get_rsps_from_netvirt_acl_actions('odl_ip', + 'odl_port', + netv) + self.assertEqual(rsp_names, result) + mock_odl_resource_elem.assert_called_once_with('odl_ip', 'odl_port', + 'service-function-path-' + 'state', 'sfp-name', + datastore='operational') + + @patch('sfc.lib.odl_utils.get_odl_resource_elem', + autospec=True, return_value='mocked_rsp') + def test_get_rsp(self, mock_odl_resource_elem): + """ + Checks the proper functionality of get_rsp + function + """ + + result = odl_utils.get_rsp('odl_ip', 'odl_port', 'rsp_name') + self.assertEqual('mocked_rsp', result) + mock_odl_resource_elem.assert_called_once_with('odl_ip', 'odl_port', + 'rendered-service-path', + 'rsp_name', + datastore='operational') + + @patch('sfc.lib.odl_utils.get_active_rsps_on_ports', autospec=True) + def test_promised_rsps_in_compute(self, mock_active_rsps_on_ports): + """ + Checks the proper functionality of propmised_rsps_in_compute + function + """ + + mock_active_rsps_on_ports.return_value = [ + {'of-matches': {'one': 'one'}, 'path-id': 1}, + {'of-matches': {'two': 'two'}, 'path-id': 2}] + + result = odl_utils.promised_rsps_in_compute('odl_ip', 'odl_port', + 'compute_ports') + + self.assertEqual(['0x1|one', '0x2|two'], result) + mock_active_rsps_on_ports.assert_called_once_with('odl_ip', 'odl_port', + 'compute_ports') + + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('time.time', autospec=True, side_effect=[1, 2]) + def test_timethis(self, + mock_time, + mock_log): + """ + Checks the proper functionality of timethis + function + """ + + expected = ('mock_this', '1') + log_calls = [call("mock_func(*('mock',), **{'name': 'this'}) " + "took: 1 sec")] + + @odl_utils.timethis + def mock_func(msg, name=''): + return msg+'_'+name + + result = mock_func('mock', name='this') + self.assertEqual(result, expected) + mock_log.info.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.get_odl_items', autospec=True) + @patch('sfc.lib.odl_utils.promised_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.os_sfc_utils.get_tacker_items', autospec=True) + def test_wait_for_classification_rules_rsps_not_configured( + self, mock_get_tacker_items, mock_promised_rsps_in_compute, + mock_get_odl_items, mock_find_compute, mock_log, mock_sleep): + """ + Checks the proper functionality of wait_for_classification_rules + function when rsps are not configured in ODL + """ + + log_calls = [call("Error when waiting for classification rules: " + "RSPs not configured in ODL")] + + mock_find_compute.return_value = 'mock_compute' + mock_promised_rsps_in_compute.return_value = None + + odl_utils.wait_for_classification_rules('ovs_logger', + 'compute_nodes', + 'odl_ip', + 'odl_port', + 'compute_name', + 'neutron_ports') + mock_promised_rsps_in_compute.assert_called_with('odl_ip', + 'odl_port', + 'neutron_ports') + assert mock_promised_rsps_in_compute.call_count == 10 + mock_find_compute.assert_called_once_with('compute_name', + 'compute_nodes') + mock_sleep.assert_called_with(3) + assert mock_sleep.call_count == 9 + mock_get_tacker_items.assert_called_once_with() + mock_get_odl_items.assert_called_once_with('odl_ip', 'odl_port') + mock_log.error.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.actual_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.promised_rsps_in_compute', autospec=True) + def test_wait_for_classification_rules_timeout_not_updated( + self, mock_promised_rsps_in_compute, mock_actual_rsps_in_compute, + mock_find_compute, mock_log, mock_sleep): + """ + Checks the proper functionality of wait_for_classification_rules + function when classification rules are not updated in a given timeout + """ + + log_calls = [call("Timeout but classification rules are not updated"), + call("RSPs in ODL Operational DataStore" + "for compute 'compute_name':"), + call("['compute|rsps']"), + call("RSPs in compute nodes:"), + call("[]")] + + mock_compute = Mock() + mock_compute.ssh_client = 'mock_ssh_client' + mock_find_compute.return_value = mock_compute + mock_actual_rsps_in_compute.return_value = [] + mock_promised_rsps_in_compute.return_value = ['compute|rsps'] + + odl_utils.wait_for_classification_rules('ovs_logger', + 'compute_nodes', + 'odl_ip', + 'odl_port', + 'compute_name', + 'neutron_ports', + timeout=2) + mock_find_compute.assert_called_once_with('compute_name', + 'compute_nodes') + mock_log.error.assert_has_calls(log_calls[:1]) + mock_log.info.assert_has_calls(log_calls[1:]) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.actual_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.promised_rsps_in_compute', autospec=True) + def test_wait_for_classification_rules_updated( + self, mock_promised_rsps_in_compute, mock_actual_rsps_in_compute, + mock_find_compute, mock_log, mock_sleep): + """ + Checks the proper functionality of wait_for_classification_rules + function when classification rules are not updated in a given timeout + """ + + log_calls = [call("RSPs in ODL Operational DataStore" + "for compute 'compute_name':"), + call("['compute|rsps']"), + call("RSPs in compute nodes:"), + call("['compute|rsps']"), + call("Classification rules were updated")] + mock_compute = Mock() + mock_compute.ssh_client = 'mock_ssh_client' + mock_find_compute.return_value = mock_compute + mock_actual_rsps_in_compute.return_value = ['compute|rsps'] + mock_promised_rsps_in_compute.return_value = ['compute|rsps'] + + odl_utils.wait_for_classification_rules('ovs_logger', + 'compute_nodes', + 'odl_ip', + 'odl_port', + 'compute_name', + 'neutron_ports', + timeout=2) + mock_log.info.assert_has_calls(log_calls) + + @patch('re.search', autospec=True) + @patch('ConfigParser.RawConfigParser', autospec=True) + @patch('os.getcwd', autospec=True, return_value='/etc') + @patch('os.path.join', autospec=True, return_value='/etc/ml2_conf.ini') + def test_get_odl_ip_port(self, mock_join, + mock_getcwd, + mock_rawconfigparser, + mock_search): + """ + Checks the proper functionality of get_odl_ip_port + function + """ + + cmd_calls = [call('pwd'), + call('sudo cp /etc/neutron/plugins/ml2/ml2_conf.ini ' + '/etc/'), + call('sudo chmod 777 /etc/ml2_conf.ini')] + + n1 = Mock() + n2 = Mock() + nodes = [n1, n2] + mock_rawconfigparser.return_value.get.return_value = 'config' + mock_search.return_value.group.return_value = 'odl_ip:odl_port' + n1.run_cmd.side_effect = ['/etc', '', ''] + + result = odl_utils.get_odl_ip_port(nodes) + self.assertEqual(('odl_ip', 'odl_port'), result) + n1.run_cmd.assert_has_calls(cmd_calls) + n1.is_controller.assert_called_once_with() + mock_getcwd.assert_called_once_with() + mock_join.assert_called_once_with('/etc', 'ml2_conf.ini') + n1.get_file.assert_called_once_with('/etc/ml2_conf.ini', + '/etc/ml2_conf.ini') + mock_rawconfigparser.return_value.read.assert_called_once_with( + '/etc/ml2_conf.ini') + mock_rawconfigparser.return_value.get.assert_called_with( + 'ml2_odl', 'url') + mock_search.assert_called_once_with(r'[0-9]+(?:\.[0-9]+){3}\:[0-9]+', + 'config') + + @patch('re.search', autospec=True) + @patch('ConfigParser.RawConfigParser', autospec=True) + @patch('os.getcwd', autospec=True, return_value='/etc') + @patch('os.path.join', autospec=True, return_value='/etc/ml2_conf.ini') + def test_get_odl_username_password(self, mock_join, + mock_getcwd, + mock_rawconfigparser, + mock_search): + """ + Check the proper functionality of get odl_username_password + function + """ + + mock_rawconfigparser.return_value.get.return_value = 'odl_username' + result = odl_utils.get_odl_username_password() + self.assertEqual(('odl_username'), result[0]) + mock_getcwd.assert_called_once_with() + mock_join.assert_called_once_with('/etc', 'ml2_conf.ini') + mock_rawconfigparser.return_value.read.assert_called_once_with( + '/etc/ml2_conf.ini') + mock_rawconfigparser.return_value.get.return_value = 'odl_password' + result = odl_utils.get_odl_username_password() + self.assertEqual(('odl_password'), result[1]) + + def test_pluralize(self): + """ + Checks the proper functionality of pluralize + function + """ + + result = odl_utils.pluralize('service-function-path') + self.assertEqual('service-function-paths', result) + + def test_get_module(self): + """ + Checks the proper functionality of get_module + function + """ + + result = odl_utils.get_module('service-function-path') + self.assertEqual('service-function-path', result) + + @patch('sfc.lib.odl_utils.get_module', + autospec=True, return_value='mocked_module') + @patch('sfc.lib.odl_utils.pluralize', + autospec=True, return_value='resources') + def test_format_odl_resource_list_url(self, mock_plularize, + mock_get_module): + """ + Checks the proper functionality of format_odl_resource_list_url + function + """ + + result = odl_utils.format_odl_resource_list_url('odl_ip', + 'odl_port', + 'resource') + formatted_url = ('http://admin:admin@odl_ip:' + 'odl_port/restconf/config/mocked_module:' + 'resources') + self.assertEqual(formatted_url, result) + mock_plularize.assert_called_once_with('resource') + mock_get_module.assert_called_once_with('resource') + + @patch('sfc.lib.odl_utils.format_odl_resource_list_url', + autospec=True, return_value='list_u/r/l') + def test_format_odl_resource_elem_url(self, mock_odl_resource_list_url): + """ + Checks the proper functionality of format_odl_resource_elem_url + function + """ + + result = odl_utils.format_odl_resource_elem_url('odl_ip', 'odl_port', + 'resource', + 'elem_name') + formatted_url = ('list_u/r/l/resource/elem_name') + self.assertEqual(formatted_url, result) + mock_odl_resource_list_url.assert_called_once_with('odl_ip', + 'odl_port', + 'resource', + 'config') + + @patch('sfc.lib.odl_utils.pluralize', + autospec=True, return_value='resources') + def test_odl_resource_list_names_returns_empty_list(self, mock_plularize): + """ + Checks the proper functionality of odl_resource_list_names + function when resources are empty + """ + + resource_json = {'resources': {}} + result = odl_utils.odl_resource_list_names('resource', resource_json) + self.assertEqual([], result) + + @patch('sfc.lib.odl_utils.pluralize', + autospec=True, return_value='resources') + def test_odl_resource_list_names(self, mock_plularize): + """ + Checks the proper functionality of odl_resource_list_names + function + """ + + resource_json = {'resources': {'resource': [{'name': 'resource_one'}, + {'name': 'resource_two'}]}} + result = odl_utils.odl_resource_list_names('resource', resource_json) + self.assertEqual(['resource_one', 'resource_two'], result) + + @patch('requests.get', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_resource_list_url', autospec=True) + def test_get_odl_resource_list(self, + mock_odl_resource_list_url, + mock_get): + """ + Checks the proper functionality of get_odl_resource_list + function + """ + + mock_odl_resource_list_url.return_value = 'u/r/l' + mock_get.return_value.json.return_value = {'key': 'value'} + + result = odl_utils.get_odl_resource_list('odl_ip', + 'odl_port', + 'resource') + + self.assertEqual({'key': 'value'}, result) + mock_odl_resource_list_url.assert_called_once_with('odl_ip', + 'odl_port', + 'resource', + datastore='config') + mock_get.assert_called_once_with('u/r/l') + + @patch('requests.get', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_resource_elem_url', autospec=True) + def test_get_odl_resource_elem(self, + mock_odl_resource_elem_url, + mock_get): + """ + Checks the proper functionality of get_odl_resource_elem + function + """ + + mock_response = Mock() + mock_response.get.return_value = ['elem_one', 'elem_two'] + mock_get.return_value.json.return_value = mock_response + mock_odl_resource_elem_url.return_value = 'u/r/l' + + result = odl_utils.get_odl_resource_elem( + 'odl_ip', 'odl_port', 'resource', 'elem_name') + + self.assertEqual('elem_one', result) + mock_odl_resource_elem_url.assert_called_once_with( + 'odl_ip', 'odl_port', 'resource', 'elem_name', 'config') + mock_get.assert_called_once_with('u/r/l') + mock_response.get.assert_called_once_with('resource', [{}]) + + @patch('requests.delete', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_resource_elem_url', + autospec=True, return_value='u/r/l') + def test_delete_odl_resource_elem(self, + mock_odl_resource_elem_url, + mock_delete): + """ + Checks the proper functionality of delete_odl_resource_elem + function + """ + + odl_utils.delete_odl_resource_elem('odl_ip', 'odl_port', 'resource', + 'elem_name') + + mock_odl_resource_elem_url('odl_ip', 'odl_port', 'resource', + 'elem_name', 'config') + mock_delete.assert_called_once_with('u/r/l') + + def test_odl_acl_types_names_returns_empty_list(self): + """ + Checks the proper functionality of odl_acl_types_names + function when access lists are empty + """ + + acl_json = {'access-lists': {}} + result = odl_utils.odl_acl_types_names(acl_json) + self.assertEqual([], result) + + def test_odl_acl_types_names(self): + """ + Checks the proper functionality of odl_acl_types_names + function + """ + + acl_json = {'access-lists': {'acl': [{'acl-type': 'type-one', + 'acl-name': 'name-one'}, + {'acl-type': 'type-two', + 'acl-name': 'name-two'}]}} + acl_types = [('type-one', 'name-one'), + ('type-two', 'name-two')] + + result = odl_utils.odl_acl_types_names(acl_json) + self.assertEqual(acl_types, result) + + def test_format_odl_acl_list_url(self): + """ + Checks the proper functionality of format_odl_acl_list_url + function + """ + + formatted_url = ('http://admin:admin@odl_ip:odl_port/restconf/config/' + 'ietf-access-control-list:access-lists') + result = odl_utils.format_odl_acl_list_url('odl_ip', 'odl_port') + self.assertEqual(formatted_url, result) + + @patch('json.dumps', + autospec=True, return_value='{\n "key": "value"\n}') + def test_improve_json_layout(self, mock_dumps): + """ + Checks the proper functionality of improve_json_layout + function + """ + + result = odl_utils.improve_json_layout({'key': 'value'}) + + self.assertEqual('{\n "key": "value"\n}', result) + mock_dumps.assert_called_once_with({'key': 'value'}, + indent=4, + separators=(',', ': ')) + + @patch('requests.get', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_acl_list_url', + autospec=True, return_value='acl_list_u/r/l') + @patch('sfc.lib.odl_utils.improve_json_layout', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_resource_list_url', autospec=True) + def test_get_odl_items(self, + mock_odl_resource_list_url, + mock_json_layout, + mock_odl_acl_list_url, + mock_log, + mock_get): + """ + Checks the proper functionality of get_odl_items + function + """ + + log_calls = [call('Configured ACLs in ODL: r_acl_j_s_o_n'), + call('Configured SFs in ODL: r_sf_j_s_o_n'), + call('Configured SFFs in ODL: r_sff_j_s_o_n'), + call('Configured SFCs in ODL: r_sfc_j_s_o_n'), + call('Configured RSPs in ODL: r_sp_j_s_o_n')] + + resource_list_url_calls = [call('odl_ip', 'odl_port', + 'service-function'), + call('odl_ip', 'odl_port', + 'service-function-forwarder'), + call('odl_ip', 'odl_port', + 'service-function-chain'), + call('odl_ip', 'odl_port', + 'rendered-service-path', + datastore='operational')] + + resource_list_urls = ['sf_list_u/r/l', 'sff_list_u/r/l', + 'sfc_list_u/r/l', 'rsp_list_u/r/l'] + + get_calls = [call(url) for url in resource_list_urls] + + mock_odl_resource_list_url.side_effect = resource_list_urls + + mock_get.return_value.json.side_effect = ['r_acl_json', 'r_sf_json', + 'r_sff_json', 'r_sfc_json', + 'r_rsp_json'] + + mock_json_layout.side_effect = ['r_acl_j_s_o_n', 'r_sf_j_s_o_n', + 'r_sff_j_s_o_n', 'r_sfc_j_s_o_n', + 'r_sp_j_s_o_n'] + + odl_utils.get_odl_items('odl_ip', 'odl_port') + + mock_odl_acl_list_url.assert_called_once_with('odl_ip', 'odl_port') + mock_odl_resource_list_url.assert_has_calls(resource_list_url_calls) + mock_get.assert_has_calls(get_calls, any_order=True) + mock_log.debug.assert_has_calls(log_calls) + + @patch('requests.get', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_acl_list_url', autospec=True) + def test_get_odl_acl_list(self, + mock_acl_list_url, + mock_get): + """ + Checks the proper functionality of get_odl_acl_list + function + """ + + mock_acl_list_url.return_value = 'acl_list/url' + mock_get.return_value.json.return_value = {'key': 'value'} + result = odl_utils.get_odl_acl_list('odl_ip', 'odl_port') + mock_acl_list_url.assert_called_once_with('odl_ip', 'odl_port') + mock_get.assert_called_once_with('acl_list/url') + self.assertEqual({'key': 'value'}, result) + + @patch('requests.delete', autospec=True) + @patch('sfc.lib.odl_utils.format_odl_acl_list_url', autospec=True) + def test_delete_odl_acl(self, + mock_acl_list_url, + mock_delete): + """ + Checks the proper functionality of delete_odl_acl + function + """ + + mock_acl_list_url.return_value = 'acl_list/url' + + odl_utils.delete_odl_acl('odl_ip', 'odl_port', 'acl_type', 'acl_name') + + mock_acl_list_url.assert_called_once_with('odl_ip', 'odl_port') + mock_delete.assert_called_once_with( + 'acl_list/url/acl/acl_type/acl_name') + + @patch('sfc.lib.odl_utils.delete_odl_acl', autospec=True) + def test_delete_acl(self, mock_delete_odl_acl): + """ + Checks the proper fucntionality of delete_acl + function + """ + + odl_utils.delete_acl('clf_name', 'odl_ip', 'odl_port') + mock_delete_odl_acl.assert_called_once_with( + 'odl_ip', + 'odl_port', + 'ietf-access-control-list:ipv4-acl', + 'clf_name') + + @patch('sfc.lib.odl_utils.logger', autospec=True) + def test_find_compute_raises_exception(self, mock_log): + """ + Checks the proper functionality of find_compute + function when compute was not found in the client + """ + + ErrorMSG = 'No compute, where the client is, was found' + compute_node_one = Mock() + compute_node_two = Mock() + compute_nodes = [compute_node_one, compute_node_two] + compute_node_one.name = 'compute_one' + compute_node_two.name = 'compute_two' + + with self.assertRaises(Exception) as cm: + odl_utils.find_compute('compute_client', compute_nodes) + + self.assertEqual(ErrorMSG, cm.exception.message) + mock_log.debug.assert_called_once_with(ErrorMSG) + + @patch('sfc.lib.odl_utils.logger', autospec=True) + def test_find_compute(self, mock_log): + """ + Checks the proper functionality of find_compute + function when compute was not found in the client + """ + + compute_node_one = Mock() + compute_node_two = Mock() + compute_nodes = [compute_node_one, compute_node_two] + compute_node_one.name = 'compute_one' + compute_node_two.name = 'compute_two' + + result = odl_utils.find_compute('compute_two', compute_nodes) + + self.assertEqual(compute_node_two, result) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.get_active_rsps_on_ports', autospec=True) + def test_check_vnffg_deletion_returns_false_rsps_still_active( + self, mock_active_rsps_on_ports, + mock_log, mock_sleep): + """ + Checks the proper functionality of check_vnffg_deletion + function to verify that it returns false on the given condition + """ + + log_calls = [call('RSPs are still active in the MD-SAL')] + mock_active_rsps_on_ports.return_value = True + result = odl_utils.check_vnffg_deletion('odl_ip', 'odl_port', + 'ovs_logger', 'neutron_ports', + 'compute_client_name', + 'compute_nodes', retries=1) + self.assertFalse(result) + mock_active_rsps_on_ports.assert_called_once_with('odl_ip', 'odl_port', + 'neutron_ports') + mock_sleep.assert_called_once_with(3) + mock_log.debug.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.actual_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.get_active_rsps_on_ports', autospec=True) + def test_check_vnffg_deletion_returns_false_error_getting_compute( + self, mock_active_rsps_on_ports, mock_actual_rsps, + mock_find_compute, mock_log, mock_sleep): + """ + Checks the proper functionality of check_vnffg_deletion + function to verify that it returns false on the given condition + """ + + log_calls = [call('There was an error getting the compute: ErrorMSG')] + mock_compute = Mock() + mock_compute.ssh_client = 'mock_ssh_client' + mock_find_compute.side_effect = [Exception('ErrorMSG'), mock_compute] + mock_active_rsps_on_ports.side_effect = [True, False] + result = odl_utils.check_vnffg_deletion('odl_ip', 'odl_port', + 'ovs_logger', 'neutron_ports', + 'compute_client_name', + 'compute_nodes', retries=2) + self.assertFalse(result) + mock_sleep.assert_called_once_with(3) + mock_find_compute.assert_called_once_with('compute_client_name', + 'compute_nodes') + mock_log.debug.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.actual_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.get_active_rsps_on_ports', autospec=True) + def test_check_vnffg_deletion_returns_false_classification_flow_in_compute( + self, mock_active_rsps_on_ports, mock_actual_rsps, + mock_find_compute, mock_log, mock_sleep): + """ + Checks the proper functionality of check_vnffg_deletion + function to verify that it returns false on the given condition + """ + + log_calls = [call('Classification flows still in the compute')] + mock_compute = Mock() + mock_compute.ssh_client = 'mock_ssh_client' + mock_find_compute.return_value = mock_compute + mock_actual_rsps.side_effect = [True, True] + mock_active_rsps_on_ports.side_effect = [True, False] + result = odl_utils.check_vnffg_deletion('odl_ip', 'odl_port', + 'ovs_logger', 'neutron_ports', + 'compute_client_name', + 'compute_nodes', retries=2) + self.assertFalse(result) + mock_actual_rsps.assert_called_with('ovs_logger', 'mock_ssh_client') + mock_sleep.assert_called_with(3) + mock_find_compute.assert_called_once_with('compute_client_name', + 'compute_nodes') + assert mock_sleep.call_count == 3 + mock_log.debug.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.odl_utils.logger', autospec=True) + @patch('sfc.lib.odl_utils.find_compute', autospec=True) + @patch('sfc.lib.odl_utils.actual_rsps_in_compute', autospec=True) + @patch('sfc.lib.odl_utils.get_active_rsps_on_ports', autospec=True) + def test_check_vnffg_deletion_returns_true(self, + mock_active_rsps_on_ports, + mock_actual_rsps, + mock_find_compute, + mock_log, mock_sleep): + """ + Checks the proper functionality of check_vnffg_deletion + function to verify that it returns true + """ + + mock_compute = Mock() + mock_compute.ssh_client = 'mock_ssh_client' + mock_active_rsps_on_ports.side_effect = [True, False] + + mock_actual_rsps.side_effect = [True, False] + + mock_find_compute.return_value = mock_compute + + result = odl_utils.check_vnffg_deletion('odl_ip', 'odl_port', + 'ovs_logger', 'neutron_ports', + 'compute_client_name', + 'compute_nodes', retries=2) + self.assertTrue(result) + mock_find_compute.assert_called_once_with('compute_client_name', + 'compute_nodes') + assert mock_sleep.call_count == 2 + mock_log.assert_not_called() diff --git a/sfc/unit_tests/unit/lib/test_openstack_utils.py b/sfc/unit_tests/unit/lib/test_openstack_utils.py new file mode 100644 index 00000000..bdd53d36 --- /dev/null +++ b/sfc/unit_tests/unit/lib/test_openstack_utils.py @@ -0,0 +1,2504 @@ +#!/usr/bin/env python + +############################################################################### +# Copyright (c) 2018 Venkata Harshavardhan Reddy Allu and others. +# +# 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 unittest + +from mock import call +from mock import Mock +from mock import patch +from mock import mock_open +from mock import DEFAULT +from mock import MagicMock + +import sfc.lib.openstack_utils as os_sfc_utils +from tackerclient.v1_0 import client as tacker_client + +__author__ = "Harshavardhan Reddy <venkataharshavardhan_ven@srmuniv.edu.in>" + + +class SfcOpenStackUtilsTesting(unittest.TestCase): + + def setUp(self): + self.patcher1 = patch.object(os_sfc_utils.constants, + 'ENV_FILE', autospec=True) + self.patcher2 = patch.object(os_sfc_utils.openstack_tests, + 'get_credentials', autospec=True) + self.patcher3 = patch.object(os_sfc_utils.nova_utils, + 'nova_client', autospec=True) + self.patcher4 = patch.object(os_sfc_utils.neutron_utils, + 'neutron_client', autospec=True) + self.patcher5 = patch.object(os_sfc_utils.heat_utils, + 'heat_client', autospec=True) + self.patcher6 = patch.object(os_sfc_utils.keystone_utils, + 'keystone_client', autospec=True) + self.patcher7 = patch.object(os_sfc_utils.connection, + 'from_config', autospec=True,) + self.patcher8 = patch.object(os_sfc_utils.neutronclient, + 'Client', autospec=True,) + + self.env_file = self.patcher1.start().return_value + self.os_creds = self.patcher2.start().return_value + self.nova = self.patcher3.start().return_value + self.neutron = self.patcher4.start().return_value + self.heat = self.patcher5.start().return_value + self.keystone = self.patcher6.start().return_value + self.conn = self.patcher7.start().return_value + self.neutron_client = self.patcher8.start().return_value + + self.os_sfc = os_sfc_utils.OpenStackSFC() + + def tearDown(self): + self.patcher1.stop() + self.patcher2.stop() + self.patcher3.stop() + self.patcher4.stop() + self.patcher5.stop() + self.patcher6.stop() + self.patcher7.stop() + self.patcher8.stop() + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('os.environ', {'OS_NETWORK_API_VERSION': '1'}) + def test_get_neutron_client_version(self, + mock_log): + """ + Checks the proper functionality of get_neutron_client_version + """ + log_calls = [call("OS_NETWORK_API_VERSION is 1")] + result = self.os_sfc.get_neutron_client_version() + assert result == '1' + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_register_glance_image_already_exists(self, + mock_log): + """ + Checks the proper functionality of register_glance_image + function when the image is local + """ + image_obj = Mock() + image_obj.name = 'name' + log_calls = [call('Registering the image...'), + call('Image ' + image_obj.name + ' already exists.')] + + self.conn.image.find_image.return_value = image_obj + result = self.os_sfc.register_glance_image('name', + 'url', + 'img_format', + 'public') + + self.conn.image.find_image.assert_called_once_with(image_obj.name) + + assert result is image_obj + + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch("__builtin__.open", autospec=True) + def test_register_glance_image_is_local(self, + mock_open_fn, + mock_log): + """ + Checks the proper functionality of register_glance_image + function when the image is local + """ + log_calls = [call('Registering the image...'), + call('Image created')] + + image_obj_None = None + image_obj_name = 'name' + image_obj = Mock() + mocked_file = mock_open(read_data='url').return_value + mock_open_fn.return_value = mocked_file + + self.conn.image.find_image.return_value = image_obj_None + self.conn.image.upload_image.return_value = image_obj + result = self.os_sfc.register_glance_image('name', + 'url', + 'img_format', + 'public') + assert result is image_obj + + self.conn.image.find_image.assert_called_once_with(image_obj_name) + + self.conn.image.upload_image.\ + assert_called_once_with(name='name', + disk_format='img_format', + data='url', + is_public='public', + container_format='bare') + + self.assertEqual([image_obj], self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.urllib2.urlopen', autospec=True) + def test_register_glance_image_is_not_local(self, + mock_urlopen, + mock_log): + """ + Checks the proper functionality of register_glance_image + function when the image is not local + """ + log_calls = [call('Registering the image...'), + call('Downloading image'), + call('Image created')] + + image_obj_None = None + image_obj_name = 'name' + image_obj = Mock() + mock_file = Mock() + mock_file.read.side_effect = ['http://url'] + mock_urlopen.return_value = mock_file + + self.conn.image.find_image.return_value = image_obj_None + self.conn.image.upload_image.return_value = image_obj + + result = self.os_sfc.register_glance_image('name', + 'http://url', + 'img_format', + 'public') + + assert result is image_obj + + self.conn.image.find_image.assert_called_once_with(image_obj_name) + + self.conn.image.upload_image.\ + assert_called_once_with(name='name', + disk_format='img_format', + data='http://url', + is_public='public', + container_format='bare') + + self.assertEqual([image_obj], self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_flavour(self, + mock_log): + """ + Checks the proper functionality of create_flavor + function + """ + + mock_openstack_flavor_ins = self.conn.compute.\ + create_flavor.return_value + log_calls = [call('Creating flavor...')] + + result = self.os_sfc.create_flavor('name', + 'ram', + 'disk', + 'vcpus') + assert result is mock_openstack_flavor_ins + self.assertEqual([mock_openstack_flavor_ins], + self.os_sfc.creators) + self.conn.compute.create_flavor.\ + assert_called_once_with(name='name', + ram='ram', + disk='disk', + vcpus='vcpus') + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.env.get', autospec=True) + def test_create_network_infrastructure(self, mock_env_get, mock_log): + log_calls = [call('Creating Networks...'), + call('Creating Router...')] + network_obj = Mock() + network_obj.id = '1' + subnet_obj = Mock() + subnet_obj.id = '2' + ext_network_obj = Mock() + ext_network_obj.id = '3' + router_obj = Mock() + router_obj.id = '4' + + self.conn.network.create_network.return_value = network_obj + self.conn.network.create_subnet.return_value = subnet_obj + self.conn.network.find_network.return_value = ext_network_obj + self.conn.network.create_router.return_value = router_obj + self.conn.network.get_router.return_value = router_obj + mock_env_get.return_value = 'ext_net_name' + + expected = (network_obj, router_obj) + result = self.os_sfc.create_network_infrastructure('net_name', + 'sn_name', + 'subnet_cidr', + 'router_name') + self.conn.network.create_network.\ + assert_called_once_with(name='net_name') + self.conn.network.create_subnet.\ + assert_called_once_with(name='sn_name', cidr='subnet_cidr', + network_id=network_obj.id, ip_version='4') + self.conn.network.find_network.\ + assert_called_once_with('ext_net_name') + self.conn.network.create_router.\ + assert_called_once_with(name='router_name') + self.conn.network.add_interface_to_router.\ + assert_called_once_with(router_obj.id, subnet_id=subnet_obj.id) + self.conn.network.update_router.\ + assert_called_once_with( + router_obj.id, + external_gateway_info={'network_id': ext_network_obj.id}) + self.conn.network.get_router.assert_called_once_with(router_obj.id) + + self.assertEqual(expected, result) + self.assertEqual([network_obj, subnet_obj, router_obj], + self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_security_group(self, + mock_log): + """ + Checks the proper functionality of create_security_group + function + """ + log_calls = [call('Creating the security groups...')] + sec_group_obj = Mock() + sec_group_obj.id = '1' + + self.conn.network.create_security_group.return_value = sec_group_obj + + result = self.os_sfc.create_security_group('sec_grp_name') + assert result is sec_group_obj + + self.conn.network.create_security_group.\ + assert_called_once_with(name='sec_grp_name') + + pc_calls = [call(security_group_id=sec_group_obj.id, + direction='ingress', + protocol='icmp'), + call(security_group_id=sec_group_obj.id, + direction='ingress', + protocol='tcp', + port_range_min=22, + port_range_max=22), + call(security_group_id=sec_group_obj.id, + direction='ingress', + protocol='tcp', + port_range_min=80, + port_range_max=80)] + + self.conn.network.create_security_group_rule.\ + assert_has_calls(pc_calls) + + self.assertEqual([sec_group_obj], self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_instance_port_security_false(self, + mock_log): + """ + Checks the proper functionality of create_instance + function + """ + + keypair_obj = Mock() + keypair_obj.name = 'keypair_name' + flavor_obj = Mock() + flavor_obj.id = '1' + port_obj1 = Mock() + port_obj1.id = '2' + port_obj2 = Mock() + port_obj2.id = '3' + instance_obj = Mock() + instance_obj.name = 'instance_name' + secgrp = Mock() + secgrp.name = 'sec_grp' + secgrp.id = '4' + img_cre = Mock() + img_cre.id = '5' + network = Mock() + network.id = '6' + ports = ['port1', 'port2'] + port_security = False + + log_calls = [call('Creating Key Pair vm_name...'), + call('Creating Port ' + str(ports) + '...'), + call('Creating the instance vm_name...'), + call('Waiting for instance_name to become Active'), + call('instance_name is active')] + + self.conn.compute.create_keypair.return_value = keypair_obj + self.conn.compute.find_flavor.return_value = flavor_obj + self.conn.network.create_port.side_effect = [port_obj1, port_obj2] + self.conn.compute.create_server.return_value = instance_obj + + port_obj_list = [port_obj1, port_obj2] + + expected = (instance_obj, port_obj_list) + result = self.os_sfc.create_instance('vm_name', + 'flavor_name', + img_cre, + network, + secgrp, + 'av_zone', + ports, + port_security=port_security) + self.assertEqual(expected, result) + + pc_calls = [call(name=ports[0], + is_port_security_enabled=port_security, + network_id=network.id), + call(name=ports[1], + is_port_security_enabled=port_security, + network_id=network.id)] + + self.conn.compute.create_keypair.\ + assert_called_once_with(name='vm_name' + "_keypair") + + self.conn.compute.find_flavor.assert_called_once_with('flavor_name') + + self.conn.network.create_port.\ + assert_has_calls(pc_calls) + + self.conn.compute.create_server.\ + assert_called_once_with(name='vm_name', + image_id=img_cre.id, + flavor_id=flavor_obj.id, + networks=[{"port": port_obj1.id}, + {"port": port_obj2.id}], + key_name=keypair_obj.name, + availability_zone='av_zone') + + self.conn.compute.wait_for_server.\ + assert_called_once_with(instance_obj) + + self.assertEqual([keypair_obj, port_obj1, port_obj2, instance_obj], + self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_instance(self, + mock_log): + """ + Checks the proper functionality of create_instance + function + """ + + keypair_obj = Mock() + keypair_obj.name = 'keypair_name' + flavor_obj = Mock() + flavor_obj.id = '1' + port_obj = Mock() + port_obj.id = '2' + instance_obj = Mock() + instance_obj.name = 'instance_name' + secgrp = Mock() + secgrp.name = 'sec_grp' + secgrp.id = '4' + img_cre = Mock() + img_cre.id = '5' + network = Mock() + network.id = '6' + ports = ['port1'] + port_obj_list = [port_obj] + port_security = True + + log_calls = [call('Creating Key Pair vm_name...'), + call('Creating Port ' + str(ports) + '...'), + call('Creating the instance vm_name...'), + call('Waiting for instance_name to become Active'), + call('instance_name is active')] + + self.conn.compute.create_keypair.return_value = keypair_obj + self.conn.compute.find_flavor.return_value = flavor_obj + self.conn.network.create_port.return_value = port_obj + self.conn.compute.create_server.return_value = instance_obj + # self.conn.compute.wait_for_server.return_value = wait_ins_obj + + expected = (instance_obj, port_obj_list) + result = self.os_sfc.create_instance('vm_name', + 'flavor_name', + img_cre, + network, + secgrp, + 'av_zone', + ports, + port_security=port_security) + self.assertEqual(expected, result) + + pc_calls = [call(name=ports[0], + is_port_security_enabled=port_security, + network_id=network.id, + security_group_ids=[secgrp.id])] + + self.conn.compute.create_keypair.\ + assert_called_once_with(name='vm_name' + "_keypair") + + self.conn.compute.find_flavor.assert_called_once_with('flavor_name') + + self.conn.network.create_port.\ + assert_has_calls(pc_calls) + + self.conn.compute.create_server.\ + assert_called_once_with(name='vm_name', + image_id=img_cre.id, + flavor_id=flavor_obj.id, + networks=[{"port": port_obj.id}], + key_name=keypair_obj.name, + availability_zone='av_zone') + + self.conn.compute.wait_for_server.\ + assert_called_once_with(instance_obj) + + self.assertEqual([keypair_obj, port_obj, instance_obj], + self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_instance_port_security_false_one_port(self, + mock_log): + """ + Checks the proper functionality of create_instance + function + """ + + keypair_obj = Mock() + keypair_obj.name = 'keypair_name' + flavor_obj = Mock() + flavor_obj.id = '1' + port_obj = Mock() + port_obj.id = '2' + instance_obj = Mock() + instance_obj.name = 'instance_name' + secgrp = Mock() + secgrp.name = 'sec_grp' + secgrp.id = '4' + img_cre = Mock() + img_cre.id = '5' + network = Mock() + network.id = '6' + ports = ['port1'] + port_obj_list = [port_obj] + port_security = False + + log_calls = [call('Creating Key Pair vm_name...'), + call('Creating Port ' + str(ports) + '...'), + call('Creating the instance vm_name...'), + call('Waiting for instance_name to become Active'), + call('instance_name is active')] + + self.conn.compute.create_keypair.return_value = keypair_obj + self.conn.compute.find_flavor.return_value = flavor_obj + self.conn.network.create_port.return_value = port_obj + self.conn.compute.create_server.return_value = instance_obj + + expected = (instance_obj, port_obj_list) + result = self.os_sfc.create_instance('vm_name', + 'flavor_name', + img_cre, + network, + secgrp, + 'av_zone', + ports, + port_security=port_security) + self.assertEqual(expected, result) + + pc_calls = [call(name=ports[0], + is_port_security_enabled=port_security, + network_id=network.id)] + + self.conn.compute.create_keypair.\ + assert_called_once_with(name='vm_name' + "_keypair") + + self.conn.compute.find_flavor.assert_called_once_with('flavor_name') + + self.conn.network.create_port.\ + assert_has_calls(pc_calls) + + self.conn.compute.create_server.\ + assert_called_once_with(name='vm_name', + image_id=img_cre.id, + flavor_id=flavor_obj.id, + networks=[{"port": port_obj.id}], + key_name=keypair_obj.name, + availability_zone='av_zone') + + self.conn.compute.wait_for_server.\ + assert_called_once_with(instance_obj) + + self.assertEqual([keypair_obj, port_obj, instance_obj], + self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + def test_get_instance(self): + """ + Checks the proper functionality of get_instance function + """ + + mock_instance_id = 'instance-abyz' + mock_instance = Mock() + mock_instance.id = mock_instance_id + mock_instance.name = 'test-instance' + mock_instance.hypervisor_hostname = 'nova-abyz' + self.conn.compute.get_server_metadata.return_value = mock_instance + result = self.os_sfc.get_instance(mock_instance_id) + self.assertEqual(result, mock_instance) + + @patch.object(os_sfc_utils.OpenStackSFC, 'get_hypervisor_hosts') + def test_get_av_zones(self, mock_hosts): + """ + Checks the proper functionality of get_av_zone + function + """ + mock_hosts.return_value = ['host1', 'host2'] + result = self.os_sfc.get_av_zones() + mock_hosts.assert_called_once() + self.assertEqual(['nova::host1', 'nova::host2'], result) + + def test_get_hypervisor_hosts(self): + """ + Checks the proper functionality of get_av_zone + function + """ + from openstack.compute.v2 import hypervisor + + hypervisor1 = Mock() + hypervisor1.state = 'up' + hypervisor1.name = 'compute00' + hypervisor2 = Mock() + hypervisor2.state = 'up' + hypervisor2.name = 'compute01' + nodes = [hypervisor1.name, hypervisor2.name] + hypervisors_list = MagicMock() + mock_obj = patch.object(hypervisor, 'Hypervisor') + mock_obj.side_effect = [hypervisor1, hypervisor2] + self.conn.compute.hypervisors.return_value = hypervisors_list + hypervisors_list.__iter__.return_value = [hypervisor1, hypervisor2] + + result = self.os_sfc.get_hypervisor_hosts() + self.conn.compute.hypervisors.assert_called_once() + self.assertEqual(nodes, result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_hypervisor_hosts_exception(self, mock_log): + """ + Checks the proper functionality of get_av_zone + function when an exception appears + """ + log_calls = [call('Error [get_hypervisors(compute)]: Error MSG')] + self.conn.compute.hypervisors.side_effect = Exception('Error MSG') + result = self.os_sfc.get_hypervisor_hosts() + mock_log.error.assert_has_calls(log_calls) + self.assertIsNone(result) + + @patch('sfc.lib.openstack_utils.OpenStackSFC.get_vm_compute', + autospec=True, return_value='mock_client') + def test_compute_client(self, mock_get_vm_compute): + """ + Checks the proper functionality of get_compute_client + function + """ + + result = self.os_sfc.get_compute_client() + self.assertEqual('mock_client', result) + mock_get_vm_compute.assert_called_once_with(self.os_sfc, 'client') + + @patch('sfc.lib.openstack_utils.OpenStackSFC.get_vm_compute', + autospec=True, return_value='mock_server') + def test_get_compute_server(self, mock_get_vm_compute): + """ + Checks the proper functionality of get_compute_server + function + """ + + result = self.os_sfc.get_compute_server() + self.assertEqual('mock_server', result) + mock_get_vm_compute.assert_called_once_with(self.os_sfc, 'server') + + def test_get_vm_compute_raised_exception(self): + """ + Checks the proper functionality of get_vm_compute + function when no VM with the given name is found + """ + + ErrorMSG = "There is no VM with name 'mock_vm_name'!!" + with self.assertRaises(Exception) as cm: + self.os_sfc.get_vm_compute('mock_vm_name') + + self.assertEqual(cm.exception.message, ErrorMSG) + + def test_get_vm_compute(self): + """ + Checks the proper functionality of get_vm_compute + function + """ + + mock_cre_obj_1 = Mock() + mock_cre_obj_2 = Mock() + mock_cre_obj_1.get_vm_inst.return_value.name = 'pro_vm' + mock_cre_obj_2.get_vm_inst.return_value.name = 'dev_vm' + mock_cre_obj_2.get_vm_inst.return_value.compute_host = 'mock_host' + self.os_sfc.creators = [mock_cre_obj_1, mock_cre_obj_2] + + result = self.os_sfc.get_vm_compute('dev_vm') + self.assertEqual('mock_host', result) + + def test_get_port_by_ip(self): + """ + Checks the proper functonality of get_port_by_ip function + """ + + mock_port_ip_address = 'e.f.g.h' + mock_port_one, mock_port_two = Mock(), Mock() + mock_port_one.id = 'port-abcd' + mock_port_two.id = 'port-efgz' + mock_port_one.fixed_ips = [{'ip_address': 'a.b.c.d'}] + mock_port_two.fixed_ips = [{'ip_address': 'e.f.g.h'}] + self.conn.network.ports.return_value = [mock_port_one, mock_port_two] + self.conn.network.get_port.return_value = mock_port_two + result = self.os_sfc.get_port_by_ip(mock_port_ip_address) + self.assertEqual(result, mock_port_two) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.cr_inst.OpenStackVmInstance', + autospec=True) + 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 + """ + + mock_os_vm_ins = mock_os_vm.return_value + mock_vm = Mock() + mock_vm.name = 'mock_vm_name' + mock_os_vm_ins.get_port_by_name.return_value = None + ErrorMSG = 'Client VM does not have the desired port' + log_calls = [call("The VM mock_vm_name does not have any port" + " with name mock_vm_name-port")] + + with self.assertRaises(Exception) as cm: + 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) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.cr_inst.OpenStackVmInstance', + autospec=True) + 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 + """ + + mock_os_vm_ins = mock_os_vm.return_value + 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_instance_port(mock_vm, mock_os_vm_ins) + self.assertEqual('mock_port', result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.env.get', autospec=True) + def test_assign_floating_ip(self, + mock_env_get, + mock_log): + """ + Checks the proper functionality of assigning_floating_ip + function + """ + ext_network_obj = Mock() + ext_network_obj.id = '1' + fip_obj = Mock() + fip_obj.floating_ip_address = 'floating_ip_address' + port_obj = Mock() + port_obj.id = '2' + instance_obj = Mock() + instance_obj.id = '3' + + log_calls = [call(' Creating floating ips '), + call(' FLoating IP address ' + + fip_obj.floating_ip_address + + ' created'), + call(' Adding Floating IPs to instances ')] + + mock_env_get.return_value = 'ext_net_name' + self.conn.network.find_network.return_value = ext_network_obj + self.conn.network.create_ip.return_value = fip_obj + self.conn.netwotk.get_port.return_value = port_obj + self.conn.compute.get_server.return_value = instance_obj + + result = self.os_sfc.assign_floating_ip(instance_obj, port_obj) + assert result is fip_obj.floating_ip_address + + self.conn.network.find_network.assert_called_once_with('ext_net_name') + self.conn.network.create_ip.\ + assert_called_once_with(floating_network_id=ext_network_obj.id, + port_id=port_obj.id) + self.conn.compute.add_floating_ip_to_server.\ + assert_called_once_with(instance_obj.id, + fip_obj.floating_ip_address) + + self.assertEqual([fip_obj], + self.os_sfc.creators) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.heat_utils.get_stack_servers', + autospec=True) + @patch('sfc.lib.openstack_utils.cr_inst.generate_creator', autospec=True) + def test_assign_floating_ip_vnfs_raised_exception_ips_provided( + self, mock_generate_creator, mock_get_stack_servers, mock_log): + """ + Checks the proper functionality of assign_floating_ip_vnfs + function when server name does not have any floating IP assignment + """ + + ErrorMSG = "The VNF server_name-float does not have any suitable" + \ + " port with ip any of ['floating_ip', 'other_ip'] for" + \ + " floating IP assignment" + log_calls = [call(ErrorMSG)] + self.os_sfc.image_settings = 'image_settings' + self.heat.stacks.list.return_value = ['stack_obj'] + mock_ips = ['floating_ip', 'other_ip'] + mock_server_obj = Mock() + mock_port_obj = Mock() + mock_server_obj.name = 'server_name' + mock_server_obj.ports = [mock_port_obj] + mock_port_obj.name = None + mock_port_obj.ips = [{'ip_address': 'floating_ip'}] + mock_get_stack_servers.return_value = [mock_server_obj] + + with self.assertRaises(Exception) as cm: + self.os_sfc.assign_floating_ip_vnfs('router', mock_ips) + + self.assertEqual(cm.exception.message, ErrorMSG) + mock_get_stack_servers.assert_called_once_with(self.heat, + self.nova, + self.neutron_client, + self.keystone, + 'stack_obj', + 'admin') + mock_generate_creator.assert_called_once_with(self.os_creds, + mock_server_obj, + 'image_settings', + 'admin') + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.heat_utils.get_stack_servers', + autospec=True) + @patch('sfc.lib.openstack_utils.cr_inst.generate_creator', autospec=True) + def test_assign_floating_ip_vnfs_raised_exception_ips_not_provided( + self, mock_generate_creator, mock_get_stack_servers, mock_log): + """ + Checks the proper functionality of assign_floating_ip_vnfs + function when server name does not have any floating IP assignment + """ + + ErrorMSG = "The VNF server_name-float does not have any suitable" + \ + " port for floating IP assignment" + log_calls = [call(ErrorMSG)] + self.os_sfc.image_settings = 'image_settings' + self.heat.stacks.list.return_value = ['stack_obj'] + mock_server_obj = Mock() + mock_port_obj = Mock() + mock_server_obj.name = 'server_name' + mock_server_obj.ports = [mock_port_obj] + mock_port_obj.name = None + mock_port_obj.ips = [{'ip_address': 'floating_ip'}] + mock_get_stack_servers.return_value = [mock_server_obj] + + with self.assertRaises(Exception) as cm: + self.os_sfc.assign_floating_ip_vnfs('router') + + mock_get_stack_servers.assert_called_once_with(self.heat, + self.nova, + self.neutron_client, + self.keystone, + 'stack_obj', + 'admin') + mock_generate_creator.assert_called_once_with(self.os_creds, + mock_server_obj, + 'image_settings', + 'admin') + self.assertEqual(cm.exception.message, ErrorMSG) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.FloatingIpConfig', autospec=True) + @patch('sfc.lib.openstack_utils.cr_inst.generate_creator', + autospec=True) + @patch('sfc.lib.openstack_utils.heat_utils.get_stack_servers', + autospec=True) + def test_assign_floating_ip_vnfs(self, + mock_get_stack_servers, + mock_generate_creator, + mock_floating_ip_config): + """ + Checks the proper functionality of assign_floating_ip_vnfs + function + """ + + self.os_sfc.image_settings = 'image_settings' + self.heat.stacks.list.return_value = ['stack_obj'] + + mock_router = Mock() + mock_server_obj = Mock() + mock_ip_obj = Mock() + mock_port_obj = Mock() + mock_router.name = 'm_router' + mock_server_obj.name = 'serv_obj' + mock_server_obj.ports = [mock_port_obj] + mock_ips = ['floating_ip', 'other_ip'] + mock_ip_obj.ip = 'mocked_ip' + mock_port_obj.name = 'port_obj' + mock_port_obj.ips = [{'ip_address': 'floating_ip'}] + mock_get_stack_servers.return_value = [mock_server_obj] + mock_os_vm_ins = mock_generate_creator.return_value + float_ip_ins = mock_floating_ip_config.return_value + mock_os_vm_ins.add_floating_ip.return_value = mock_ip_obj + + result = self.os_sfc.assign_floating_ip_vnfs(mock_router, mock_ips) + self.assertEqual(['mocked_ip'], result) + self.assertEqual([mock_os_vm_ins], self.os_sfc.creators) + mock_get_stack_servers.assert_called_once_with(self.heat, + self.nova, + self.neutron_client, + self.keystone, + 'stack_obj', 'admin') + mock_generate_creator.assert_called_once_with(self.os_creds, + mock_server_obj, + 'image_settings', + 'admin') + mock_floating_ip_config.assert_called_once_with(name='serv_obj-float', + port_name='port_obj', + router_name='m_router') + mock_os_vm_ins.add_floating_ip.assert_called_once_with(float_ip_ins) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_all_security_groups(self, mock_log): + """ + Checks the proper functionality of delete_all_security_groups + function + """ + + log_calls_info = [call('Deleting remaining security groups...')] + secgrp1_obj = Mock() + secgrp2_obj = Mock() + secgrp_list = MagicMock() + + self.conn.network.create_security_groups.side_effect = [secgrp1_obj, + secgrp2_obj] + self.conn.network.security_groups.return_value = secgrp_list + + secgrp_list.__iter__.return_value = [secgrp1_obj, secgrp2_obj] + del_calls = [call(secgrp1_obj), + call(secgrp2_obj)] + + self.os_sfc.delete_all_security_groups() + self.conn.network.security_groups.assert_called_once() + self.conn.network.delete_security_group.assert_has_calls(del_calls) + mock_log.info.assert_has_calls(log_calls_info) + + @patch('sfc.lib.openstack_utils.cr_inst.OpenStackVmInstance', + autospec=True) + def test_wait_for_vnf(self, mock_os_vm): + """ + Checks the proper functionality of wait_for_vnf function + """ + + mock_os_vm.vm_active.return_value = "x" + result = self.os_sfc.wait_for_vnf(mock_os_vm) + self.assertEqual('x', result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_raises_exception(self, mock_log): + """ + Checks the create_port_groups when length of ports is greater than 2 + """ + instance_obj = Mock() + instance_obj.name = 'name' + self.conn.compute.get_server.return_value = instance_obj + + 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, instance_obj) + 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('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_returns_none_from_pp(self, mock_log): + """ + Checks the create_port_groups when something goes wrong in port pair + creation + """ + instance_obj = Mock() + instance_obj.name = 'name' + port_obj1 = Mock() + port_obj2 = Mock() + port_obj1.id = '123abc' + port_obj2.id = '456def' + + self.conn.compute.get_server.return_value = instance_obj + self.conn.network.get_port.return_value = port_obj1 + self.conn.network.get_port.return_value = port_obj2 + + 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': instance_obj.name})] + self.neutron_client.create_sfc_port_pair.return_value = None + result = self.os_sfc.create_port_groups( + [port_obj1, port_obj2], instance_obj) + 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_exception_nopp(self, mock_log, mock_osvm, + mock_port): + """ + Checks the create_port_groups when openstack does not commit the pp + """ + + log_calls_info = [call('Creating the port pairs...')] + mock_port_ins = mock_port.return_value + mock_port_ins.id = '123abc' + mock_vm_ins = mock_osvm.return_value + mock_vm_ins.name = 'vm' + exception_message = "Port pair was not committed in openstack" + expected_port_pair = {'name': 'vm-connection-points', + 'description': 'port pair for vm', + 'ingress': '123abc', + 'egress': '123abc'} + self.neutron_client.create_sfc_port_pair.return_value = \ + {'port_pair': {'id': 'pp_id'}} + self.neutron_client.list_sfc_port_pairs.return_value = \ + {'port_pairs': [{'id': 'xxxx'}]} + with self.assertRaises(Exception) as cm: + self.os_sfc.create_port_groups([mock_port_ins], mock_vm_ins) + self.assertEqual(exception_message, cm.exception.message) + self.neutron_client.create_sfc_port_pair.assert_has_calls( + [call({'port_pair': expected_port_pair})]) + mock_log.info.assert_has_calls(log_calls_info) + + @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 + """ + + instance_obj = Mock() + instance_obj.name = 'name' + port_obj = Mock() + port_obj.id = '123abc' + + self.conn.compute.get_server.return_value = instance_obj + self.conn.network.get_port.return_value = port_obj + + log_calls_info = [call('Creating the port pairs...'), + call('Creating the port pair groups for name')] + log_calls_warn = [call('Chain creation failed due to port pair group ' + 'creation failed for vnf ' + '{}'.format(instance_obj.name))] + self.neutron_client.create_sfc_port_pair.return_value = \ + {'port_pair': {'id': 'pp_id'}} + self.neutron_client.list_sfc_port_pairs.return_value = \ + {'port_pairs': [{'id': 'pp_id'}]} + self.neutron_client.create_sfc_port_pair_group.return_value = None + result = self.os_sfc.create_port_groups([port_obj], instance_obj) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls_info) + mock_log.warning.assert_has_calls(log_calls_warn) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_port_groups_returns_id(self, mock_log): + """ + Checks the create_port_groups when everything goes as expected + """ + + log_calls_info = [call('Creating the port pairs...')] + + instance_obj = Mock() + instance_obj.name = 'vm' + port_obj = Mock() + port_obj.id = '123abc' + self.conn.compute.get_server.return_value = instance_obj + self.conn.network.get_port.return_value = port_obj + + expected_port_pair = {'name': 'vm-connection-points', + 'description': 'port pair for vm', + 'ingress': '123abc', + 'egress': '123abc'} + self.neutron_client.create_sfc_port_pair.return_value = \ + {'port_pair': {'id': 'pp_id'}} + self.neutron_client.list_sfc_port_pairs.return_value = \ + {'port_pairs': [{'id': 'pp_id'}]} + self.neutron_client.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([port_obj], instance_obj) + self.neutron_client.create_sfc_port_pair.assert_has_calls( + [call({'port_pair': expected_port_pair})]) + self.neutron_client.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_classifier(self, mock_log): + """ + Checks the create_classifier method + """ + + log_calls = [call('Creating the classifier...')] + neutron_port = 'neutron_port_id' + port = 80 + protocol = 'tcp' + fc_name = 'red_http' + symmetrical = False + self.neutron_client.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + + expected_sfc_classifier_params = {'name': fc_name, + 'logical_source_port': neutron_port, + 'destination_port_range_min': port, + 'destination_port_range_max': port, + 'protocol': protocol} + self.os_sfc.create_classifier(neutron_port, port, + protocol, fc_name, symmetrical) + self.neutron_client.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_classifier_symmetric(self, mock_log): + """ + Checks the create_chain method + """ + + log_calls = [call('Creating the classifier...')] + neutron_port = 'neutron_port_id' + port = 80 + protocol = 'tcp' + fc_name = 'red_http' + symmetrical = True + serv_p = '123' + server_ip = '1.1.1.2' + self.neutron_client.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + + expected_sfc_classifier_params = {'name': fc_name, + '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} + self.os_sfc.create_classifier(neutron_port, port, + protocol, fc_name, symmetrical, + server_port='123', + server_ip='1.1.1.2') + self.neutron_client.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + mock_log.info.assert_has_calls(log_calls) + + @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_client.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + self.neutron_client.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_client.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + self.neutron_client.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_client.create_sfc_flow_classifier.return_value = \ + {'flow_classifier': {'id': 'fc_id'}} + self.neutron_client.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_client.create_sfc_flow_classifier.assert_has_calls( + [call({'flow_classifier': expected_sfc_classifier_params})]) + self.neutron_client.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_update_chain_symmetric(self, mock_log): + """ + Checks the update_chain method + """ + + log_calls = [call('Update the chain...')] + vnffg_name = 'red_http' + fc_name = 'blue_ssh' + symmetrical = True + self.neutron_client.find_resource.return_value = \ + {'id': 'fc_id'} + expected_chain_config = {'name': vnffg_name + '-port-chain', + 'flow_classifiers': ['fc_id'], + 'chain_parameters': {'symmetric': True}} + self.os_sfc.update_chain(vnffg_name, fc_name, symmetrical) + self.neutron_client.update_sfc_port_chain.assert_has_calls( + [call('fc_id', {'port_chain': expected_chain_config})]) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_swap_classifiers(self, mock_log): + """ + Checks the swap_classifiers method + """ + + log_calls = [call('Swap classifiers...')] + vnffg_1_name = 'red_http' + vnffg_2_name = 'blue_ssh' + symmetrical = False + self.os_sfc.swap_classifiers(vnffg_1_name, vnffg_2_name, symmetrical) + 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_client.list_sfc_port_pair_groups.return_value = \ + {'port_pair_groups': [{'id': 'id_ppg1'}, {'id': 'id_ppg2'}]} + self.neutron_client.list_sfc_port_pairs.return_value = \ + {'port_pairs': [{'id': 'id_pp1'}, {'id': 'id_pp2'}]} + self.os_sfc.delete_port_groups() + + self.neutron_client.delete_sfc_port_pair_group.assert_has_calls( + [call('id_ppg1'), call('id_ppg2')]) + self.neutron_client.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_client.list_sfc_port_chains.return_value = \ + {'port_chains': [{'id': 'id_pc1'}]} + self.neutron_client.list_sfc_flow_classifiers.return_value = \ + {'flow_classifiers': [{'id': 'id_fc1'}]} + self.os_sfc.delete_chain() + + self.neutron_client.delete_sfc_port_chain.\ + assert_has_calls([call('id_pc1')]) + self.neutron_client.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 + + def tearDown(self): + self.patcher.stop() + + @patch('os.getenv', autospec=True, return_value=None) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_tacker_client_version_returned_default(self, + mock_log, + mock_getenv): + """ + Checks the proper functionality of get_tacker_client_version + function when the os.getenv returns none + """ + result = os_sfc_utils.get_tacker_client_version() + self.assertEqual(result, '1.0') + mock_getenv.assert_called_once_with('OS_TACKER_API_VERSION') + mock_log.info.assert_not_called() + + @patch('os.getenv', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_tacker_client_version(self, + mock_log, + mock_getenv): + """ + Checks the proper functionality of get_tacker_client_version + function when the os.getenv returns version + """ + + ver = '2.0' + mock_getenv.return_value = ver + log_calls = [call("OS_TACKER_API_VERSION is set in env as '%s'", ver)] + + result = os_sfc_utils.get_tacker_client_version() + self.assertEqual(result, ver) + mock_getenv.assert_called_once_with('OS_TACKER_API_VERSION') + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_id_from_name_returned_none(self, mock_log): + """ + Checks the proper functionality of get_id_from_name + function when tacker_client.list returns None + """ + + resource_name = 'mock_resource_name' + resource_type = 'mock_resource_type' + params = {'fields': 'id', 'name': resource_name} + collection = resource_type + 's' + path = '/' + collection + self.mock_tacker_client.list.side_effect = Exception('ErrorMSG') + log_calls = [call('Error [get_id_from_name(tacker_client, ' + 'resource_type, resource_name)]: ErrorMSG')] + + result = os_sfc_utils.get_id_from_name(self.mock_tacker_client, + resource_type, + resource_name) + self.assertIsNone(result) + self.mock_tacker_client.list.assert_called_once_with(collection, + path, + **params) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.openstack_tests.get_credentials', + autospec=True, return_value='os_creds') + @patch('sfc.lib.openstack_utils.keystone_utils.keystone_session', + autospec=True, return_value='keystone_session_obj') + @patch('sfc.lib.openstack_utils.constants.ENV_FILE', autospec=True) + @patch('sfc.lib.openstack_utils.tackerclient.Client', autospec=True) + def test_get_tacker_client(self, mock_tacker_client, + mock_env_file, + mock_keystone_session, + mock_get_credentials): + """ + checks the proper functionality of get_tacker_client + function + """ + + mock_tacker_client_ins = mock_tacker_client.return_value + result = os_sfc_utils.get_tacker_client() + assert result is mock_tacker_client_ins + mock_get_credentials.assert_called_once_with(os_env_file=mock_env_file, + overrides=None) + mock_keystone_session.assert_called_once_with('os_creds') + mock_tacker_client.assert_called_once_with( + '1.0', session='keystone_session_obj') + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_id_from_name(self, mock_log): + """ + Checks the proper functionality of get_id_from_name + function when tacker_client.list returns id + """ + + resource_name = 'mock_resource_name' + resource_type = 'mock_resource_type' + params = {'fields': 'id', 'name': resource_name} + collection = resource_type + 's' + self.mock_tacker_client.list.return_value = {collection: {0: {'id': + 'mock_id'}}} + path = '/' + collection + result = os_sfc_utils.get_id_from_name(self.mock_tacker_client, + resource_type, + resource_name) + self.assertEqual('mock_id', result) + self.mock_tacker_client.list.assert_called_once_with(collection, + path, + **params) + mock_log.error.assert_not_called() + + @patch('sfc.lib.openstack_utils.get_id_from_name', autospec=True) + def test_get_vnfd_id(self, mock_get_id): + """ + Checks the proper functionality of get_vnfd_id + function + """ + + mock_get_id.return_value = 'id' + result = os_sfc_utils.get_vnfd_id(self.mock_tacker_client, + 'vnfd_name') + self.assertEqual('id', result) + mock_get_id.assert_called_once_with(self.mock_tacker_client, + 'vnfd', + 'vnfd_name') + + @patch('sfc.lib.openstack_utils.get_id_from_name', autospec=True) + def test_get_vim_id(self, mock_get_id): + """ + Checks the proper fucntionality of get_vim_id + function + """ + + mock_get_id.return_value = 'id' + result = os_sfc_utils.get_vim_id(self.mock_tacker_client, 'vim_name') + mock_get_id.assert_called_once_with(self.mock_tacker_client, + 'vim', + 'vim_name') + self.assertEqual('id', result) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_id_from_name', autospec=True) + def test_get_vnf_id(self, + mock_get_id, + mock_log, + mock_sleep): + """ + Checks the proper functionality of get_vnf_id + function + """ + + vnf_name = 'mock_vnf' + log_calls = [call("Could not retrieve ID for vnf with name [%s]." + " Retrying." % vnf_name)] + + get_id_calls = [call(self.mock_tacker_client, 'vnf', vnf_name)] * 2 + + mock_get_id.side_effect = [None, 'vnf_id'] + + result = os_sfc_utils.get_vnf_id(self.mock_tacker_client, vnf_name, 2) + self.assertEqual('vnf_id', result) + mock_sleep.assert_called_once_with(1) + mock_log.info.assert_has_calls(log_calls) + mock_get_id.assert_has_calls(get_id_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_id_from_name', autospec=True) + def test_get_vnffg_id(self, + mock_get_id, + mock_log, + mock_sleep): + """ + Checks the proper functionality of get_vnffg_id + function + """ + + vnffg_name = 'mock_vnffg' + log_calls = [call("Could not retrieve ID for vnffg with name [%s]." + " Retrying." % vnffg_name)] + + get_id_calls = [call(self.mock_tacker_client, 'vnffg', vnffg_name)] * 2 + + mock_get_id.side_effect = [None, 'vnf_id'] + + result = os_sfc_utils.get_vnffg_id(self.mock_tacker_client, + vnffg_name, + 2) + self.assertEqual('vnf_id', result) + mock_sleep.assert_called_once_with(1) + mock_log.info.assert_has_calls(log_calls) + mock_get_id.assert_has_calls(get_id_calls) + + @patch('sfc.lib.openstack_utils.get_id_from_name', autospec=True) + def test_get_vnffgd_id(self, mock_get_id): + """ + Checks the proper functionality of get_vnffgd_id + function + """ + + mock_get_id.return_value = 'id' + result = os_sfc_utils.get_vnffgd_id(self.mock_tacker_client, + 'vnffgd_name') + mock_get_id.assert_called_once_with(self.mock_tacker_client, + 'vnffgd', + 'vnffgd_name') + self.assertEqual('id', result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vnfds_returned_none(self, mock_log): + """ + Checks the proper functionality of list_vnfds + function when the list_vnfds returns none + """ + + log_calls = [call('Error [list_vnfds(tacker_client)]: ErrorMSG')] + self.mock_tacker_client.list_vnfds.side_effect = Exception('ErrorMSG') + result = os_sfc_utils.list_vnfds(self.mock_tacker_client) + mock_log.error.assert_has_calls(log_calls) + self.mock_tacker_client.list_vnfds.assert_called_once_with( + retrieve_all=True) + self.assertIsNone(result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vnfds(self, mock_log): + """ + Checks the proper functionality of list_vnfds + function when the list_vnfds returns vnfds + """ + + vnfds = { + 'vnfds': [{'id': 1}, + {'id': 2}] + } + self.mock_tacker_client.list_vnfds.return_value = vnfds + result = os_sfc_utils.list_vnfds(self.mock_tacker_client) + self.mock_tacker_client.list_vnfds.assert_called_once_with( + retrieve_all=True) + mock_log.assert_not_called() + self.assertEqual([1, 2], result) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnfd_returned_none_tosca_file_not_provided(self, mock_log): + """ + Checks the proper functionality of create_vnfd + function when an exception is raised + """ + + log_calls = [call("Creating the vnfd..."), + call("Error [create_vnfd(tacker_client, 'None')]: " + "ErrorMSG")] + + self.mock_tacker_client.create_vnfd.side_effect = Exception('ErrorMSG') + result = os_sfc_utils.create_vnfd(self.mock_tacker_client, + None, + 'vnfd_name') + self.assertIsNone(result) + self.mock_tacker_client.create_vnfd.assert_called_once_with( + body={'vnfd': {'attributes': {'vnfd': {}}, + 'name': 'vnfd_name'}}) + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.error.assert_has_calls(log_calls[1:]) + + @patch('yaml.safe_load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnfd_returned_none_tosca_file_provided(self, + mock_log, + mock_open, + mock_safe_load): + """ + Checks the proper functionality of create_vnfd + function when an exception is raised + """ + + log_calls = [call("Creating the vnfd..."), + call("VNFD template:\nmock_vnfd"), + call("Error [create_vnfd(tacker_client, 'tosca_file')]: " + "ErrorMSG")] + + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'mock_vnfd' + mock_safe_load.return_value = 'mock_vnfd_body' + self.mock_tacker_client.create_vnfd.side_effect = Exception('ErrorMSG') + result = os_sfc_utils.create_vnfd(self.mock_tacker_client, + 'tosca_file', + 'vnfd_name') + self.assertIsNone(result) + mock_open.assert_called_once_with('tosca_file') + open_handler.read.assert_called_once_with() + mock_safe_load.assert_called_once_with('mock_vnfd') + mock_log.info.assert_has_calls(log_calls[:2]) + mock_log.error.assert_has_calls(log_calls[2:]) + + @patch('yaml.safe_load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnfd(self, + mock_log, + mock_open, + mock_safe_load): + """ + Checks the proper functionality of create_vnfd + function + """ + + log_calls = [call("VNFD template:\nmock_vnfd")] + + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'mock_vnfd' + mock_safe_load.return_value = 'mock_vnfd_body' + result = os_sfc_utils.create_vnfd(self.mock_tacker_client, + 'tosca_file', + 'vnfd_name') + assert result is self.mock_tacker_client.create_vnfd.return_value + self.mock_tacker_client.create_vnfd.assert_called_once_with( + body={"vnfd": {"attributes": {"vnfd": "mock_vnfd_body"}, + "name": "vnfd_name"}}) + mock_open.assert_called_once_with('tosca_file') + open_handler.read.assert_called_once_with() + mock_safe_load.assert_called_once_with('mock_vnfd') + mock_log.info.assert_has_calls(log_calls) + mock_log.error.assert_not_called() + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_vnfd_returned_none(self, mock_log): + """ + Checks the proper functionality of delete_vnfd + function when an exception is raised + """ + + log_calls = [call("Error [delete_vnfd(tacker_client, 'None', 'None')]:" + " You need to provide VNFD id or VNFD name")] + + result = os_sfc_utils.delete_vnfd(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.get_vnfd_id', + autospec=True, return_value='vnfd') + def test_delete_vnfd(self, mock_get_vnfd_id): + """ + Checks the proper functionality of delete_vnfd + function + """ + + result = os_sfc_utils.delete_vnfd(self.mock_tacker_client, + None, + 'vnfd_name') + assert result is self.mock_tacker_client.delete_vnfd.return_value + mock_get_vnfd_id.assert_called_once_with(self.mock_tacker_client, + 'vnfd_name') + self.mock_tacker_client.delete_vnfd.assert_called_once_with('vnfd') + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vnfs_returned_none(self, mock_log): + """ + Checks the proper functionality of list_vnfs + function + """ + + log_calls = [call("Error [list_vnfs(tacker_client)]: ErrorMSG")] + + self.mock_tacker_client.list_vnfs.side_effect = Exception('ErrorMSG') + result = os_sfc_utils.list_vnfs(self.mock_tacker_client) + self.assertIsNone(result) + self.mock_tacker_client.list_vnfs.assert_called_once_with( + retrieve_all=True) + mock_log.error.assert_has_calls(log_calls) + + def test_list_vnfs(self): + """ + Checks the proper functionality of list_vnfs + function + """ + vnfs = {'vnfs': [{'id': 1}, + {'id': 2}]} + + self.mock_tacker_client.list_vnfs.return_value = vnfs + result = os_sfc_utils.list_vnfs(self.mock_tacker_client) + self.assertEqual([1, 2], result) + self.mock_tacker_client.list_vnfs.assert_called_once_with( + retrieve_all=True) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnf_returned_none_vnfd_not_provided(self, mock_log): + """ + Checks the proper functionality of create_vnf + function when an exception is raised + """ + + log_calls = [call("Creating the vnf..."), + call("error [create_vnf(tacker_client," + " 'vnf_name', 'None', 'None')]: " + "vnfd id or vnfd name is required")] + result = os_sfc_utils.create_vnf(self.mock_tacker_client, 'vnf_name') + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.error.assert_has_calls(log_calls[1:]) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnf_returned_none_vnfd_provided(self, mock_log): + """ + Checks the proper functionality of create_vnf + function when an exception is raised + """ + + log_calls = [call("Creating the vnf..."), + call("error [create_vnf(tacker_client," + " 'vnf_name', 'None', 'vnfd_name')]: " + "vim id or vim name is required")] + result = os_sfc_utils.create_vnf(self.mock_tacker_client, + 'vnf_name', + None, + 'vnfd_name', + None, + None) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.error.assert_has_calls(log_calls[1:]) + + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vim_id', + autospec=True, return_value='vim_id') + @patch('sfc.lib.openstack_utils.get_vnfd_id', + autospec=True, return_value='vnfd_id') + def test_create_vnf_vim_id_not_provided(self, + mock_get_vnfd_id, + mock_get_vim_id, + mock_log, + mock_open): + """ + Checks the proper functionality of create_vnf + function + """ + mock_body = {'vnf': {'attributes': {'param_values': 'mock_data'}, + 'vim_id': 'vim_id', + 'name': 'vnf_name', + 'vnfd_id': 'vnfd_id'}} + log_calls = [call('Creating the vnf...')] + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'mock_data' + result = os_sfc_utils.create_vnf(self.mock_tacker_client, + 'vnf_name', + None, + 'vnfd_name', + None, + 'vim_name', + 'param_file') + + assert result is self.mock_tacker_client.create_vnf.return_value + mock_get_vnfd_id.assert_called_once_with(self.mock_tacker_client, + 'vnfd_name') + mock_get_vim_id.assert_called_once_with(self.mock_tacker_client, + 'vim_name') + mock_log.info.assert_has_calls(log_calls) + self.mock_tacker_client.create_vnf.assert_called_once_with( + body=mock_body) + + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnf_vim_id_provided(self, mock_log, mock_open): + """ + Checks the proper functionality of create_vnf + function + """ + mock_body = {'vnf': {'attributes': {}, + 'vim_id': 'vim_id', + 'name': 'vnf_name', + 'vnfd_id': 'vnfd_id'}} + log_calls = [call('Creating the vnf...')] + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'mock_data' + + result = os_sfc_utils.create_vnf(self.mock_tacker_client, + 'vnf_name', + 'vnfd_id', + 'vnfd_name', + 'vim_id', + 'vim_name') + assert result is self.mock_tacker_client.create_vnf.return_value + mock_log.info.assert_has_calls(log_calls) + self.mock_tacker_client.create_vnf.assert_called_once_with( + body=mock_body) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_vnf_returned_none_vnf_not_provided(self, mock_log): + """ + Checks the proper functionality of get_vnf + functionality when an exception is raised + """ + + log_calls = [call("Could not retrieve VNF [vnf_id=None, vnf_name=None]" + " - You must specify vnf_id or vnf_name")] + + result = os_sfc_utils.get_vnf(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.get_vnf_id', + autospec=True, return_value=None) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_vnf_returned_none_vnf_provided(self, + mock_log, + mock_get_vnf_id): + """ + Checks the proper functionality of get_vnf + functionality when an exception is raised + """ + + log_calls = [call("Could not retrieve VNF [vnf_id=None, " + "vnf_name=vnf_name] - Could not retrieve ID from " + "name [vnf_name]")] + result = os_sfc_utils.get_vnf(self.mock_tacker_client, + None, + 'vnf_name') + self.assertIsNone(result) + mock_get_vnf_id.assert_called_once_with(self.mock_tacker_client, + 'vnf_name') + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.list_vnfs', autospec=True) + def test_get_vnf(self, + mock_list_vnfs, + mock_log): + """ + Checks the proper functionality of get_vnf + function + """ + + vnf = {'vnfs': [{'id': 'default'}, + {'id': 'vnf_id'}]} + + mock_list_vnfs.return_value = vnf + result = os_sfc_utils.get_vnf(self.mock_tacker_client, 'vnf_id', None) + self.assertDictEqual(vnf['vnfs'][1], result) + mock_log.error.assert_not_called() + + @patch('json.loads', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf', autospe=True) + def test_get_vnf_ip(self, + mock_get_vnf, + mock_json_loads): + """ + Checks the proper functionality of get_vnf_ip + function + """ + + vnf = {"mgmt_url": {"VDU1": "192.168.120.3"}} + mock_get_vnf.return_value = vnf + mock_json_loads.return_value = vnf['mgmt_url'] + result = os_sfc_utils.get_vnf_ip(self.mock_tacker_client) + self.assertEqual("192.168.120.3", result) + mock_get_vnf.assert_called_once_with(self.mock_tacker_client, + None, + None) + mock_json_loads.assert_called_once_with(vnf['mgmt_url']) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf', autospec=True) + def test_wait_for_vnf_returned_none_unable_to_retrieve_vnf(self, + mock_get_vnf, + mock_log): + """ + Checks the proper functionality of wait_for_vnf + function when an Exception is raised + """ + + mock_get_vnf.return_value = None + log_calls = [call("error [wait_for_vnf(tacker_client, 'vnf_id', " + "'vnf_name')]: Could not retrieve VNF - id='vnf_id'," + " name='vnf_name'")] + + result = os_sfc_utils.wait_for_vnf(self.mock_tacker_client, + 'vnf_id', + 'vnf_name', + 0) + self.assertIsNone(result) + mock_get_vnf.assert_called_once_with(self.mock_tacker_client, + 'vnf_id', + 'vnf_name') + mock_log.error.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf', autospec=True) + def test_wait_for_vnf_returned_none_unable_to_boot_vnf(self, + mock_get_vnf, + mock_log, + mock_sleep): + """ + Checks the proper functionality of wait_for_vnf + function when an Exception is raised + """ + + mock_vnf_values = [{'id': 'vnf_id', + 'status': 'ERROR'}, + {'id': 'vnf_id', + 'status': 'PEDNING_CREATE'}] + mock_get_vnf.side_effect = mock_vnf_values + log_calls = [call("Waiting for vnf %s" % str(mock_vnf_values[0])), + call("error [wait_for_vnf(tacker_client, 'vnf_id', " + "'vnf_name')]: Error when booting vnf vnf_id")] + + result = os_sfc_utils.wait_for_vnf(self.mock_tacker_client, + 'vnf_id', + 'vnf_name', + 0) + self.assertIsNone(result) + mock_get_vnf.assert_called_once_with(self.mock_tacker_client, + 'vnf_id', + 'vnf_name') + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.error.assert_has_calls(log_calls[1:]) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf', autospec=True) + def test_wait_for_vnf_returned_none_timeout_booting_vnf(self, + mock_get_vnf, + mock_log, + mock_sleep): + """ + Checks the proper functionality of wait_for_vnf + function when an Exception is raised + """ + + mock_vnf_values = [{'id': 'vnf_id', + 'status': 'PENDING_CREATE'}, + {'id': 'vnf_id', + 'status': 'PENDING_CREATE'}] + mock_get_vnf.side_effect = mock_vnf_values + log_calls = [call("Waiting for vnf %s" % str(mock_vnf_values[1])), + call("error [wait_for_vnf(tacker_client, 'vnf_id', " + "'vnf_name')]: Timeout when booting vnf vnf_id")] + + result = os_sfc_utils.wait_for_vnf(self.mock_tacker_client, + 'vnf_id', + 'vnf_name', + 0) + self.assertIsNone(result) + mock_get_vnf.assert_called_with(self.mock_tacker_client, + 'vnf_id', + 'vnf_name') + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.error.assert_has_calls(log_calls[1:]) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf', autospec=True) + def test_wait_for_vnf(self, + mock_get_vnf, + mock_log, + mock_sleep): + """ + Checks for the proper functionality of wait_for_vnf + function + """ + + mock_vnf_values = [{'status': 'PENDING_CREATE', + 'id': 'vnf_id'}, + {'status': 'ACTIVE', + 'id': 'vnf_id'}] + + log_calls = [call("Waiting for vnf %s" % mock_vnf_values[0])] + + mock_get_vnf.side_effect = mock_vnf_values + + result = os_sfc_utils.wait_for_vnf(self.mock_tacker_client, + 'vnf_id', + 'vnf_name', + 3) + self.assertEqual('vnf_id', result) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_vnf_returned_none(self, mock_log): + """ + Checks the proper functionality of delete_vnf + function + """ + + log_calls = [call("Error [delete_vnf(tacker_client, 'None', 'None')]:" + " You need to provide a VNF id or name")] + result = os_sfc_utils.delete_vnf(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnf_id', autospec=True) + def test_delete_vnf(self, + mock_get_vnf_id, + mock_log): + """ + Checks the proper functionality of delete_vnf + function + """ + + mock_get_vnf_id.return_value = 'vnf' + result = os_sfc_utils.delete_vnf(self.mock_tacker_client, + None, + 'vnf_name') + assert result is self.mock_tacker_client.delete_vnf.return_value + mock_get_vnf_id.assert_called_once_with(self.mock_tacker_client, + 'vnf_name') + self.mock_tacker_client.delete_vnf.assert_called_once_with('vnf') + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vim_returned_none(self, + mock_log): + """ + Checks the proper functionality of create_vim + function when the vim_file is not provided + """ + + self.mock_tacker_client.create_vim.side_effect = Exception('ErrorMSG') + log_calls = [[call("Creating the vim...")], + [call("Error [create_vim(tacker_client, 'None')]" + ": ErrorMSG")]] + + result = os_sfc_utils.create_vim(self.mock_tacker_client) + self.assertIsNone(result) + self.mock_tacker_client.create_vim.assert_called_once_with(body={}) + mock_log.info.assert_has_calls(log_calls[0]) + mock_log.error.assert_has_calls(log_calls[1]) + + @patch('json.load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vim(self, + mock_log, + mock_open, + mock_json_loads): + """ + Checks the proper functionality of create_vim + function + """ + + log_calls = [call("Creating the vim..."), + call("VIM template:\nmock_data")] + + open_handler = mock_open.return_value.__enter__.return_value + mock_json_loads.return_value = 'mock_data' + result = os_sfc_utils.create_vim(self.mock_tacker_client, 'vim_file') + assert result is self.mock_tacker_client.create_vim.return_value + mock_log.info.assert_has_calls(log_calls) + mock_open.assert_called_once_with('vim_file') + mock_json_loads.assert_called_once_with(open_handler) + mock_log.error.assert_not_called() + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnffgd_returned_none(self, mock_log): + """ + Checks the proper functionality of create_vnffgd + function when create_vnffgd raises an Exception + """ + + self.mock_tacker_client.create_vnffgd.side_effect = Exception( + 'ErrorMSG') + log_calls = [[call("Creating the vnffgd...")], + [call("Error [create_vnffgd(tacker_client, 'None')]" + ": ErrorMSG")]] + + result = os_sfc_utils.create_vnffgd(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls[0]) + mock_log.error.assert_has_calls(log_calls[1]) + + @patch('yaml.safe_load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnffgd(self, + mock_log, + mock_open, + mock_safe_load): + """ + Checks the proper functionality of create_vnffgd + function + """ + + log_calls = [call('Creating the vnffgd...'), + call('VNFFGD template:\nmock_data')] + + vnffgd_body = {'id': 0, 'type': 'dict'} + + mock_vim_body = {'vnffgd': {'name': 'vnffgd_name', + 'template': {'vnffgd': vnffgd_body}}} + + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'mock_data' + mock_safe_load.return_value = {'id': 0, 'type': 'dict'} + result = os_sfc_utils.create_vnffgd(self.mock_tacker_client, + 'tosca_file', + 'vnffgd_name') + assert result is self.mock_tacker_client.create_vnffgd.return_value + mock_open.assert_called_once_with('tosca_file') + mock_safe_load.assert_called_once_with('mock_data') + self.mock_tacker_client.create_vnffgd.assert_called_once_with( + body=mock_vim_body) + mock_log.info.assert_has_calls(log_calls) + mock_log.error.assert_not_called() + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnffg_returned_none(self, mock_log): + """ + Checks the proper functionality of create_vnffg + function when the vnffgd id or vnffg name is not provided + """ + + log_calls = [[call("Creating the vnffg...")], + [call("error [create_vnffg(tacker_client," + " 'None', 'None', 'None')]: " + "vnffgd id or vnffgd name is required")]] + + result = os_sfc_utils.create_vnffg(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.info.assert_has_calls(log_calls[0]) + mock_log.error.assert_has_calls(log_calls[1]) + + @patch('yaml.safe_load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + @patch('sfc.lib.openstack_utils.get_vnffgd_id', autospec=True) + def test_create_vnffg_vnffgd_id_not_provided(self, + mock_get_vnffgd_id, + mock_log, + mock_open, + mock_safe_load): + """ + Checks the proper functionality of create_vnffg + function when the vnffgd id or vnffg name is not provided + """ + + log_calls = [call('Creating the vnffg...')] + vnffg_calls = [call(body={ + 'vnffg': { + 'attributes': {'param_values': {'type': 'dict', + 'id': 0}}, + 'vnffgd_id': 'mocked_vnffg_id', + 'name': 'vnffg_name', + 'symmetrical': False}})] + mock_get_vnffgd_id.return_value = 'mocked_vnffg_id' + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'data' + mock_safe_load.return_value = {'id': 0, 'type': 'dict'} + + result = os_sfc_utils.create_vnffg(self.mock_tacker_client, + 'vnffg_name', + None, + 'vnffgd_name', + 'param_file') + assert result is self.mock_tacker_client.create_vnffg.return_value + mock_open.assert_called_once_with('param_file') + open_handler.read.assert_called_once_with() + mock_get_vnffgd_id.assert_called_once_with(self.mock_tacker_client, + 'vnffgd_name') + mock_safe_load.assert_called_once_with('data') + mock_log.info.assert_has_calls(log_calls) + self.mock_tacker_client.create_vnffg.assert_has_calls(vnffg_calls) + + @patch('yaml.safe_load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_create_vnffg_vnffgd_id_provided(self, + mock_log, + mock_open, + mock_safe_load): + """ + Checks the proper functionality of create_vnffg + function when the vnffgd id or vnffg name is not provided + """ + + log_calls = [call('Creating the vnffg...')] + vnffg_calls = [call(body={ + 'vnffg': { + 'attributes': {'param_values': {'type': 'dict', + 'id': 0}}, + 'vnffgd_id': 'vnffgd_id', + 'name': 'vnffg_name', + 'symmetrical': False}})] + open_handler = mock_open.return_value.__enter__.return_value + open_handler.read.return_value = 'data' + mock_safe_load.return_value = {'id': 0, 'type': 'dict'} + + result = os_sfc_utils.create_vnffg(self.mock_tacker_client, + 'vnffg_name', + 'vnffgd_id', + 'vnffgd_name', + 'param_file') + assert result is self.mock_tacker_client.create_vnffg.return_value + mock_open.assert_called_once_with('param_file') + open_handler.read.assert_called_once_with() + mock_safe_load.assert_called_once_with('data') + mock_log.info.assert_has_calls(log_calls) + self.mock_tacker_client.create_vnffg.assert_has_calls(vnffg_calls) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vnffgds_returned_none(self, mock_log): + """ + Checks the proper functionality of list_vnffgds + function when list_vnffgds raises an Exception + """ + + self.mock_tacker_client.list_vnffgds.side_effect = Exception( + 'ErrorMSG') + log_calls = [call('Error [list_vnffgds(tacker_client)]: ErrorMSG')] + + result = os_sfc_utils.list_vnffgds(self.mock_tacker_client) + self.assertIsNone(result) + self.mock_tacker_client.list_vnffgds.assert_called_once_with( + retrieve_all=True) + mock_log.error.assert_has_calls(log_calls) + + def test_list_vnffgds(self): + """ + Checks the proper functtionality of list_vnffgds + function + """ + + vnffgds = {'vnffgds': [{'id': 'vnffgd_obj_one'}, + {'id': 'vnffgd_obj_two'}]} + + mock_vnffgds = ['vnffgd_obj_one', 'vnffgd_obj_two'] + + self.mock_tacker_client.list_vnffgds.return_value = vnffgds + result = os_sfc_utils.list_vnffgds(self.mock_tacker_client) + self.assertEqual(mock_vnffgds, result) + self.mock_tacker_client.list_vnffgds.assert_called_once_with( + retrieve_all=True) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vnffgs_returned_none(self, mock_log): + """ + Checks the proper functionality of list_vnffgs + function when list_vnffgs raises an Exception + """ + + self.mock_tacker_client.list_vnffgs.side_effect = Exception('ErrorMSG') + log_calls = [call('Error [list_vnffgs(tacker_client)]: ErrorMSG')] + + result = os_sfc_utils.list_vnffgs(self.mock_tacker_client) + self.assertIsNone(result) + self.mock_tacker_client.list_vnffgs.assert_called_once_with( + retrieve_all=True) + mock_log.error.assert_has_calls(log_calls) + + def test_list_vnffgs(self): + """ + Checks the proper functionality of list_vnffgs + function + """ + + vnffgs = {'vnffgs': [{'id': 'vnffg_obj_one'}, + {'id': 'vnffg_obj_two'}]} + + mock_vnffgs = ['vnffg_obj_one', 'vnffg_obj_two'] + + self.mock_tacker_client.list_vnffgs.return_value = vnffgs + result = os_sfc_utils.list_vnffgs(self.mock_tacker_client) + self.assertEqual(mock_vnffgs, result) + self.mock_tacker_client.list_vnffgs.assert_called_once_with( + retrieve_all=True) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_vnffg_returned_none(self, mock_log): + """ + Checks the proper functionality of delete_vnffg + function + """ + + log_calls = [call("Error [delete_vnffg(tacker_client, 'None', 'None')]" + ": You need to provide a VNFFG id or name")] + + result = os_sfc_utils.delete_vnffg(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.get_vnffg_id', + autospec=True, return_value='vnffg') + def test_delete_vnffg(self, mock_get_vnffg_id): + """ + Checks the proper functionality of delete_vnffg + function + """ + + self.mock_tacker_client.delete_vnffg.return_value = 'deleted' + result = os_sfc_utils.delete_vnffg(self.mock_tacker_client, + None, + 'vnffg_name') + self.assertEqual('deleted', result) + mock_get_vnffg_id.assert_called_once_with(self.mock_tacker_client, + 'vnffg_name') + self.mock_tacker_client.delete_vnffg.assert_called_once_with('vnffg') + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_vnffgd_returned_none(self, mock_log): + """ + Checks the proper functionality of delete_vnffgd + function + """ + + log_calls = [call("Error [delete_vnffgd(tacker_client, 'None', 'None')" + "]: You need to provide VNFFGD id or VNFFGD name")] + + result = os_sfc_utils.delete_vnffgd(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.get_vnffgd_id', + autospec=True, return_value='vnffgd') + def test_delete_vnffgd(self, mock_get_vnffgd_id): + """ + Checks the proper functionality of delete_vnffgd + function + """ + + self.mock_tacker_client.delete_vnffgd.return_value = 'deleted' + result = os_sfc_utils.delete_vnffgd(self.mock_tacker_client, + None, + 'vnffgd_name') + self.assertEqual('deleted', result) + mock_get_vnffgd_id.assert_called_once_with(self.mock_tacker_client, + 'vnffgd_name') + self.mock_tacker_client.delete_vnffgd.assert_called_once_with('vnffgd') + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_list_vims_returned_none(self, mock_log): + """ + Checks the proper functionality of list_vims + function when VNFFGD id is not provided + """ + + self.mock_tacker_client.list_vims.side_effect = Exception('ErrorMSG') + log_calls = [call('Error [list_vims(tacker_client)]: ErrorMSG')] + + result = os_sfc_utils.list_vims(self.mock_tacker_client) + self.assertIsNone(result) + self.mock_tacker_client.list_vims.assert_called_once_with( + retrieve_all=True) + mock_log.error.assert_has_calls(log_calls) + + def test_list_vims(self): + """ + Checks the proper functionality list_vims + function + """ + + vims = {'vims': [{'id': 'vim_obj_1'}, + {'id': 'vim_obj_2'}]} + + mock_vims = ['vim_obj_1', 'vim_obj_2'] + + self.mock_tacker_client.list_vims.return_value = vims + result = os_sfc_utils.list_vims(self.mock_tacker_client) + self.assertEqual(mock_vims, result) + self.mock_tacker_client.list_vims.assert_called_once_with( + retrieve_all=True) + + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_delete_vim_returned_none(self, mock_log): + """ + Checks the proper functionality of delete_vim + function when VIM id and VIM name is not provided + """ + + log_calls = [call("Error [delete_vim(tacker_client, '%s', '%s')]: %s" + % (None, None, 'You need to provide ' + 'VIM id or VIM name'))] + + result = os_sfc_utils.delete_vim(self.mock_tacker_client) + self.assertIsNone(result) + mock_log.error.assert_has_calls(log_calls) + + @patch('sfc.lib.openstack_utils.get_vim_id', + autospec=True, return_value='vim_id') + def test_delete_vim(self, mock_get_vim_id): + """ + Checks the proper functionality of delete_vim + function + """ + + result = os_sfc_utils.delete_vim(self.mock_tacker_client, + None, + 'vim_name') + assert result is self.mock_tacker_client.delete_vim.return_value + mock_get_vim_id.assert_called_once_with(self.mock_tacker_client, + 'vim_name') + self.mock_tacker_client.delete_vim.assert_called_once_with('vim_id') + + @patch('sfc.lib.openstack_utils.get_tacker_client', + autospec=True, return_value='tacker_client_obj') + @patch('sfc.lib.openstack_utils.logger', autospec=True) + def test_get_tacker_items(self, + mock_log, + mock_tacker_client): + """ + Checks the proper functionality of get_tacker_items + function + """ + + mock_dict = {'list_vims': DEFAULT, + 'list_vnfds': DEFAULT, + 'list_vnfs': DEFAULT, + 'list_vnffgds': DEFAULT, + 'list_vnffgs': DEFAULT} + with patch.multiple('sfc.lib.openstack_utils', + **mock_dict) as mock_values: + + os_sfc_utils.get_tacker_items() + + mock_tacker_client.assert_called_once_with() + self.assertEqual(5, mock_log.debug.call_count) + for key in mock_values: + mock_values[key].assert_called_once_with('tacker_client_obj') + + @patch('json.dump', autospec=True) + @patch('json.load', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.create_vim', autospec=True) + def test_register_vim(self, + mock_create_vim, + mock_open, + mock_json_loads, + mock_json_dump): + """ + Checks the proper functionality of register_vim + function + """ + + tmp_file = '/tmp/register-vim.json' + open_handler = mock_open.return_value.__enter__.return_value + open_calls = [call('vim_file'), + call(tmp_file, 'w')] + + mock_json_loads.return_value = {'vim': {'auth_cred': + {'password': None}, + 'auth_url': None}} + + json_dict = {'vim': {'auth_cred': {'password': 'os_auth_cred'}, + 'auth_url': 'os_auth_url'}} + + patch_dict = {'OS_AUTH_URL': 'os_auth_url', + 'OS_PASSWORD': 'os_auth_cred'} + + with patch.dict('os.environ', patch_dict): + os_sfc_utils.register_vim(self.mock_tacker_client, 'vim_file') + mock_json_loads.assert_called_once_with(open_handler) + mock_json_dump.assert_called_once_with(json_dict, + mock_open(tmp_file, 'w')) + mock_open.assert_has_calls(open_calls, any_order=True) + mock_create_vim.assert_called_once_with(self.mock_tacker_client, + vim_file=tmp_file) + + @patch('json.dump', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.create_vnf', autospec=True) + @patch('os.path.join', + autospec=True, return_value='/tmp/param_av_zone.json') + def test_create_vnf_in_av_zone(self, + mock_path_join, + mock_create_vnf, + mock_open, + mock_json_dump): + """ + Checks the proper fucntionality of test_create_vnf_in_av_zone + fucntion + """ + + data = {'zone': 'av::zone'} + param_file = '/tmp/param_av_zone.json' + os_sfc_utils.create_vnf_in_av_zone(self.mock_tacker_client, + 'vnf_name', + 'vnfd_name', + 'vim_name', + 'param_file', + 'av::zone') + open_handler = mock_open.return_value.__enter__.return_value + mock_path_join.assert_called_once_with('/tmp', 'param_av_zone.json') + mock_open.assert_called_once_with(param_file, 'w+') + mock_json_dump.assert_called_once_with(data, open_handler) + mock_create_vnf.assert_called_once_with(self.mock_tacker_client, + 'vnf_name', + vnfd_name='vnfd_name', + vim_name='vim_name', + param_file=param_file) + + @patch('json.dump', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('sfc.lib.openstack_utils.create_vnffg', autospec=True) + @patch('os.path.join', autospec=True, return_value='/tmp/param_name.json') + def test_create_vnffg_with_param_file(self, + mock_path_join, + mock_create_vnffg, + mock_open, + mock_json_dump): + """ + Checks the proper functionality of create_vnffg_with_param_file + function + """ + + data = { + 'ip_dst_prefix': 'server_ip', + 'net_dst_port_id': 'server_port', + 'net_src_port_id': 'client_port' + } + param_file = '/tmp/param_name.json' + os_sfc_utils.create_vnffg_with_param_file(self.mock_tacker_client, + 'vnffgd_name', + 'vnffg_name', + 'default_param_file', + 'client_port', + 'server_port', + 'server_ip') + open_handler = mock_open.return_value.__enter__.return_value + mock_path_join.assert_called_once_with('/tmp', 'param_vnffg_name.json') + mock_open.assert_called_once_with(param_file, 'w+') + mock_json_dump.assert_called_once_with(data, open_handler) + mock_create_vnffg.assert_called_once_with(self.mock_tacker_client, + vnffgd_name='vnffgd_name', + vnffg_name='vnffg_name', + param_file=param_file, + symmetrical=True) diff --git a/sfc/unit_tests/unit/lib/test_test_utils.py b/sfc/unit_tests/unit/lib/test_test_utils.py new file mode 100644 index 00000000..a7d2bfde --- /dev/null +++ b/sfc/unit_tests/unit/lib/test_test_utils.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python + +############################################################################### +# Copyright (c) 2018 Venkata Harshavardhan Reddy Allu and others. +# +# 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 unittest + +from mock import Mock +from mock import call +from mock import patch + +import sfc.lib.test_utils as test_utils + +__author__ = "Harshavardhan Reddy <venkataharshavardhan_ven@srmuniv.edu.in>" + + +class SfcTestUtilsTesting(unittest.TestCase): + + def setUp(self): + self.ip = '10.10.10.10' + + @patch('subprocess.PIPE', autospec=True) + @patch('subprocess.Popen', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + def test_run_cmd(self, + mock_log, + mock_popen, + mock_pipe): + """ + Checks the proper functionality of run_cmd + function + """ + + cmd = 'mock_command' + log_calls = [call('Running [mock_command] returns: [0] ' + + '- STDOUT: "output" - STDERR: "output"')] + + pipe_mock = Mock() + attrs = {'communicate.return_value': ('output', 'error'), + 'returncode': 0} + pipe_mock.configure_mock(**attrs) + mock_popen.return_value = pipe_mock + result = test_utils.run_cmd(cmd) + self.assertTupleEqual(result, (0, 'output', 'error')) + mock_popen.assert_called_once_with(cmd, + shell=True, + stdout=mock_pipe, + stderr=mock_pipe) + mock_popen.return_value.communicate.assert_called_once_with() + mock_log.debug.assert_has_calls(log_calls) + + @patch('sfc.lib.test_utils.run_cmd', autospec=True) + def test_run_cmd_remote(self, mock_run_cmd): + """ + Checks the proper functionality of the run_cmd_remote + function + """ + + cmd = 'mock_command' + mock_rc = 'sshpass -p opnfv ssh -q -o UserKnownHostsFile=/dev/null' + \ + ' -o StrictHostKeyChecking=no -o ConnectTimeout=50 ' + \ + ' root@10.10.10.10 mock_command' + test_utils.run_cmd_remote(self.ip, cmd) + mock_run_cmd.assert_called_once_with(mock_rc) + + @patch('shutil.copyfileobj') + @patch('urllib.urlopen', autospec=True) + @patch('__builtin__.open', autospec=True) + def test_download_url_with_exception(self, + mock_open, + mock_urlopen, + mock_copyfileobj): + """ + Checks the proper functionality of download_url + function when an exception is raised + """ + + dest_path = 'mocked_/dest_/path' + url = 'mocked_/url' + mock_urlopen.side_effect = Exception('HttpError') + self.assertFalse(test_utils.download_url(url, dest_path)) + mock_urlopen.assert_called_once_with(url) + mock_open.assert_not_called() + mock_copyfileobj.assert_not_called() + + @patch('urllib.urlopen', autospec=True) + @patch('__builtin__.open', autospec=True) + @patch('shutil.copyfileobj', autospec=True) + def test_download_url_without_exception(self, + mock_copyfileobj, + mock_open, + mock_urlopen): + """ + Checks the proper functionality of download_url + function when any exception isn't raised + """ + + response = '<mocked_response>' + dest_path = 'mocked_/dest_/path' + url = 'mocked_/url' + open_handler = mock_open.return_value.__enter__.return_value + mock_urlopen.return_value = response + self.assertTrue(test_utils.download_url(url, dest_path)) + mock_urlopen.assert_called_once_with(url) + mock_open.assert_called_once_with('mocked_/dest_/path/url', 'wb') + mock_copyfileobj.assert_called_once_with(response, open_handler) + + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.download_url', autospec=True) + @patch('os.path.isfile', autospec=True, return_value=False) + @patch('os.path.dirname', autospec=True, return_value='mocked_') + @patch('os.path.basename', autospec=True, return_value='image_path') + def test_download_image_file_not_found(self, + mock_basename, + mock_dirname, + mock_isfile, + mock_download_url, + mock_log): + """ + Checks the proper functionality of download_image + function when the image file was not found locally + """ + + url = 'mocked_/url' + image_path = 'mocked_/image_path' + log_calls = [call('Downloading image')] + test_utils.download_image(url, image_path) + mock_log.info.assert_has_calls(log_calls) + mock_basename.assert_called_once_with(image_path) + mock_dirname.assert_called_once_with(image_path) + mock_isfile.assert_called_once_with(image_path) + mock_download_url.assert_called_once_with('mocked_/url/image_path', + 'mocked_') + + @patch('sfc.lib.test_utils.download_url') + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('os.path.isfile', autospec=True, return_value=True) + @patch('os.path.dirname', autospec=True, return_value='mocked_') + @patch('os.path.basename', autospec=True, return_value='image_path') + def test_download_image_file_found(self, + mock_basename, + mock_dirname, + mock_isfile, + mock_log, + mock_download_url): + """ + Checks the proper functionality of download_image + function when the image file was found locally + """ + + url = 'mocked_/url' + image_path = 'mocked_/image_path' + log_calls = [call('Using old image')] + test_utils.download_image(url, image_path) + mock_log.info.assert_has_calls(log_calls) + mock_basename.assert_called_once_with(image_path) + mock_dirname.assert_called_once_with(image_path) + mock_isfile.assert_called_once_with(image_path) + mock_download_url.assert_not_called() + + @patch('sfc.lib.test_utils.run_cmd', autospec=True) + def test_ping_gets_error(self, mock_run_cmd): + """ + Checks the proper functionality of ping + function when run_cmd returns non-zero returncode + """ + + mock_cmd = 'ping -c1 -w1 %s' % self.ip + mock_run_cmd.return_value = (1, '', '') + self.assertFalse(test_utils.ping(self.ip, 1)) + mock_run_cmd.assert_called_once_with(mock_cmd) + + @patch('sfc.lib.test_utils.run_cmd', autospec=True) + def test_ping_gets_no_error(self, mock_run_cmd): + """ + Checks the proper functionality of ping + function when run_cmd returns zero as returncode + """ + + mock_cmd = 'ping -c1 -w1 %s' % self.ip + mock_run_cmd.return_value = (0, '', '') + self.assertTrue(test_utils.ping(self.ip, 1)) + mock_run_cmd.assert_called_once_with(mock_cmd) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_start_http_server_returned_false_failed_to_start( + self, mock_run_cmd_remote, mock_log, mock_sleep): + """ + Checks the proper functionality of start_http_server + function when http_server is failed to start + """ + + cmd = "\'python -m SimpleHTTPServer 80 " + \ + "> /dev/null 2>&1 &\'" + + rcr_calls = [call(self.ip, cmd), + call(self.ip, 'ps aux | grep SimpleHTTPServer')] + log_calls = [call('Failed to start http server')] + + mock_run_cmd_remote.side_effect = [('', '', ''), + ('', '', '')] + + result = test_utils.start_http_server(self.ip, 1) + self.assertFalse(result) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_sleep.assert_called_once_with(3) + mock_log.error.assert_has_calls(log_calls) + mock_log.info.assert_not_called() + mock_log.debug.assert_not_called() + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_start_http_server_returned_false_port_is_down( + self, mock_run_cmd_remote, mock_log, mock_sleep): + """ + Checks the proper functionality of start_http_server + function when port 80 is down + """ + + cmd = "\'python -m SimpleHTTPServer 80 " + \ + "> /dev/null 2>&1 &\'" + + rcr_calls = [call(self.ip, cmd), + call(self.ip, 'ps aux | grep SimpleHTTPServer'), + call(self.ip, 'netstat -pntl | grep :80')] + + log_calls = [call('output'), + call('Port 80 is not up yet')] + + mock_run_cmd_remote.side_effect = [('', '', ''), + ('', 'output', ''), + ('', '', '')] + + result = test_utils.start_http_server(self.ip, 1) + self.assertFalse(result) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_sleep.assert_called_with(5) + mock_log.info.assert_has_calls(log_calls[:1]) + mock_log.debug.assert_has_calls(log_calls[1:]) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_start_http_server_returned_true(self, + mock_run_cmd_remote, + mock_log, + mock_sleep): + """ + Checks the proper functionality of start_http_server + function when the port 80 is up + """ + + cmd = "\'python -m SimpleHTTPServer 80 " + \ + "> /dev/null 2>&1 &\'" + + rcr_calls = [call(self.ip, cmd), + call(self.ip, 'ps aux | grep SimpleHTTPServer'), + call(self.ip, 'netstat -pntl | grep :80')] + + log_calls = [call('output')] + + mock_run_cmd_remote.side_effect = [('', '', ''), + ('', 'output', ''), + ('', 'output', '')] + + self.assertTrue(test_utils.start_http_server(self.ip, 1)) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_sleep.assert_called_once_with(3) + mock_log.info.assert_has_calls(log_calls) + mock_log.debug.assert_not_called() + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_start_vxlan_tool_returned_false(self, + mock_run_cmd_remote, + mock_log, + mock_sleep): + """ + Checks the proper functionality of start_vxlan_tool + function when no output is returned on ps command + """ + + mock_run_cmd_remote.side_effect = [('', 'output', ''), + ('', '', '')] + + mock_rc = 'nohup python /root/vxlan_tool.py --do ' + \ + 'forward --interface eth0 > /dev/null 2>&1 &' + + rcr_calls = [call(self.ip, mock_rc), + call(self.ip, 'ps aux | grep vxlan_tool')] + + log_calls = [call('Failed to start the vxlan tool')] + + self.assertFalse(test_utils.start_vxlan_tool(self.ip)) + mock_sleep.assert_called_once_with(3) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_log.error.assert_has_calls(log_calls) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_start_vxlan_tool_returned_output(self, + mock_run_cmd_remote, + mock_log, + mock_sleep): + """ + Checks the proper functionality of start_vxlan_tool + function when output is returned on ps command + """ + + mock_run_cmd_remote.side_effect = [('', 'output', ''), + ('', 'output', '')] + + mock_rc = 'nohup python /root/vxlan_tool.py --do ' + \ + 'forward --interface eth0 > /dev/null 2>&1 &' + + rcr_calls = [call(self.ip, mock_rc), + call(self.ip, 'ps aux | grep vxlan_tool')] + + self.assertIsNotNone(test_utils.start_vxlan_tool(self.ip)) + mock_sleep.assert_called_once_with(3) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_log.error.assert_not_called() + + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_stop_vxlan_tool(self, mock_run_cmd_remote): + """ + Checks the proper functionality of stop_vxlan_tool + function + """ + + mock_rc = 'pkill -f vxlan_tool.py' + test_utils.stop_vxlan_tool(self.ip) + mock_run_cmd_remote.assert_called_once_with(self.ip, mock_rc) + + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_netcat(self, + mock_run_cmd_remote, + mock_log): + """ + Checks the proper functionality of netcat + function + """ + + dest_ip = 'mock_destination_ip' + c = 'nc -z -w 5 %s 1234' % dest_ip + log_calls = [call('Running [%s] from [%s] returns [0]' % (c, self.ip))] + mock_run_cmd_remote.return_value = (0, '', '') + result = test_utils.netcat(self.ip, dest_ip, 1234) + self.assertEqual(result, 0) + mock_log.info.assert_has_calls(log_calls) + + @patch('sfc.lib.test_utils.netcat', autospec=True) + def test_is_ssh_blocked_returned_false(self, mock_netcat): + """ + Checks the proper funcitonality of is_ssh_blocked + function when the returncode is zero + """ + + dest_ip = 'mock_destination_ip' + nc_calls = [call('10.10.10.10', + 'mock_destination_ip', + destination_port='22', + source_port=None)] + + mock_netcat.return_value = 0 + self.assertFalse(test_utils.is_ssh_blocked(self.ip, dest_ip)) + mock_netcat.assert_has_calls(nc_calls) + + @patch('sfc.lib.test_utils.netcat', autospec=True) + def test_is_ssh_blocked_returned_true(self, mock_netcat): + """ + Checks the proper funcitonality of is_ssh_blocked + function when the returncode is non-zero integer + """ + + dest_ip = 'mock_destination_ip' + nc_calls = [call('10.10.10.10', + 'mock_destination_ip', + destination_port='22', + source_port=None)] + + mock_netcat.return_value = 1 + self.assertTrue(test_utils.is_ssh_blocked(self.ip, dest_ip)) + mock_netcat.assert_has_calls(nc_calls) + + @patch('sfc.lib.test_utils.netcat', autospec=True) + def test_is_http_blocked_returned_false(self, mock_netcat): + """ + Checks the proper funcitonality of is_http_blocked + function when the returncode is zero + """ + + dest_ip = 'mock_destination_ip' + nc_calls = [call('10.10.10.10', + 'mock_destination_ip', + destination_port='80', + source_port=None)] + + mock_netcat.return_value = 0 + self.assertFalse(test_utils.is_http_blocked(self.ip, dest_ip)) + mock_netcat.assert_has_calls(nc_calls) + + @patch('sfc.lib.test_utils.netcat', autospec=True) + def test_is_http_blocked_returned_true(self, mock_netcat): + """ + Checks the proper funcitonality of is_http_blocked + function when the returncode is non-zero integer + """ + + dest_ip = 'mock_destination_ip' + nc_calls = [call('10.10.10.10', + 'mock_destination_ip', + destination_port='80', + source_port=None)] + + mock_netcat.return_value = 1 + self.assertTrue(test_utils.is_http_blocked(self.ip, dest_ip)) + mock_netcat.assert_has_calls(nc_calls) + + @patch('time.strftime', autospec=True) + @patch('opnfv.utils.ovs_logger.OVSLogger', autospec=True) + def test_capture_ovs_logs(self, + mock_ovs_log, + mock_strftime): + """ + Checks the proper functionality of capture_ovs_logs + function + """ + + log_calls = [call('controller_clients', + 'compute_clients', + 'error', + 'date_time')] + + mock_strftime.return_value = 'date_time' + test_utils.capture_ovs_logs(mock_ovs_log, + 'controller_clients', + 'compute_clients', + 'error') + + mock_strftime.assert_called_once_with('%Y%m%d-%H%M%S') + mock_ovs_log.dump_ovs_logs.assert_has_calls(log_calls) + + def test_get_ssh_clients(self): + """ + Checks the proper functionality of get_ssh_clients + fucntion + """ + + mock_node_obj_one = Mock() + mock_node_obj_two = Mock() + mock_node_obj_one.ssh_client = 'ssh_client_one' + mock_node_obj_two.ssh_client = 'ssh_client_two' + nodes = [mock_node_obj_one, mock_node_obj_two] + result = test_utils.get_ssh_clients(nodes) + self.assertEqual(result, ['ssh_client_one', 'ssh_client_two']) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_check_ssh_returned_false(self, + mock_run_cmd_remote, + mock_log, + mock_sleep): + """ + Checks the proper functionality of check_ssh + fucntion when few VNFs can't establish SSH connectivity + """ + + ips = ["ip_address-1", + "ip_address-2"] + + rcr_calls = [call(ips[0], 'exit'), + call(ips[1], 'exit')] + + log_calls = [call('Checking SSH connectivity ' + + 'to the SFs with ips %s' % str(ips))] + + mock_run_cmd_remote.side_effect = [(1, '', ''), + (0, '', '')] + + self.assertFalse(test_utils.check_ssh(ips, retries=1)) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_log.info.assert_has_calls(log_calls) + mock_sleep.assert_called_once_with(3) + + @patch('time.sleep', autospec=True) + @patch('sfc.lib.test_utils.logger', autospec=True) + @patch('sfc.lib.test_utils.run_cmd_remote', autospec=True) + def test_check_ssh_returned_true(self, + mock_run_cmd_remote, + mock_log, + mock_sleep): + """ + Checks the proper functionality of check_ssh + fucntion when all VNFs can establish SSH connectivity + """ + + ips = ["ip_address-1", + "ip_address-2"] + + rcr_calls = [call(ips[0], 'exit'), + call(ips[1], 'exit')] + + log_calls = [call('Checking SSH connectivity to ' + + 'the SFs with ips %s' % str(ips)), + call('SSH connectivity to the SFs established')] + + mock_run_cmd_remote.side_effect = [(0, '', ''), + (0, '', '')] + + self.assertTrue(test_utils.check_ssh(ips, retries=1)) + mock_run_cmd_remote.assert_has_calls(rcr_calls) + mock_log.info.assert_has_calls(log_calls) + mock_sleep.assert_not_called() + + def test_fill_installer_dict(self): + """ + Checks the proper functionality of fill_installer_dict + function + """ + + installer_type = 'mock_installer' + installer_yaml_fields = { + 'user': 'defaults.installer.mock_installer.user', + 'password': 'defaults.installer.mock_installer.password', + 'cluster': 'defaults.installer.mock_installer.cluster', + 'pkey_file': 'defaults.installer.mock_installer.pkey_file' + } + result = test_utils.fill_installer_dict(installer_type) + self.assertDictEqual(result, installer_yaml_fields) |