diff options
Diffstat (limited to 'tests/unit')
-rw-r--r-- | tests/unit/test_daisy_server.py | 324 | ||||
-rw-r--r-- | tests/unit/test_deploy.py | 67 | ||||
-rw-r--r-- | tests/unit/test_environment.py | 288 | ||||
-rw-r--r-- | tests/unit/test_utils.py | 180 |
4 files changed, 784 insertions, 75 deletions
diff --git a/tests/unit/test_daisy_server.py b/tests/unit/test_daisy_server.py index ada3c96d..97e0a1e2 100644 --- a/tests/unit/test_daisy_server.py +++ b/tests/unit/test_daisy_server.py @@ -1,18 +1,81 @@ import os import pytest import mock +import paramiko from deploy import daisy_server from deploy.daisy_server import ( - DaisyServer + DaisyServer, + log_from_stream, + log_scp ) +from deploy.utils import WORKSPACE -@pytest.fixture(scope="session") +@pytest.fixture(scope="module") def conf_file_dir(data_root): return os.path.join(data_root, 'lab_conf') +@pytest.fixture(scope="module") +def common_test_file_dir(data_root): + return os.path.join(data_root, 'common') + + +def ssh_test_file_dir(): + return os.path.join(WORKSPACE, 'tests/data/common') + + +def get_ssh_test_command_from_file(dir, file_name): + file_path = os.path.join(dir, file_name) + with open(file_path) as f: + return f.read() + + +data1 = get_ssh_test_command_from_file(ssh_test_file_dir(), 'ssh_stream_data1.txt') +res1 = None +expected_ret1 = None +res2 = 'test_res_commd' +data2 = get_ssh_test_command_from_file(ssh_test_file_dir(), 'ssh_stream_data2.txt') +expected_ret2 = 'test_ssh_cmd3' +data3 = get_ssh_test_command_from_file(ssh_test_file_dir(), 'ssh_stream_data3.txt') + + +@pytest.mark.parametrize('data, res, expected', [ + (data1, res1, expected_ret1), + (data1, res2, expected_ret1), + (data2, res1, expected_ret2), + (data2, res2, expected_ret2), + (data3, res1, expected_ret1), + (data3, res2, expected_ret1)]) +def test_log_from_stream(data, res, expected): + def log_func(str): + print str + pre_val = daisy_server.BLOCK_SIZE + daisy_server.BLOCK_SIZE = 16 + ret = log_from_stream(res, data, log_func) + daisy_server.BLOCK_SIZE = pre_val + assert expected == ret + + +@pytest.mark.parametrize('filename, size, send', [ + ('test_file_name', 1024, 1000), + ('test_file_name', 2048, 2048), + ('test_file_name_1234', 2097152, 2097152)]) +@mock.patch('deploy.daisy_server.LD') +def test_log_scp(mock_LD, filename, size, send): + pre_val = daisy_server.LEN_OF_NAME_PART + daisy_server.LEN_OF_NAME_PART = 24 + log_scp(filename, size, send) + daisy_server.LEN_OF_NAME_PART = pre_val + if size != send: + mock_LD.assert_not_called() + elif len(filename) <= 18: + mock_LD.assert_called_once() + else: + assert mock_LD.call_count == 2 + + daisy_server_info = { 'name': 'daisy', 'image': 'daisy.qcow2', @@ -53,6 +116,239 @@ def test_create_DaisyServer_instance(tmpdir): tmpdir.remove() +@mock.patch.object(daisy_server.paramiko.SSHClient, 'connect') +@mock.patch.object(daisy_server.DaisyServer, 'ssh_run') +def test_connect_DaisyServer(mock_ssh_run, mock_connect, tmpdir): + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + mock_connect.return_value = 0 + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.connect() + mock_ssh_run.assert_called_once_with('ls -al', check=True) + tmpdir.remove() + + +@mock.patch.object(daisy_server.paramiko.SSHClient, 'close') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'connect') +@mock.patch.object(daisy_server.DaisyServer, 'ssh_run') +def test_close_DaisyServer(mock_ssh_run, mock_connect, + mock_close, tmpdir): + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + mock_connect.return_value = 0 + mock_ssh_run.return_valule = 0 + mock_close.return_value = 0 + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.connect() + DaisyServerInst.close() + mock_close.assert_called_once_with() + tmpdir.remove() + + +stdout1 = open(os.path.join(ssh_test_file_dir(), 'sim_stdout_file')) +stdin1 = open(os.path.join(ssh_test_file_dir(), 'sim_stdout_file')) +stderr1 = open(os.path.join(ssh_test_file_dir(), 'sim_stderr_file')) +stderr2 = open(os.path.join(ssh_test_file_dir(), 'sim_stdout_file')) + + +@pytest.mark.parametrize('stdout, stdin, stderr', [ + (stdout1, stdin1, stderr1), + (stdout1, stdin1, stderr2)]) +@mock.patch('deploy.daisy_server.err_exit') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'connect') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'exec_command') +@mock.patch.object(daisy_server.DaisyServer, 'ssh_run') +def test_ssh_exec_cmd_DaisyServer(mock_ssh_run, mock_exec_command, + mock_connect, mock_err_exit, + stdout, stdin, stderr, tmpdir): + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + cmd = 'ls -l' + mock_connect.return_value = 0 + mock_ssh_run.return_valule = 0 + expect = 'stdout file data' + mock_exec_command.return_value = (stdin, stdout, stderr) + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.connect() + ret = DaisyServerInst.ssh_exec_cmd(cmd) + mock_exec_command.assert_called_once() + if stderr == stderr1: + if stdout == stdout1: + assert ret == expect + elif stderr == stderr2: + mock_err_exit.assert_called_once_with('SSH client error occurred') + tmpdir.remove() + + +@pytest.mark.parametrize('check, is_recv_exit_status, expect', [ + (False, 0, 0), + (True, 1, 1)]) +@mock.patch('deploy.daisy_server.err_exit') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'get_transport') +def test_ssh_run_DaisyServer(mock_get_transport, mock_err_exit, + check, is_recv_exit_status, + expect, tmpdir): + class TestSession(): + + def __init__(self, is_recv_exit_status): + self.recv_data = 'recv_test_data' + self.recv_data_total_len = len(self.recv_data) + self.recv_data_read_index = 0 + self.recv_err_data = 'recv_test_err_data' + self.recv_err_data_total_len = len(self.recv_err_data) + self.recv_err_data_read_index = 0 + self.is_recv_exit_status = is_recv_exit_status + return None + + def exec_command(self, cmd): + return 0 + + def recv_ready(self): + return True + + def recv(self, size): + if self.recv_data_read_index < self.recv_data_total_len: + if size <= self.recv_data_total_len - self.recv_data_read_index: + cur_index = self.recv_data_read_index + self.recv_data_read_index += size + return self.recv_data[cur_index:self.recv_data_read_index] + else: + cur_index = self.recv_data_read_index + self.recv_data_read_index = self.recv_data_total_len + return self.recv_data[cur_index:] + else: + return None + + def recv_stderr_ready(self): + return True + + def recv_stderr(self, size): + if self.recv_err_data_read_index < self.recv_err_data_total_len: + if size <= self.recv_err_data_total_len - self.recv_err_data_read_index: + cur_index = self.recv_err_data_read_index + self.recv_err_data_read_index += size + return self.recv_err_data[cur_index:self.recv_err_data_read_index] + else: + cur_index = self.recv_err_data_read_index + self.recv_err_data_read_index = self.recv_err_data_total_len + return self.recv_err_data[cur_index:] + else: + return None + + def exit_status_ready(self): + return True + + def recv_exit_status(self): + return self.is_recv_exit_status + + class TestTransport(): + def __init__(self, is_recv_exit_status): + self.is_recv_exit_status = is_recv_exit_status + + def set_keepalive(self, time): + self.time = time + + def open_session(self): + return TestSession(is_recv_exit_status) + + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + cmd = 'ls -l' + mock_get_transport.return_value = TestTransport(is_recv_exit_status) + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.ssh_client = paramiko.SSHClient() + DaisyServerInst.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ret = DaisyServerInst.ssh_run(cmd, check=check) + if check and is_recv_exit_status: + mock_err_exit.assert_called_once() + assert ret == expect + tmpdir.remove() + + +@mock.patch.object(daisy_server.scp.SCPClient, 'get') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'get_transport') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'connect') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'exec_command') +@mock.patch.object(daisy_server.DaisyServer, 'ssh_run') +def test_scp_get_DaisyServer(mock_ssh_run, mock_exec_command, + mock_connect, mock_get_transport, + mock_get, tmpdir): + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + mock_connect.return_value = 0 + mock_ssh_run.return_valule = 0 + remote = '/remote_dir' + local = '.' + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.connect() + DaisyServerInst.scp_get(remote, local) + mock_get.assert_called_once_with(remote, local_path=local, recursive=True) + tmpdir.remove() + + +@mock.patch.object(daisy_server.scp.SCPClient, 'put') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'get_transport') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'connect') +@mock.patch.object(daisy_server.paramiko.SSHClient, 'exec_command') +@mock.patch.object(daisy_server.DaisyServer, 'ssh_run') +def test_scp_put_DaisyServer(mock_ssh_run, mock_exec_command, + mock_connect, mock_get_transport, + mock_put, tmpdir): + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) + mock_connect.return_value = 0 + mock_ssh_run.return_valule = 0 + remote = '.' + local = '/tmp' + DaisyServerInst = DaisyServer(daisy_server_info['name'], + daisy_server_info['address'], + daisy_server_info['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + DaisyServerInst.connect() + DaisyServerInst.scp_put(local, remote) + mock_put.assert_called_once_with(local, remote_path=remote, recursive=True) + tmpdir.remove() + + @mock.patch.object(daisy_server.DaisyServer, 'ssh_exec_cmd') def test_create_dir_DaisyServer(mock_ssh_exec_cmd, tmpdir): remote_dir_test = '/home/daisy/test' @@ -91,6 +387,13 @@ def test_delete_dir_DaisyServer(mock_ssh_exec_cmd, tmpdir): tmpdir.remove() +bin_file_path1 = os.path.join('/tmp', bin_file_name) +bin_file_path2 = os.path.join(WORKSPACE, bin_file_name) + + +@pytest.mark.parametrize('bin_file', [ + (bin_file_path1), + (bin_file_path2)]) @mock.patch.object(daisy_server.DaisyServer, 'delete_dir') @mock.patch.object(daisy_server.DaisyServer, 'scp_put') @mock.patch.object(daisy_server.DaisyServer, 'create_dir') @@ -99,8 +402,8 @@ def test_prepare_files_DaisyServer(mock_update_config, mock_create_dir, mock_scp_put, mock_delete_dir, + bin_file, tmpdir): - bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) DaisyServerInst = DaisyServer(daisy_server_info['name'], daisy_server_info['address'], daisy_server_info['password'], @@ -113,7 +416,10 @@ def test_prepare_files_DaisyServer(mock_update_config, DaisyServerInst.prepare_files() DaisyServerInst.delete_dir.assert_called_once_with(remote_dir) DaisyServerInst.create_dir.assert_called_once_with('/home/daisy_install') - assert DaisyServerInst.scp_put.call_count == 3 + if bin_file == bin_file_path1: + assert DaisyServerInst.scp_put.call_count == 3 + else: + assert DaisyServerInst.scp_put.call_count == 2 tmpdir.remove() @@ -138,10 +444,11 @@ def test_install_daisy_DaisyServer(mock_prepare_files, mock_ssh_run, tmpdir): tmpdir.remove() +@pytest.mark.parametrize('adapter', [ + ('libvirt'), ('ipmi')]) @mock.patch.object(daisy_server.DaisyServer, 'ssh_run') -def test_prepare_configurations_DaisyServer(mock_ssh_run, tmpdir): +def test_prepare_configurations_DaisyServer(mock_ssh_run, adapter, tmpdir): bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, bin_file_name) - adapter = 'libvirt' DaisyServerInst = DaisyServer(daisy_server_info['name'], daisy_server_info['address'], daisy_server_info['password'], @@ -158,7 +465,10 @@ def test_prepare_configurations_DaisyServer(mock_ssh_run, tmpdir): net_file=os.path.join(remote_dir, net_file_name), is_bare=1 if adapter == 'ipmi' else 0) DaisyServerInst.prepare_configurations() - DaisyServerInst.ssh_run.assert_called_once_with(cmd) + if adapter == 'libvirt': + DaisyServerInst.ssh_run.assert_called_once_with(cmd) + else: + DaisyServerInst.ssh_run.assert_not_called() tmpdir.remove() diff --git a/tests/unit/test_deploy.py b/tests/unit/test_deploy.py index 0c4ebb6f..142a7f45 100644 --- a/tests/unit/test_deploy.py +++ b/tests/unit/test_deploy.py @@ -16,7 +16,7 @@ from deploy.utils import WORKSPACE import mock sys.modules['libvirt'] = mock.Mock() -from deploy import environment # noqa: ignore=E402 +from deploy import environment # noqa: ignore=E402 from deploy.deploy import ( config_arg_parser, DaisyDeployment, @@ -128,6 +128,66 @@ def test_create_DaisyDeployment_instance(mock_err_exit, mock_deploy_schema_valid tmpdir.remove() +@pytest.mark.parametrize('kwargs, is_use_pdf, exp_deploy_file', [ + ( + { + 'lab_name': 'zte', + 'pod_name': 'virtual1', + 'deploy_file': 'deploy_virtual1.yml', + 'net_file': 'network_virtual1.yml', + 'bin_file': 'opnfv.bin', + 'daisy_only': False, + 'cleanup_only': False, + 'remote_dir': '/home/daisy', + 'work_dir': 'workdir', + 'storage_dir': 'vms', + 'pxe_bridge': 'libvirt', + 'deploy_log': 'deploy.log', + 'scenario': 'os-nosdn-nofeature-ha' + }, False, 'deploy_vir1_nosdn-ha.yml' + ), + ( + { + 'lab_name': 'zte', + 'pod_name': 'pod1', + 'deploy_file': 'deploy_baremetal.yml', + 'net_file': 'network_baremetal.yml', + 'bin_file': 'opnfv.bin', + 'daisy_only': False, + 'cleanup_only': False, + 'remote_dir': '/home/daisy', + 'work_dir': 'workdir', + 'storage_dir': 'vms', + 'pxe_bridge': 'pxebr', + 'deploy_log': 'deploy.log', + 'scenario': 'os-odl-nofeature-ha' + }, True, 'deploy_bare_odl-ha.yml' + )]) +@mock.patch.object(DaisyDeployment, '_use_pod_descriptor_file') +def test__construct_final_deploy_conf_in_DaisyDeployment(mock__use_pod_descriptor_file, + conf_file_dir, tmpdir, + kwargs, is_use_pdf, exp_deploy_file): + kwargs['deploy_file'] = os.path.join(conf_file_dir, kwargs['deploy_file']) + kwargs['net_file'] = os.path.join(conf_file_dir, kwargs['net_file']) + tmpdir.join(kwargs['bin_file']).write('testdata') + kwargs['bin_file'] = os.path.join(tmpdir.dirname, tmpdir.basename, kwargs['bin_file']) + kwargs['deploy_log'] = os.path.join(tmpdir.dirname, tmpdir.basename, kwargs['deploy_log']) + tmpsubdir = tmpdir.mkdir(kwargs['work_dir']) + kwargs['work_dir'] = os.path.join(tmpsubdir.dirname, tmpsubdir.basename) + tmpsubdir = tmpdir.mkdir(kwargs['storage_dir']) + kwargs['storage_dir'] = os.path.join(tmpsubdir.dirname, tmpsubdir.basename) + exp_deploy_file_path = os.path.join(conf_file_dir, exp_deploy_file) + pdf_deploy_file = None if not is_use_pdf else kwargs['deploy_file'] + + mock__use_pod_descriptor_file.return_value = pdf_deploy_file + daisy_deploy = DaisyDeployment(**kwargs) + mock__use_pod_descriptor_file.asser_called_once_with() + with open(exp_deploy_file_path) as yaml_file: + exp_deploy_struct = yaml.safe_load(yaml_file) + assert daisy_deploy.deploy_struct == exp_deploy_struct + tmpdir.remove() + + @pytest.mark.parametrize('kwargs', [ ( { @@ -213,6 +273,8 @@ def test_run_in_DaisyDeployment(mock_deploy, mock_install_daisy, tmpdir.remove() +@pytest.mark.parametrize('cleanup_only', [ + (False), (True)]) @mock.patch('deploy.deploy.argparse.ArgumentParser.parse_args') @mock.patch('deploy.deploy.check_sudo_privilege') @mock.patch('deploy.deploy.save_log_to_file') @@ -223,7 +285,7 @@ def test_run_in_DaisyDeployment(mock_deploy, mock_install_daisy, def test_parse_arguments(mock_confirm_dir_exists, mock_make_file_executable, mock_check_file_exists, mock_check_scenario_valid, mock_save_log_to_file, mock_check_sudo_privilege, - mock_parse_args, tmpdir): + mock_parse_args, cleanup_only, tmpdir): class MockArg(): def __init__(self, lab_name, pod_name, bin_file, daisy_only, cleanup_only, remote_dir, work_dir, storage_dir, pxe_bridge, @@ -245,7 +307,6 @@ def test_parse_arguments(mock_confirm_dir_exists, mock_make_file_executable, conf_base_dir = os.path.join(WORKSPACE, 'labs', 'zte', 'pod2') deploy_file = os.path.join(conf_base_dir, 'daisy/config/deploy.yml') net_file = os.path.join(conf_base_dir, 'daisy/config/network.yml') - cleanup_only = False expected = { 'lab_name': 'zte', 'pod_name': 'pod2', diff --git a/tests/unit/test_environment.py b/tests/unit/test_environment.py index 250a80a9..dcd8b046 100644 --- a/tests/unit/test_environment.py +++ b/tests/unit/test_environment.py @@ -2,10 +2,13 @@ import os import copy import mock import yaml +import pytest from deepdiff import DeepDiff from deploy.utils import WORKSPACE from deploy import environment +from deploy import daisy_server +from deploy.daisy_server import DaisyServer from deploy.environment import ( DaisyEnvironmentBase, BareMetalEnvironment, @@ -29,6 +32,7 @@ def get_conf_info_from_file(file_dir, conf_file_name): deploy_struct = get_conf_info_from_file(get_conf_file_dir(), 'deploy_baremetal.yml') deploy_virtual_struct = get_conf_info_from_file(get_conf_file_dir(), 'deploy_virtual1.yml') +deploy_virtual2_struct = get_conf_info_from_file(get_conf_file_dir(), 'deploy_virtual2.yml') net_struct = get_conf_info_from_file(get_conf_file_dir(), 'network_baremetal.yml') adapter = 'ipmi' adapter_virtual = 'libvirt' @@ -80,7 +84,18 @@ def test_delete_daisy_server_DaisyEnvironmentBase(tmpdir, mocker): tmpdir.remove() -def test_create_daisy_server_image_DaisyEnvironmentBase(tmpdir, monkeypatch): +@pytest.mark.parametrize('ret_run_shell, ret_access', [ + (0, 1), + (1, 0)]) +@mock.patch('deploy.environment.os.access') +@mock.patch('deploy.environment.os.remove') +@mock.patch('deploy.environment.shutil.move') +@mock.patch('deploy.environment.err_exit') +@mock.patch('deploy.environment.run_shell') +def test_create_daisy_server_image_DaisyEnvironmentBase(mock_run_shell, mock_err_exit, + mock_move, mock_remove, + mock_access, tmpdir, + ret_run_shell, ret_access): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) os.makedirs(work_dir, 0755) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) @@ -90,15 +105,40 @@ def test_create_daisy_server_image_DaisyEnvironmentBase(tmpdir, monkeypatch): DaisyEnvBaseInst = DaisyEnvironmentBase( deploy_struct, net_struct, adapter, pxe_bridge, daisy_server, work_dir, storage_dir, scenario) + mock_run_shell.return_value = ret_run_shell + mock_access.return_value = ret_access + mock_err_exit.return_value = 0 - def create_server_image_sucess(cmd): - os.makedirs(os.path.join(work_dir, 'daisy')) - with open(os.path.join(work_dir, 'daisy', 'centos7.qcow2'), 'w') as f: - f.write('image-data') - return 0 - monkeypatch.setattr(environment, 'run_shell', create_server_image_sucess) DaisyEnvBaseInst.create_daisy_server_image() - assert os.path.isfile(DaisyEnvBaseInst.daisy_server_info['image']) + if ret_run_shell: + mock_err_exit.assert_called_once_with('Failed to create Daisy Server image') + else: + if ret_access: + mock_remove.assert_called_once_with(DaisyEnvBaseInst.daisy_server_info['image']) + else: + mock_move.assert_called_once() + tmpdir.remove() + + +@mock.patch.object(daisy_server.DaisyServer, 'connect') +@mock.patch.object(daisy_server.DaisyServer, 'install_daisy') +def test_install_daisy_DaisyEnvironmentBase(mock_install_daisy, mock_connect, tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + os.makedirs(work_dir, 0755) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + os.makedirs(storage_dir, 0755) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + remote_dir = '/home/daisy' + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, 'opnfv.bin') + deploy_file_name = 'final_deploy.yml' + net_file_name = 'network_baremetal.yml' + DaisyEnvBaseInst = DaisyEnvironmentBase( + deploy_struct, net_struct, adapter, pxe_bridge, + daisy_server, work_dir, storage_dir, scenario) + DaisyEnvBaseInst.install_daisy(remote_dir, bin_file, deploy_file_name, net_file_name) + mock_install_daisy.assert_called_once_with() + mock_connect.assert_called_once_with() tmpdir.remove() @@ -153,18 +193,36 @@ def test_create_daisy_server_vm_BareMetalEnvironment(mocker, tmpdir): tmpdir.remove() +@pytest.mark.parametrize('deploy_struct_info', [ + (deploy_struct)]) @mock.patch('deploy.environment.ipmi_reboot_node') -def test_reboot_nodes_BareMetalEnvironment(mock_ipmi_reboot_node, tmpdir): +def test_reboot_nodes_BareMetalEnvironment(mock_ipmi_reboot_node, + deploy_struct_info, tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) daisy_server = copy.deepcopy(daisy_server_info) daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) mock_ipmi_reboot_node.return_value = True BareMetalEnvironmentInst = BareMetalEnvironment( - deploy_struct, net_struct, adapter, pxe_bridge, + deploy_struct_info, net_struct, adapter, pxe_bridge, daisy_server, work_dir, storage_dir, scenario) BareMetalEnvironmentInst.reboot_nodes() - assert environment.ipmi_reboot_node.call_count == 5 + assert mock_ipmi_reboot_node.call_count == 5 + tmpdir.remove() + + +@pytest.mark.parametrize('deploy_struct_info', [ + (deploy_virtual_struct)]) +def test_reboot_nodes_err_BareMetalEnvironment(deploy_struct_info, tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + BareMetalEnvironmentInst = BareMetalEnvironment( + deploy_struct_info, net_struct, adapter, pxe_bridge, + daisy_server, work_dir, storage_dir, scenario) + with pytest.raises(SystemExit): + BareMetalEnvironmentInst.reboot_nodes() tmpdir.remove() @@ -186,6 +244,49 @@ def test_create_daisy_server_BareMetalEnvironment(mock_create_daisy_server_vm, m tmpdir.remove() +@mock.patch.object(daisy_server.DaisyServer, 'prepare_cluster') +@mock.patch.object(environment.BareMetalEnvironment, 'reboot_nodes') +@mock.patch.object(daisy_server.DaisyServer, 'prepare_host_and_pxe') +@mock.patch.object(daisy_server.DaisyServer, 'check_os_installation') +@mock.patch.object(daisy_server.DaisyServer, 'check_openstack_installation') +@mock.patch.object(daisy_server.DaisyServer, 'post_deploy') +def test_deploy_BareMetalEnvironment(mock_post_deploy, mock_check_openstack_installation, + mock_check_os_installation, mock_prepare_host_and_pxe, + mock_reboot_nodes, mock_prepare_cluster, + tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + deploy_file = os.path.join(get_conf_file_dir(), 'deploy_baremetal.yml') + net_file = os.path.join(get_conf_file_dir(), 'network_baremetal.yml') + remote_dir = '/home/daisy' + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, 'opnfv.bin') + deploy_file_name = 'final_deploy.yml' + net_file_name = 'network_baremetal.yml' + BareMetalEnvironmentInst = BareMetalEnvironment( + deploy_struct, net_struct, adapter, pxe_bridge, + daisy_server, work_dir, storage_dir, scenario) + BareMetalEnvironmentInst.server = DaisyServer( + daisy_server['name'], + daisy_server['address'], + daisy_server['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + BareMetalEnvironmentInst.deploy(deploy_file, net_file) + mock_prepare_cluster.assert_called_once_with(deploy_file, net_file) + mock_reboot_nodes.assert_called_once_with(boot_dev='pxe') + mock_prepare_host_and_pxe.assert_called_once_with() + mock_check_os_installation.assert_called_once_with(len(BareMetalEnvironmentInst.deploy_struct['hosts'])) + mock_check_openstack_installation.assert_called_once_with(len(BareMetalEnvironmentInst.deploy_struct['hosts'])) + mock_post_deploy.assert_called_once_with() + tmpdir.remove() + + def test_create_VirtualEnvironment_instance(tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) @@ -208,19 +309,67 @@ def test_create_VirtualEnvironment_instance(tmpdir): tmpdir.remove() +@mock.patch.object(environment.VirtualEnvironment, 'check_nodes_template') +def test_check_configuration_VirtualEnvironment(mock_check_nodes_template, tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + VirtualEnvironment( + deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, + daisy_server, work_dir, storage_dir, scenario) + mock_check_nodes_template.assert_called_once_with() + tmpdir.remove() + + +deploy_virtual_invalid_struct = get_conf_info_from_file(get_conf_file_dir(), 'deploy_virtual_invalid_template.yml') + + +@pytest.mark.parametrize('deploy_struct_info', [ + (deploy_struct), + (deploy_virtual_struct), + (deploy_virtual_invalid_struct)]) +@mock.patch('deploy.environment.err_exit') +def test_check_nodes_template_VirtualEnvironment(mock_err_exit, deploy_struct_info, tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + mock_err_exit.return_value = 0 + VirtualEnvironment( + deploy_struct_info, net_struct, adapter_virtual, pxe_bridge_virtual, + daisy_server, work_dir, storage_dir, scenario) + if deploy_struct_info == deploy_struct: + mock_err_exit.assert_not_called() + elif deploy_struct_info == deploy_virtual_struct: + mock_err_exit.assert_not_called() + elif deploy_struct_info == deploy_virtual_invalid_struct: + assert mock_err_exit.call_count == 5 + tmpdir.remove() + + +@pytest.mark.parametrize('net_name', [ + (pxe_bridge_virtual)]) @mock.patch('deploy.environment.create_virtual_network') -def test_create_daisy_server_network_VirtualEnvironment(mock_create_virtual_network, tmpdir): +@mock.patch('deploy.environment.err_exit') +def test_create_daisy_server_network_VirtualEnvironment(mock_err_exit, mock_create_virtual_network, + net_name, tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) daisy_server = copy.deepcopy(daisy_server_info) daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) - mock_create_virtual_network.return_value = pxe_bridge_virtual + mock_create_virtual_network.return_value = net_name + mock_err_exit.return_value = 0 VirtualEnvironmentInst = VirtualEnvironment( deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, daisy_server, work_dir, storage_dir, scenario) VirtualEnvironmentInst.create_daisy_server_network() - environment.create_virtual_network.assert_called_once_with(VMDEPLOY_DAISY_SERVER_NET) - assert VirtualEnvironmentInst._daisy_server_net == pxe_bridge_virtual + mock_create_virtual_network.assert_called_once_with(VMDEPLOY_DAISY_SERVER_NET) + if net_name == pxe_bridge_virtual: + mock_err_exit.assert_not_called() + assert VirtualEnvironmentInst._daisy_server_net == pxe_bridge_virtual + elif net_name == pxe_bridge: + mock_err_exit.assert_called_once() tmpdir.remove() @@ -238,6 +387,7 @@ def test_create_daisy_server_vm_VirtualEnvironment(mock_create_vm, tmpdir): environment.create_vm.assert_called_once_with(VMDEPLOY_DAISY_SERVER_VM, name=daisy_server['name'], disks=[daisy_server['image']]) + tmpdir.remove() @mock.patch.object(environment.DaisyEnvironmentBase, 'create_daisy_server_image') @@ -261,23 +411,30 @@ def test_create_daisy_server_VirtualEnvironment(mock_create_daisy_server_vm, tmpdir.remove() +@pytest.mark.parametrize('deploy_info_struct, node_num', [ + (deploy_virtual_struct, 0), + (deploy_virtual_struct, 3), + (deploy_virtual2_struct, 0)]) @mock.patch('deploy.environment.create_vm') @mock.patch('deploy.environment.create_virtual_disk') -def test_create_virtual_node_VirtualEnvironment(mock_create_virtual_disk, - mock_create_vm, - tmpdir): +def test_create_virtual_node_VirtualEnvironment(mock_create_virtual_disk, mock_create_vm, + deploy_info_struct, node_num, tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) daisy_server = copy.deepcopy(daisy_server_info) daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) VirtualEnvironmentInst = VirtualEnvironment( - deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, + deploy_info_struct, net_struct, adapter_virtual, pxe_bridge_virtual, daisy_server, work_dir, storage_dir, scenario) - VirtualEnvironmentInst.create_virtual_node(deploy_virtual_struct['hosts'][0]) - environment.create_virtual_disk.call_count == 2 - name = deploy_virtual_struct['hosts'][0]['name'] - template = deploy_virtual_struct['hosts'][0]['template'] - files = [os.path.join(storage_dir, name + '.qcow2'), os.path.join(storage_dir, name + '_data.qcow2')] + VirtualEnvironmentInst.create_virtual_node(deploy_info_struct['hosts'][node_num]) + name = deploy_info_struct['hosts'][node_num]['name'] + template = deploy_info_struct['hosts'][node_num]['template'] + if deploy_info_struct == deploy_virtual_struct: + files = [os.path.join(storage_dir, name + '.qcow2'), os.path.join(storage_dir, name + '_data.qcow2')] + assert environment.create_virtual_disk.call_count == 2 + elif deploy_info_struct == deploy_virtual2_struct: + files = [os.path.join(storage_dir, name + '.qcow2')] + assert environment.create_virtual_disk.call_count == 1 environment.create_vm.assert_called_once_with(template, name, files) tmpdir.remove() @@ -321,6 +478,7 @@ def test_delete_nodes_VirtualEnvironment(mock_delete_vm_and_disk, tmpdir): daisy_server, work_dir, storage_dir, scenario) VirtualEnvironmentInst.delete_nodes() assert environment.delete_vm_and_disk.call_count == 5 + tmpdir.remove() @mock.patch('deploy.environment.reboot_vm') @@ -334,19 +492,29 @@ def test_reboot_nodes_VirtualEnvironment(mock_reboot_vm, tmpdir): daisy_server, work_dir, storage_dir, scenario) VirtualEnvironmentInst.reboot_nodes() assert environment.reboot_vm.call_count == 5 + tmpdir.remove() +@pytest.mark.parametrize('isdir', [ + (True), + (False)]) @mock.patch('deploy.environment.delete_virtual_network') -def test_delete_networks_VirtualEnvironment(mock_delete_virtual_network, tmpdir): +@mock.patch('deploy.environment.os.path.isdir') +def test_delete_networks_VirtualEnvironment(mock_isdir, mock_delete_virtual_network, isdir, tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) daisy_server = copy.deepcopy(daisy_server_info) daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + mock_isdir.return_value = isdir VirtualEnvironmentInst = VirtualEnvironment( deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, daisy_server, work_dir, storage_dir, scenario) VirtualEnvironmentInst.delete_networks() - assert environment.delete_virtual_network.call_count == 3 + if isdir is True: + assert mock_delete_virtual_network.call_count == 3 + else: + mock_delete_virtual_network.assert_not_called() + tmpdir.remove() @mock.patch.object(environment.DaisyEnvironmentBase, 'delete_daisy_server') @@ -370,13 +538,72 @@ def test_delete_old_environment_VirtualEnvironment(mock_delete_daisy_server, tmpdir.remove() +@mock.patch.object(environment.DaisyServer, 'post_deploy') +@mock.patch.object(environment.DaisyServer, 'check_openstack_installation') +@mock.patch.object(environment.DaisyServer, 'check_os_installation') +@mock.patch.object(environment.DaisyServer, 'install_virtual_nodes') +@mock.patch.object(environment.DaisyServer, 'prepare_host_and_pxe') +@mock.patch.object(environment.DaisyServer, 'copy_new_deploy_config') +@mock.patch.object(environment.DaisyServer, 'prepare_cluster') +@mock.patch.object(environment.VirtualEnvironment, '_post_deploy') +@mock.patch.object(environment.VirtualEnvironment, 'reboot_nodes') +@mock.patch.object(environment.VirtualEnvironment, 'create_nodes') +def test_deploy_VirtualEnvironment(mock_create_nodes, mock_reboot_nodes, + mock__post_deploy, mock_prepare_cluster, + mock_copy_new_deploy_config, mock_prepare_host_and_pxe, + mock_install_virtual_nodes, mock_check_os_installation, + mock_check_openstack_installation, mock_post_deploy, + tmpdir): + work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) + storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) + daisy_server = copy.deepcopy(daisy_server_info) + daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) + remote_dir = '/home/daisy' + bin_file = os.path.join(tmpdir.dirname, tmpdir.basename, 'opnfv.bin') + deploy_file_name = 'final_deploy.yml' + net_file_name = 'network_virtual1.yml' + deploy_file = os.path.join(get_conf_file_dir(), 'deploy_virtual1.yml') + net_file = os.path.join(get_conf_file_dir(), 'network_virtual1.yml') + VirtualEnvironmentInst = VirtualEnvironment( + deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, + daisy_server, work_dir, storage_dir, scenario) + VirtualEnvironmentInst.server = DaisyServer( + daisy_server['name'], + daisy_server['address'], + daisy_server['password'], + remote_dir, + bin_file, + adapter, + scenario, + deploy_file_name, + net_file_name) + VirtualEnvironmentInst.deploy(deploy_file, net_file) + mock_create_nodes.assert_called_once() + assert mock_reboot_nodes.call_count == 2 + mock__post_deploy.assert_called_once() + mock_prepare_cluster.assert_called_once() + mock_copy_new_deploy_config.assert_called_once() + mock_prepare_host_and_pxe.assert_called_once() + mock_install_virtual_nodes.assert_called_once() + mock_check_os_installation.assert_called_once() + mock_check_openstack_installation.assert_called_once() + mock_post_deploy.assert_called_once() + tmpdir.remove() + + +@pytest.mark.parametrize('status', [ + (True), + (False)]) @mock.patch('deploy.environment.commands.getstatusoutput') -def test_post_deploy_VirtualEnvironment(mock_getstatusoutput, tmpdir): +@mock.patch('deploy.environment.LW') +@mock.patch('deploy.environment.LI') +def test__post_deploy_VirtualEnvironment(mock_LI, mock_LW, + mock_getstatusoutput, status, tmpdir): work_dir = os.path.join(tmpdir.dirname, tmpdir.basename, work_dir_name) storage_dir = os.path.join(tmpdir.dirname, tmpdir.basename, storage_dir_name) daisy_server = copy.deepcopy(daisy_server_info) daisy_server['image'] = os.path.join(storage_dir, daisy_server['image']) - mock_getstatusoutput.return_value = (0, 'sucess') + mock_getstatusoutput.return_value = (status, 'success') VirtualEnvironmentInst = VirtualEnvironment( deploy_virtual_struct, net_struct, adapter_virtual, pxe_bridge_virtual, daisy_server, work_dir, storage_dir, scenario) @@ -384,3 +611,8 @@ def test_post_deploy_VirtualEnvironment(mock_getstatusoutput, tmpdir): VirtualEnvironmentInst._daisy_server_net = 'daisy1' VirtualEnvironmentInst._daisy_os_net = 'daisy2' assert environment.commands.getstatusoutput.call_count == 4 + if status: + mock_LW.assert_called() + else: + mock_LI.assert_called() + tmpdir.remove() diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 4cfe741d..e3b9dff7 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -9,73 +9,118 @@ import os import pytest import mock +import shutil from deploy import utils from deploy.utils import ( + get_logger, + save_log_to_file, err_exit, + log_bar, check_sudo_privilege, check_file_exists, make_file_executable, confirm_dir_exists, - check_scenario_valid + update_config, + ipmi_reboot_node, + run_shell, + check_scenario_valid, + LI ) +@pytest.fixture(scope="module") +def daisy_conf_file_dir(data_root): + return os.path.join(data_root, 'daisy_conf') + + +@mock.patch.object(utils.logging.Logger, 'addHandler') +def test_get_logger(mock_addHandler): + get_logger() + mock_addHandler.assert_called_once() + + +@mock.patch.object(utils.logging.Logger, 'addHandler') +def test_save_log_to_file(mock_addHandler, tmpdir): + log_file = os.path.join(tmpdir.dirname, tmpdir.basename, 'test_log.txt') + save_log_to_file(log_file) + mock_addHandler.assert_called_once() + tmpdir.remove() + + def test_err_exit(): message = 'test error msg!' with pytest.raises(SystemExit): err_exit(message) +def test_log_bar(): + log_bar('test_messgae', log_func=LI) + + @mock.patch('deploy.utils.err_exit') @mock.patch('deploy.utils.os.getuid') def test_check_sudo_privilege(mock_getuid, mock_err_exit): mock_getuid.return_value = 1 check_sudo_privilege() - utils.err_exit.assert_called_once_with('You need run this script with sudo privilege') + mock_err_exit.assert_called_once_with('You need run this script with sudo privilege') -@pytest.mark.parametrize('test_file_name', [ - ('no_exist_file'), - ('exist_file')]) -def test_check_file_exists(tmpdir, test_file_name): - try: +@pytest.mark.parametrize('test_file_name, include_dirname', [ + ('no_exist_file', True), + ('exist_file', True), + ('no_exist_file', False), + ('exist_file', False)]) +@mock.patch('deploy.utils.err_exit') +def test_check_file_exists(mock_err_exit, tmpdir, test_file_name, include_dirname): + if include_dirname: file_path = os.path.join(tmpdir.dirname, tmpdir.basename, test_file_name) if test_file_name == 'exist_file': os.mknod(file_path) - check_file_exists(file_path) - except SystemExit: - if test_file_name == 'exist_file': - assert 0 else: - if test_file_name == 'no_exist_file': - assert 0 - finally: - tmpdir.remove() - - -@pytest.mark.parametrize('test_file_name', [ - ('no_exist_file'), - ('no_exe_file'), - ('exe_file')]) -def test_make_file_executable(tmpdir, test_file_name): - try: + file_path = test_file_name + check_file_exists(file_path) + if include_dirname is True: + if test_file_name == 'exist_file': + mock_err_exit.assert_not_called() + else: + mock_err_exit.assert_called_once() + if include_dirname is False: + mock_err_exit.assert_called_once() + tmpdir.remove() + + +@pytest.mark.parametrize('test_file_name, status, include_dir', [ + ('no_exist_file', False, False), + ('no_exe_file', False, True), + ('no_exe_file', True, True), + ('exe_file', False, True)]) +@mock.patch('deploy.utils.commands.getstatusoutput') +@mock.patch('deploy.utils.err_exit') +def test_make_file_executable(mock_err_exit, mock_getstatusoutput, + tmpdir, test_file_name, + status, include_dir): + if include_dir: file_path = os.path.join(tmpdir.dirname, tmpdir.basename, test_file_name) - if test_file_name == 'no_exe_file': - os.mknod(file_path) - if test_file_name == 'exe_file': - os.mknod(file_path, 0700) - make_file_executable(file_path) - except SystemExit: - if test_file_name == 'no_exe_file' or test_file_name == 'exe_file': - assert 0 else: - if test_file_name == 'no_exist_file': - assert 0 - finally: - if test_file_name == 'no_exe_file' or test_file_name == 'exe_file': - assert os.access(file_path, os.X_OK) - tmpdir.remove() + file_path = test_file_name + if test_file_name == 'no_exe_file': + os.mknod(file_path) + if test_file_name == 'exe_file': + os.mknod(file_path, 0700) + output = 'test_out' + mock_getstatusoutput.return_value = (status, output) + make_file_executable(file_path) + if test_file_name == 'exe_file': + mock_err_exit.assert_not_called() + assert os.access(file_path, os.X_OK) + if test_file_name == 'no_exe_file' and status is False: + mock_err_exit.assert_not_called() + if test_file_name == 'no_exe_file' and status is True: + mock_err_exit.assert_called() + if test_file_name == 'no_exist_file': + mock_err_exit.assert_called() + tmpdir.remove() @pytest.mark.parametrize('test_dir_name', [ @@ -92,6 +137,67 @@ def test_confirm_dir_exists(tmpdir, test_dir_name): tmpdir.remove() +def test_update_config(daisy_conf_file_dir, tmpdir): + src_daisy_conf_file = os.path.join(daisy_conf_file_dir, 'daisy.conf') + dst_daisy_conf_file = os.path.join(tmpdir.dirname, tmpdir.basename, 'daisy.conf') + shutil.copyfile(src_daisy_conf_file, dst_daisy_conf_file) + key = 'daisy_management_ip' + value = '10.20.11.2' + update_line = 'daisy_management_ip = 10.20.11.2' + is_match = False + update_config(dst_daisy_conf_file, key, value, section='DEFAULT') + with open(dst_daisy_conf_file) as f: + lines = f.readlines() + for line in lines: + line_content = line.strip() + if update_line in line_content: + is_match = True + break + assert is_match + tmpdir.remove() + + +@pytest.mark.parametrize('boot_source, status', [ + (None, 0), + (None, 1), + ('test_source', 0), + ('test_source', 1)]) +@mock.patch('deploy.utils.err_exit') +@mock.patch.object(utils.commands, 'getstatusoutput') +def test_ipmi_reboot_node(mock_getstatusoutput, mock_err_exit, + boot_source, status): + host = '192.168.1.11' + user = 'testuser' + passwd = 'testpass' + output = 'test_out' + mock_getstatusoutput.return_value = (status, output) + ipmi_reboot_node(host, user, passwd, boot_source=boot_source) + if boot_source: + assert mock_getstatusoutput.call_count == 2 + if status: + assert mock_err_exit.call_count == 2 + else: + mock_getstatusoutput.called_once() + if status: + mock_err_exit.called_once() + + +@pytest.mark.parametrize('cmd, check, expect', [ + ('cd /home', False, 0), + ('cd /home', True, 0), + ('test_command', False, 127), + ('test_command', True, 127)]) +@mock.patch('deploy.utils.err_exit') +def test_run_shell(mock_err_exit, cmd, check, expect): + ret = run_shell(cmd, check=check) + if check: + if cmd == 'cd /home': + mock_err_exit.assert_not_called() + elif cmd == 'test_command': + mock_err_exit.assert_called_once() + assert ret == expect + + @pytest.mark.parametrize('scenario', [ ('os-nosdn-nofeature-ha')]) @mock.patch("deploy.utils.err_exit") |