def test_check_elementsw_params_negative(self): ''' check elementsw parameters for source negative testing ''' data = self.set_default_args() del data['source_path'] set_module_args(data) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored') with pytest.raises(AnsibleFailJson) as exc: my_obj.check_elementsw_parameters('source') assert 'Error: Missing required parameter source_path' in exc.value.args[ 0]['msg']
def test_get_facts(self, *args): set_module_args( dict(gather_subset=['system-info'], password='******', server='localhost', user='******')) fixture1 = load_fixture('load_shared_system_setup_1.json') module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) tm = SystemInfoFactManager(module=module) tm.read_collection_from_device = Mock(return_value=fixture1) # Override methods to force specific logic in the module to happen mm = ModuleManager(module=module) mm.get_manager = Mock(return_value=tm) results = mm.exec_module() assert results['changed'] is True assert 'system_info' in results
def test_update_time_until_up(self, *args): set_module_args( dict(name='foo', time_until_up=300, partition='Common', server='localhost', password='******', user='******')) current = Parameters(params=load_fixture('load_ltm_monitor_tcp.json')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) # Override methods in the specific type of manager mm = ModuleManager(module=module) mm.exists = Mock(return_value=True) mm.read_current_from_device = Mock(return_value=current) mm.update_on_device = Mock(return_value=True) results = mm.exec_module() assert results['changed'] is True assert results['time_until_up'] == 300
def test_command_with_commas(self, *args): set_module_args( dict(commands=""" tmsh create /auth ldap system-auth {bind-dn uid=binduser, cn=users,dc=domain,dc=com bind-pw $ENCRYPTEDPW check-roles-group enabled search-base-dn cn=users,dc=domain,dc=com servers add { ldap.server.com } } """, server='localhost', user='******', password='******')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) m1 = V2Manager(module=module) m1.execute_on_device = Mock(return_value=['resp1', 'resp2']) mm = ModuleManager(module=module) mm.get_manager = Mock(return_value=m1) results = mm.exec_module() assert results['changed'] is True assert m1.execute_on_device.call_count == 2
def test_create_vlan_untagged_interfaces(self, *args): set_module_args(dict( name='somevlan', untagged_interface=['2.1', '1.1'], server='localhost', password='******', user='******', partition='Common', )) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode ) # Override methods to force specific logic in the module to happen mm = ModuleManager(module=module) mm.create_on_device = Mock(return_value=True) mm.exists = Mock(return_value=False) results = mm.exec_module() assert results['changed'] is True assert results['untagged_interfaces'] == ['1.1', '2.1']
def test_update_interval_larger_than_existing_timeout(self, *args): set_module_args( dict(name='foo', interval=30, partition='Common', server='localhost', password='******', user='******')) current = Parameters(params=load_fixture('load_ltm_monitor_tcp.json')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) # Override methods in the specific type of manager mm = ModuleManager(module=module) mm.exists = Mock(return_value=True) mm.read_current_from_device = Mock(return_value=current) mm.update_on_device = Mock(return_value=True) with pytest.raises(F5ModuleError) as ex: mm.exec_module() assert "must be less than" in str(ex)
def test_ensure_feature_is_enabled_nitro_exception_caught(self): set_module_args( dict( nitro_user='******', nitro_pass='******', nsip='192.0.2.1', state='present', save_config=False, )) from ansible.modules.network.netscaler import netscaler_lb_vserver client_mock = Mock() lb_vserver_proxy_mock = Mock() errorcode = 10 message = 'mock error' class MockException(Exception): def __init__(self): self.errorcode = errorcode self.message = message feature_mock = Mock(side_effect=MockException) with patch.multiple( 'ansible.modules.network.netscaler.netscaler_lb_vserver', get_nitro_client=Mock(return_value=client_mock), lb_vserver_exists=Mock(side_effect=[True, True]), lb_vserver_identical=Mock(side_effect=[True, True]), servicegroup_bindings_identical=Mock(side_effect=[True, True]), service_bindings_identical=Mock(side_effect=[True, True]), ConfigProxy=Mock(return_value=lb_vserver_proxy_mock), ensure_feature_is_enabled=feature_mock, nitro_exception=MockException, ): self.module = netscaler_lb_vserver result = self.failed() expected_msg = 'nitro exception errorcode=%s, message=%s' % ( errorcode, message) self.assertEqual(result['msg'], expected_msg, 'Failed to handle nitro exception')
def test_create(self, *args): # Configure the arguments that would be sent to the Ansible module set_module_args( dict(name='foo', parent='bar', idle_timeout=500, datagram_load_balancing=True, password='******', server='localhost', user='******')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) mm = ModuleManager(module=module) # Override methods to force specific logic in the module to happen mm.exists = Mock(return_value=False) mm.create_on_device = Mock(return_value=True) results = mm.exec_module() assert results['changed'] is True assert results['idle_timeout'] == 500
def test_create_service(self, *args): parameters = load_fixture( 'create_iapp_service_parameters_f5_http.json') set_module_args( dict(name='foo', template='f5.http', parameters=parameters, state='present', password='******', server='localhost', user='******')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) mm = ModuleManager(module=module) # Override methods to force specific logic in the module to happen mm.exists = Mock(return_value=False) mm.create_on_device = Mock(return_value=True) mm.template_exists = Mock(return_value=True) results = mm.exec_module() assert results['changed'] is True
def test_elementsw_source_path_format(self): ''' test element_source_path_format_matches ''' data = self.set_default_args() set_module_args(data) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored') match = my_obj.element_source_path_format_matches('1.1.1.1:dummy') assert match is None match = my_obj.element_source_path_format_matches( '10.10.10.10:/lun/10') assert match is not None
def test_etc_lsb_release(self): module = self._mock_module() module.get_bin_path = Mock(return_value=None) with patch('ansible.module_utils.facts.system.lsb.os.path.exists', return_value=True): with patch('ansible.module_utils.facts.system.lsb.get_file_lines', return_value=etc_lsb_release_ubuntu14.splitlines()): fact_collector = self.collector_class() facts_dict = fact_collector.collect(module=module) self.assertIsInstance(facts_dict, dict) self.assertEqual(facts_dict['lsb']['release'], '14.04') self.assertEqual(facts_dict['lsb']['id'], 'Ubuntu') self.assertEqual(facts_dict['lsb']['description'], '"Ubuntu 14.04.3 LTS"') self.assertEqual(facts_dict['lsb']['codename'], 'trusty')
def test_valid_schedule_count_with_snapmirror_labels(self): ''' validate when schedule has same number of elements with snapmirror labels ''' data = self.set_default_args() data['schedule'] = ['hourly', 'daily', 'weekly', 'monthly', '5min'] data['count'] = [1, 2, 3, 4, 5] data['snapmirror_label'] = ['hourly', 'daily', 'weekly', 'monthly', '5min'] set_module_args(data) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: my_obj.server = self.server my_obj.create_snapshot_policy() create_xml = my_obj.server.xml_in assert data['count'][2] == int(create_xml['count3']) assert data['schedule'][4] == create_xml['schedule5'] assert data['snapmirror_label'][3] == create_xml['snapmirror-label4']
def test_view_storage_failed_case(self, idrac_connection_storage_volume_mock, idrac_default_args): idrac_default_args.update({ "controller_id": "controller", "volume_id": "virtual_disk" }) msg = {"Status": "Failed", "msg": "Failed to fetch storage details"} obj = MagicMock() idrac_connection_storage_volume_mock.config_mgr.RaidHelper = obj obj.view_storage = Mock(return_value=msg) f_module = self.get_module_mock(params=idrac_default_args) with pytest.raises(Exception) as ex: self.module.view_storage(idrac_connection_storage_volume_mock, f_module) assert "Failed to fetch storage details" == str(ex.value)
def test_get_fc_wwn_info(mocker): module = Mock() inst = fc_wwn.FcWwnInitiatorFactCollector() mocker.patch.object(module, 'get_bin_path', side_effect=mock_get_bin_path) mocker.patch.object(module, 'run_command', side_effect=mock_run_command) d = { 'aix6': ['10000090FA551508'], 'sunos5': ['10000090fa1658de'], 'hp-ux11': ['0x50060b00006975ec'] } for key, value in d.items(): mocker.patch('sys.platform', key) wwn_expected = {"fibre_channel_wwn": value} assert wwn_expected == inst.collect(module=module)
def test_invalid_schedule_count_is_none(self): ''' validate error when schedule is None ''' data = self.set_default_args() data['schedule'] = None data['count'] = None set_module_args(data) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: my_obj.server = self.server with pytest.raises(AnsibleFailJson) as exc: my_obj.create_snapshot_policy() msg = 'Error: A Snapshot policy must have at least 1 ' \ 'schedule and can have up to a maximum of 5 schedules, with a count ' \ 'representing the maximum number of Snapshot copies for each schedule' assert exc.value.args[0]['msg'] == msg
def test_if_all_methods_catch_exception(self): data = self.set_default_args() data['source_hostname'] = '10.10.10.10' data['source_volume'] = 'ansible' data['destination_volume'] = 'ansible2' set_module_args(data) my_obj = my_module() if not self.onbox: my_obj.server = MockONTAPConnection('snapmirror_fail') with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_get() assert 'Error fetching snapmirror info: ' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_abort() assert 'Error aborting SnapMirror relationship :' in exc.value.args[0][ 'msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_break() assert 'Error breaking SnapMirror relationship :' in exc.value.args[0][ 'msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_get = Mock( return_value={'mirror_state': 'transferring'}) my_obj.snapmirror_initialize() assert 'Error initializing SnapMirror :' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_update() assert 'Error updating SnapMirror :' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.set_source_cluster_connection = Mock(return_value=True) my_obj.source_server = MockONTAPConnection('snapmirror_fail') my_obj.check_if_remote_volume_exists() assert 'Error fetching source volume details' in exc.value.args[0][ 'msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.check_if_remote_volume_exists = Mock(return_value=True) my_obj.source_server = MockONTAPConnection() my_obj.snapmirror_create() assert 'Error creating SnapMirror ' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_quiesce() assert 'Error Quiescing SnapMirror :' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_quiesce = Mock(return_value=None) my_obj.get_destination = Mock(return_value=None) my_obj.snapmirror_break = Mock(return_value=None) my_obj.delete_snapmirror(False, 'data_protection') assert 'Error deleting SnapMirror :' in exc.value.args[0]['msg'] with pytest.raises(AnsibleFailJson) as exc: my_obj.snapmirror_modify({ 'policy': 'ansible2', 'schedule': 'abc2' }) assert 'Error modifying SnapMirror schedule or policy :' in exc.value.args[ 0]['msg']
def test_module_utils_basic_ansible_module_selinux_mls_enabled(self): from ansible.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_run_idrac_reset_status_success_case02(self, idrac_reset_connection_mock, idrac_default_args): msg = {"Status": "Success"} obj = MagicMock() idrac_reset_connection_mock.config_mgr = obj obj.reset_idrac = Mock(return_value="msg") f_module = self.get_module_mock(params=msg, check_mode=False) msg, err = self.module.run_idrac_reset(idrac_reset_connection_mock, f_module) assert msg == { 'changed': False, 'failed': False, 'msg': { 'idracreset': 'msg' } }
def test_successful_element_ontap_create(self, check_param, snapmirror_create): ''' creating ElementSW to ONTAP snapmirror ''' data = self.set_default_args() data['schedule'] = 'abc' data['connection_type'] = 'elementsw_ontap' data['source_hostname'] = '10.10.10.10' set_module_args(data) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: my_obj.server = self.server with pytest.raises(AnsibleExitJson) as exc: my_obj.apply() assert exc.value.args[0]['changed'] snapmirror_create.assert_called_with() check_param.assert_called_with()
def test_configlet_update_task_good_three_tries(self, mock_info, mock_sleep): ''' Test configlet_update_task gets task on third try. ''' module = Mock() task1 = dict(data=dict(WORKFLOW_ACTION='Configlet Push'), description='Configlet Assign', workOrderId='7') task2 = dict(data=dict(WORKFLOW_ACTION='Nonsense'), description='Configlet Assign', workOrderId='700') device_info = dict(taskIdList=[task1, task2]) mock_info.side_effect = [dict(), dict(), device_info] result = cv_server_provision.configlet_update_task(module) self.assertEqual(result, '7') self.assertEqual(mock_sleep.call_count, 2) self.assertEqual(mock_info.call_count, 3)
def test_activate_import_from_template(self, *args): set_module_args( dict( name='fake_policy', template='OWA Exchange 2007 (https)', state='present', active='yes', server='localhost', password='******', user='******', )) current = V1Parameters( params=load_fixture('load_asm_policy_inactive.json')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) v1 = V1Manager(module=module) v1.exists = Mock(return_value=False) v1.import_to_device = Mock(return_value=True) v1.wait_for_task = Mock(side_effect=[True, True]) v1.read_current_from_device = Mock(return_value=current) v1.apply_on_device = Mock(return_value=True) v1.create_from_template_on_device = Mock(return_value=True) v1._file_is_missing = Mock(return_value=False) # Override methods to force specific logic in the module to happen mm = ModuleManager(module=module) mm.version_is_less_than_13 = Mock(return_value=False) mm.get_manager = Mock(return_value=v1) results = mm.exec_module() assert results['changed'] is True assert results['name'] == 'fake_policy' assert results['template'] == 'OWA Exchange 2007 (https)' assert results['active'] is True
def test_botocore_exception_reports_nicely_via_fail_json_aws(self): basic._ANSIBLE_ARGS = to_bytes( json.dumps({ 'ANSIBLE_MODULE_ARGS': { '_ansible_tmpdir': '/tmp/ansible-abc' } })) module = AnsibleAWSModule(argument_spec=dict( fail_mode=dict(type='list', default=['success']))) fail_json_double = Mock() err_msg = {'Error': {'Code': 'FakeClass.FakeError'}} with patch.object(basic.AnsibleModule, 'fail_json', fail_json_double): try: raise botocore.exceptions.ClientError(err_msg, 'Could not find you') except Exception as e: print("exception is " + str(e)) module.fail_json_aws( e, msg="Fake failure for testing boto exception messages") assert (len(fail_json_double.mock_calls) > 0), "failed to call fail_json when should have" assert (len(fail_json_double.mock_calls) < 2), "called fail_json multiple times when once would do" assert("test_botocore_exception_reports_nicely" in fail_json_double.mock_calls[0][2]["exception"]), \ "exception traceback doesn't include correct function, fail call was actually: " \ + str(fail_json_double.mock_calls[0]) assert("Fake failure for testing boto exception messages:" in fail_json_double.mock_calls[0][2]["msg"]), \ "error message doesn't include the local message; was: " \ + str(fail_json_double.mock_calls[0]) assert("Could not find you" in fail_json_double.mock_calls[0][2]["msg"]), \ "error message doesn't include the botocore exception message; was: " \ + str(fail_json_double.mock_calls[0]) try: fail_json_double.mock_calls[0][2]["error"] except KeyError: raise Exception("error was missing; call was: " + str(fail_json_double.mock_calls[0])) assert("FakeClass.FakeError" == fail_json_double.mock_calls[0][2]["error"]["code"]), \ "Failed to find error/code; was: " + str(fail_json_double.mock_calls[0])
def test_ensure_feature_is_enabled_called(self): self.set_module_state('present') from ansible.modules.network.netscaler import netscaler_cs_policy client_mock = Mock() ensure_feature_is_enabled_mock = Mock() with patch.multiple( 'ansible.modules.network.netscaler.netscaler_cs_policy', get_nitro_client=Mock(return_value=client_mock), policy_exists=Mock(side_effect=[True, True]), nitro_exception=self.MockException, ensure_feature_is_enabled=ensure_feature_is_enabled_mock, ): self.module = netscaler_cs_policy result = self.exited() ensure_feature_is_enabled_mock.assert_has_calls([call(client_mock, 'CS')])
def test_module_utils_basic_ansible_module_set_context_if_different(self): from ansible.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_absent_state_workflow(self): set_module_args( dict( nitro_user='******', nitro_pass='******', nsip='192.0.2.1', state='absent', )) from ansible.modules.network.netscaler import netscaler_lb_vserver lb_vserver_proxy_mock = Mock() client_mock = Mock() with patch.multiple( 'ansible.modules.network.netscaler.netscaler_lb_vserver', get_nitro_client=Mock(return_value=client_mock), ConfigProxy=Mock(return_value=lb_vserver_proxy_mock), ensure_feature_is_enabled=Mock(return_value=True), lb_vserver_exists=Mock(side_effect=[True, False]), ): self.module = netscaler_lb_vserver result = self.exited() lb_vserver_proxy_mock.assert_has_calls([call.delete()]) self.assertTrue(result['changed'])
def test_get_mount_facts(self, mock_lsblk_uuid, mock_find_bind_mounts, mock_mtab_entries, mock_udevadm_uuid): module = Mock() # Returns a LinuxHardware-ish lh = hardware.linux.LinuxHardware(module=module, load_on_init=False) # Nothing returned, just self.facts modified as a side effect mount_facts = lh.get_mount_facts() self.assertIsInstance(mount_facts, dict) self.assertIn('mounts', mount_facts) self.assertIsInstance(mount_facts['mounts'], list) self.assertIsInstance(mount_facts['mounts'][0], dict) # Find mounts with space in the mountpoint path mounts_with_space = [ x for x in mount_facts['mounts'] if ' ' in x['mount'] ] self.assertEqual(len(mounts_with_space), 1) self.assertEqual(mounts_with_space[0]['mount'], '/mnt/foo bar')
def test_delete_iapp_template_idempotent(self, *args): set_module_args( dict(content=load_fixture('basic-iapp.tmpl'), password='******', server='localhost', user='******', state='absent')) module = AnsibleModule( argument_spec=self.spec.argument_spec, supports_check_mode=self.spec.supports_check_mode) mm = ModuleManager(module=module) # Override methods to force specific logic in the module to happen mm.exists = Mock(side_effect=[False, False]) results = mm.exec_module() assert results['changed'] is False
def test_run_export_lc_logs_failed_case01( self, idrac_connection_export_lc_logs_mock, idrac_default_args, idrac_file_manager_export_lc_logs_mock): idrac_default_args.update({ "share_name": "sharename", "share_mnt": "mountname", "share_user": "******", "share_password": "******", "job_wait": True }) error_msg = "Error in Runtime" obj2 = MagicMock() idrac_connection_export_lc_logs_mock.log_mgr = obj2 type(obj2).lclog_export = Mock(side_effect=Exception(error_msg)) f_module = self.get_module_mock(params=idrac_default_args) result, err = self.module.run_export_lc_logs( idrac_connection_export_lc_logs_mock, f_module) assert result['failed'] is True assert result['msg'] == "Error: {0}".format(error_msg)
def test_graceful_nitro_exception_operation_absent(self): self.set_module_state('absent') from ansible.modules.network.netscaler import netscaler_service class MockException(Exception): def __init__(self, *args, **kwargs): self.errorcode = 0 self.message = '' m = Mock(side_effect=MockException) with patch.multiple( 'ansible.modules.network.netscaler.netscaler_service', service_exists=m, nitro_exception=MockException): self.module = netscaler_service result = self.failed() self.assertTrue( result['msg'].startswith('nitro exception'), msg='Nitro exception not caught on operation absent')
def test_object_store_create(self): ''' test for creating object store''' module_args = { 'provider_type': 'abc', 'server': 'abc', 'container': 'abc', 'access_key': 'abc', 'secret_password': '******' } module_args.update(self.set_default_args()) set_module_args(module_args) my_obj = my_module() my_obj.asup_log_for_cserver = Mock(return_value=None) if not self.onbox: # mock the connection my_obj.server = MockONTAPConnection(kind='object_store') with pytest.raises(AnsibleExitJson) as exc: my_obj.apply() assert not exc.value.args[0]['changed']