def test_module_utils_basic_ansible_module_find_mount_point(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) def _mock_ismount(path): if path == b'/': return True return False with patch('os.path.ismount', side_effect=_mock_ismount): self.assertEqual(am.find_mount_point('/root/fs/../mounted/path/to/whatever'), '/') def _mock_ismount(path): if path == b'/subdir/mount': return True if path == b'/': return True return False with patch('os.path.ismount', side_effect=_mock_ismount): self.assertEqual(am.find_mount_point('/subdir/mount/path/to/whatever'), '/subdir/mount')
def test_module_utils_basic_ansible_module_selinux_default_context(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) am.selinux_initial_context = MagicMock(return_value=[None, None, None, None]) am.selinux_enabled = MagicMock(return_value=True) # we first test the cases where the python selinux lib is not installed basic.HAVE_SELINUX = False self.assertEqual(am.selinux_default_context(path='/foo/bar'), [None, None, None, None]) # all following tests assume the python selinux bindings are installed basic.HAVE_SELINUX = True basic.selinux = Mock() with patch.dict('sys.modules', {'selinux': basic.selinux}): # next, we test with a mocked implementation of selinux.matchpathcon to simulate # an actual context being found with patch('selinux.matchpathcon', return_value=[0, 'unconfined_u:object_r:default_t:s0']): self.assertEqual(am.selinux_default_context(path='/foo/bar'), ['unconfined_u', 'object_r', 'default_t', 's0']) # we also test the case where matchpathcon returned a failure with patch('selinux.matchpathcon', return_value=[-1, '']): self.assertEqual(am.selinux_default_context(path='/foo/bar'), [None, None, None, None]) # finally, we test where an OSError occurred during matchpathcon's call with patch('selinux.matchpathcon', side_effect=OSError): self.assertEqual(am.selinux_default_context(path='/foo/bar'), [None, None, None, None]) delattr(basic, 'selinux')
def test_tmpdir_property(self, monkeypatch, args, expected, stat_exists): makedirs = {'called': False} def mock_mkdtemp(prefix, dir): return os.path.join(dir, prefix) def mock_makedirs(path, mode): makedirs['called'] = True makedirs['path'] = path makedirs['mode'] = mode return monkeypatch.setattr(tempfile, 'mkdtemp', mock_mkdtemp) monkeypatch.setattr(os.path, 'exists', lambda x: stat_exists) monkeypatch.setattr(os, 'makedirs', mock_makedirs) monkeypatch.setattr(shutil, 'rmtree', lambda x: None) monkeypatch.setattr( basic, '_ANSIBLE_ARGS', to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': args}))) with patch('time.time', return_value=42): am = basic.AnsibleModule(argument_spec={}) actual_tmpdir = am.tmpdir assert actual_tmpdir == expected # verify subsequent calls always produces the same tmpdir assert am.tmpdir == actual_tmpdir if not stat_exists: assert makedirs['called'] expected = os.path.expanduser(os.path.expandvars(am._remote_tmp)) assert makedirs['path'] == expected assert makedirs['mode'] == 0o700
def test_options_type_list(self, stdin, options_argspec_list, expected): """Test that a basic creation with required and required_if works""" # should test ok, tests basic foo requirement and required_if am = basic.AnsibleModule(**options_argspec_list) assert isinstance(am.params['foobar'], list) assert am.params['foobar'] == expected
def test_module_utils_basic_ansible_module_selinux_enabled(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) # we first test the cases where the python selinux lib is # not installed, which has two paths: one in which the system # does have selinux installed (and the selinuxenabled command # is present and returns 0 when run), or selinux is not installed basic.HAVE_SELINUX = False am.get_bin_path = MagicMock() am.get_bin_path.return_value = '/path/to/selinuxenabled' am.run_command = MagicMock() am.run_command.return_value = (0, '', '') self.assertRaises(SystemExit, am.selinux_enabled) am.get_bin_path.return_value = None self.assertEqual(am.selinux_enabled(), False) # finally we test the case where the python selinux lib is installed, # and both possibilities there (enabled vs. disabled) basic.HAVE_SELINUX = True basic.selinux = Mock() with patch.dict('sys.modules', {'selinux': basic.selinux}): with patch('selinux.is_selinux_enabled', return_value=0): self.assertEqual(am.selinux_enabled(), False) with patch('selinux.is_selinux_enabled', return_value=1): self.assertEqual(am.selinux_enabled(), True) delattr(basic, 'selinux')
def test_list_with_elements_path(self, capfd, mocker, stdin, complex_argspec): """Test choices with list""" am = basic.AnsibleModule(**complex_argspec) assert isinstance(am.params['bar3'], list) assert am.params['bar3'][0].startswith('/') assert am.params['bar3'][1] == 'test/'
def test_validator_fail(stdin, capfd, argspec, expected): with pytest.raises(SystemExit): basic.AnsibleModule(argument_spec=argspec) out, err = capfd.readouterr() assert not err assert expected in json.loads(out)['msg'] assert json.loads(out)['failed']
def test_validator_function(mocker, stdin): # Type is a callable MOCK_VALIDATOR_SUCCESS = mocker.MagicMock(return_value=27) argspec = {'arg': {'type': MOCK_VALIDATOR_SUCCESS}} am = basic.AnsibleModule(argspec) assert isinstance(am.params['arg'], integer_types) assert am.params['arg'] == 27
def test_no_log_true(stdin, capfd): """Explicitly mask an argument (no_log=True).""" arg_spec = {"arg_pass": {"no_log": True}} am = basic.AnsibleModule(arg_spec) # no_log=True is picked up by both am._log_invocation and list_no_log_values # (called by am._handle_no_log_values). As a result, we can check for the # value in am.no_log_values. assert "testing" in am.no_log_values
def test_elements_path_in_option(self, mocker, stdin, options_argspec_dict): """Test that the complex argspec works with elements path type""" am = basic.AnsibleModule(**options_argspec_dict) assert isinstance(am.params['foobar']['bar4'][0], str) assert am.params['foobar']['bar4'][0].startswith('/')
def test_no_log_none(stdin, capfd): """Allow Ansible to make the decision by matching the argument name against PASSWORD_MATCH.""" arg_spec = {"arg_pass": {}} am = basic.AnsibleModule(arg_spec) # Omitting no_log is only picked up by _log_invocation, so the value never # makes it into am.no_log_values. Instead we can check for the warning # emitted by am._log_invocation. assert len(am._warnings) > 0
def test_fail_required_together(self, capfd, stdin, complex_argspec): """Fail because only one of a required_together pair of parameters was specified""" with pytest.raises(SystemExit): am = basic.AnsibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_fail_validate_options_list(self, capfd, stdin, options_argspec_list, expected): """Fail because one of a required_together pair of parameters has a default and the other was not specified""" with pytest.raises(SystemExit): am = basic.AnsibleModule(**options_argspec_list) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert expected in results['msg']
def test_fail_mutually_exclusive(self, capfd, stdin, complex_argspec): """Fail because of mutually exclusive parameters""" with pytest.raises(SystemExit): am = basic.AnsibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results[ 'msg'] == "parameters are mutually exclusive: bar|bam, bing|bang|bong"
def test_fail_list_with_choices(self, capfd, mocker, stdin, complex_argspec): """Fail because one of the items is not in the choice""" with pytest.raises(SystemExit): basic.AnsibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results[ 'msg'] == "value of zardoz2 must be one or more of: one, two, three. Got no match for: four, five"
def validate_config(spec, data): """ Validate if the input data against the AnsibleModule spec format :param spec: Ansible argument spec :param data: Data to be validated :return: """ params = basic._ANSIBLE_ARGS basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': data})) validated_data = basic.AnsibleModule(spec).params basic._ANSIBLE_ARGS = params return validated_data
def test_fail_required_together_and_default(self, capfd, stdin, complex_argspec): """Fail because one of a required_together pair of parameters has a default and the other was not specified""" complex_argspec['argument_spec']['baz'] = {'default': 42} with pytest.raises(SystemExit): am = basic.AnsibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_complex_type_fallback(self, mocker, stdin, complex_argspec): """Test that the complex argspec works if we get a required parameter via fallback""" environ = os.environ.copy() environ['BAZ'] = 'test data' mocker.patch( 'ansible_collections.notstdlib.moveitallout.plugins.module_utils.basic.os.environ', environ) am = basic.AnsibleModule(**complex_argspec) assert isinstance(am.params['baz'], str) assert am.params['baz'] == 'test data'
def test_module_utils_basic_ansible_module_selinux_initial_context(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) am.selinux_mls_enabled = MagicMock() am.selinux_mls_enabled.return_value = False self.assertEqual(am.selinux_initial_context(), [None, None, None]) am.selinux_mls_enabled.return_value = True self.assertEqual(am.selinux_initial_context(), [None, None, None, None])
def test_validator_basic_types(argspec, expected, stdin): am = basic.AnsibleModule(argspec) if 'type' in argspec['arg']: if argspec['arg']['type'] == 'int': type_ = integer_types else: type_ = getattr(builtins, argspec['arg']['type']) else: type_ = str assert isinstance(am.params['arg'], type_) assert am.params['arg'] == expected
def get_info_mock_object(self, kind=None): """ Helper method to return an na_ontap_info object """ module = basic.AnsibleModule( argument_spec=netapp_utils.na_ontap_host_argument_spec(), supports_check_mode=True) obj = info_module(module) obj.netapp_info = dict() if kind is None: obj.server = MockONTAPConnection() else: obj.server = MockONTAPConnection(kind) return obj
def test_module_utils_basic_ansible_module_user_and_group(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) mock_stat = MagicMock() mock_stat.st_uid = 0 mock_stat.st_gid = 0 with patch('os.lstat', return_value=mock_stat): self.assertEqual(am.user_and_group('/path/to/file'), (0, 0))
def test_fail_required_together_and_fallback(self, capfd, mocker, stdin, complex_argspec): """Fail because one of a required_together pair of parameters has a fallback and the other was not specified""" environ = os.environ.copy() environ['BAZ'] = 'test data' mocker.patch( 'ansible_collections.notstdlib.moveitallout.plugins.module_utils.basic.os.environ', environ) with pytest.raises(SystemExit): am = basic.AnsibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_module_utils_basic_ansible_module_set_context_if_different(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) basic.HAVE_SELINUX = False am.selinux_enabled = MagicMock(return_value=False) self.assertEqual(am.set_context_if_different('/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], True), True) self.assertEqual(am.set_context_if_different('/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], False), False) basic.HAVE_SELINUX = True am.selinux_enabled = MagicMock(return_value=True) am.selinux_context = MagicMock(return_value=['bar_u', 'bar_r', None, None]) am.is_special_selinux_path = MagicMock(return_value=(False, None)) basic.selinux = Mock() with patch.dict('sys.modules', {'selinux': basic.selinux}): with patch('selinux.lsetfilecon', return_value=0) as m: self.assertEqual(am.set_context_if_different('/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], False), True) m.assert_called_with('/path/to/file', 'foo_u:foo_r:foo_t:s0') m.reset_mock() am.check_mode = True self.assertEqual(am.set_context_if_different('/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], False), True) self.assertEqual(m.called, False) am.check_mode = False with patch('selinux.lsetfilecon', return_value=1) as m: self.assertRaises(SystemExit, am.set_context_if_different, '/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], True) with patch('selinux.lsetfilecon', side_effect=OSError) as m: self.assertRaises(SystemExit, am.set_context_if_different, '/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], True) am.is_special_selinux_path = MagicMock(return_value=(True, ['sp_u', 'sp_r', 'sp_t', 's0'])) with patch('selinux.lsetfilecon', return_value=0) as m: self.assertEqual(am.set_context_if_different('/path/to/file', ['foo_u', 'foo_r', 'foo_t', 's0'], False), True) m.assert_called_with('/path/to/file', 'sp_u:sp_r:sp_t:s0') delattr(basic, 'selinux')
def test_module_utils_basic_ansible_module_is_special_selinux_path(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic args = json.dumps(dict(ANSIBLE_MODULE_ARGS={'_ansible_selinux_special_fs': "nfs,nfsd,foos", '_ansible_remote_tmp': "/tmp", '_ansible_keep_remote_files': False})) with swap_stdin_and_argv(stdin_data=args): basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) def _mock_find_mount_point(path): if path.startswith('/some/path'): return '/some/path' elif path.startswith('/weird/random/fstype'): return '/weird/random/fstype' return '/' am.find_mount_point = MagicMock(side_effect=_mock_find_mount_point) am.selinux_context = MagicMock(return_value=['foo_u', 'foo_r', 'foo_t', 's0']) m = mock_open() m.side_effect = OSError with patch.object(builtins, 'open', m, create=True): self.assertEqual(am.is_special_selinux_path('/some/path/that/should/be/nfs'), (False, None)) mount_data = [ '/dev/disk1 / ext4 rw,seclabel,relatime,data=ordered 0 0\n', '1.1.1.1:/path/to/nfs /some/path nfs ro 0 0\n', 'whatever /weird/random/fstype foos rw 0 0\n', ] # mock_open has a broken readlines() implementation apparently... # this should work by default but doesn't, so we fix it m = mock_open(read_data=''.join(mount_data)) m.return_value.readlines.return_value = mount_data with patch.object(builtins, 'open', m, create=True): self.assertEqual(am.is_special_selinux_path('/some/random/path'), (False, None)) self.assertEqual(am.is_special_selinux_path('/some/path/that/should/be/nfs'), (True, ['foo_u', 'foo_r', 'foo_t', 's0'])) self.assertEqual(am.is_special_selinux_path('/weird/random/fstype/path'), (True, ['foo_u', 'foo_r', 'foo_t', 's0']))
def test_module_utils_basic_ansible_module_selinux_mls_enabled(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) basic.HAVE_SELINUX = False self.assertEqual(am.selinux_mls_enabled(), False) basic.HAVE_SELINUX = True basic.selinux = Mock() with patch.dict('sys.modules', {'selinux': basic.selinux}): with patch('selinux.is_selinux_mls_enabled', return_value=0): self.assertEqual(am.selinux_mls_enabled(), False) with patch('selinux.is_selinux_mls_enabled', return_value=1): self.assertEqual(am.selinux_mls_enabled(), True) delattr(basic, 'selinux')
def test_module_utils_basic_ansible_module_set_group_if_different(self): from ansible_collections.notstdlib.moveitallout.plugins.module_utils import basic basic._ANSIBLE_ARGS = None am = basic.AnsibleModule( argument_spec=dict(), ) self.assertEqual(am.set_group_if_different('/path/to/file', None, True), True) self.assertEqual(am.set_group_if_different('/path/to/file', None, False), False) am.user_and_group = MagicMock(return_value=(500, 500)) with patch('os.lchown', return_value=None) as m: self.assertEqual(am.set_group_if_different('/path/to/file', 0, False), True) m.assert_called_with(b'/path/to/file', -1, 0) def _mock_getgrnam(*args, **kwargs): mock_gr = MagicMock() mock_gr.gr_gid = 0 return mock_gr m.reset_mock() with patch('grp.getgrnam', side_effect=_mock_getgrnam): self.assertEqual(am.set_group_if_different('/path/to/file', 'root', False), True) m.assert_called_with(b'/path/to/file', -1, 0) with patch('grp.getgrnam', side_effect=KeyError): self.assertRaises(SystemExit, am.set_group_if_different, '/path/to/file', 'root', False) m.reset_mock() am.check_mode = True self.assertEqual(am.set_group_if_different('/path/to/file', 0, False), True) self.assertEqual(m.called, False) am.check_mode = False with patch('os.lchown', side_effect=OSError) as m: self.assertRaises(SystemExit, am.set_group_if_different, '/path/to/file', 'root', False)
def test_deprecated_alias(self, capfd, mocker, stdin, complex_argspec): """Test a deprecated alias""" am = basic.AnsibleModule(**complex_argspec) assert "Alias 'zodraz' is deprecated." in am._deprecations[0]['msg'] assert am._deprecations[0]['version'] == '9.99'
def test_complex_duplicate_warning(self, stdin, complex_argspec): """Test that the complex argspec issues a warning if we specify an option both with its canonical name and its alias""" am = basic.AnsibleModule(**complex_argspec) assert isinstance(am.params['foo'], str) assert 'Both option foo and its alias dup are set.' in am._warnings assert am.params['foo'] == 'hello2'
def test_list_with_choices(self, capfd, mocker, stdin, complex_argspec): """Test choices with list""" am = basic.AnsibleModule(**complex_argspec) assert isinstance(am.params['zardoz2'], list) assert am.params['zardoz2'] == ['one', 'three']