From fb7882bf862a99f7f8d9d5ca60c6faddfc9ee817 Mon Sep 17 00:00:00 2001 From: Eitan Akman Date: Wed, 27 Jun 2018 00:51:17 -0400 Subject: [PATCH] Issue #39860: Add 'not_contains' method to parsing.py (#39874) * Issue #39860: Add 'not_contains' method to parsing.py * Issue #30860 Adds self.negate to Conditional class in lib/ansible/module_utils/network/common/parsing.py * Issue #39860 Fix singleton-comparison issue per sanity tests * Issue #39860 'test/integration/targets/nxos_command/tests/cli/not_comparison_operator.yaml' integration test * Issue #39860 Add unit tests to '../../test/units/module_utils/network/common/test_parsing.py' * Issue #39860 Fix singleton comparison issue * Fix E302 expected 2 blank lines, found 1 * Issue #39860 Add license header to unit tests * Issue #39860 Move integration test to 'test/integration/targets/nxos_command/tests/common/'; remove unnecessary comment from unit test * Issue #39860 remove unnecessary comment from unit test --- .../module_utils/network/common/parsing.py | 16 +++++-- .../tests/common/not_comparison_operator.yaml | 18 ++++++++ .../network/common/test_parsing.py | 45 +++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 test/integration/targets/nxos_command/tests/common/not_comparison_operator.yaml create mode 100644 test/units/module_utils/network/common/test_parsing.py diff --git a/lib/ansible/module_utils/network/common/parsing.py b/lib/ansible/module_utils/network/common/parsing.py index 18d25a6693..3e338c82ba 100644 --- a/lib/ansible/module_utils/network/common/parsing.py +++ b/lib/ansible/module_utils/network/common/parsing.py @@ -205,9 +205,16 @@ class Conditional(object): def __init__(self, conditional, encoding=None): self.raw = conditional - + self.negate = False try: - key, op, val = shlex.split(conditional) + components = shlex.split(conditional) + key, val = components[0], components[-1] + op_components = components[1:-1] + if 'not' in op_components: + self.negate = True + op_components.pop(op_components.index('not')) + op = op_components[0] + except ValueError: raise ValueError('failed to parse conditional') @@ -217,7 +224,10 @@ class Conditional(object): def __call__(self, data): value = self.get_value(dict(result=data)) - return self.func(value) + if not self.negate: + return self.func(value) + else: + return not self.func(value) def _cast_value(self, value): if value in BOOLEANS_TRUE: diff --git a/test/integration/targets/nxos_command/tests/common/not_comparison_operator.yaml b/test/integration/targets/nxos_command/tests/common/not_comparison_operator.yaml new file mode 100644 index 0000000000..b24ffb1c65 --- /dev/null +++ b/test/integration/targets/nxos_command/tests/common/not_comparison_operator.yaml @@ -0,0 +1,18 @@ +--- +- debug: msg="START common/not_comparison_operator.yaml on connection={{ ansible_connection }}" + +- name: test 'not' keyword in wait_for + nxos_command: + commands: + - show version + wait_for: + - "result[0] not contains QWERTYQWERTYQWERTY" + - "result[0] == not QWERTYQWERTYQWERTY" + - "result[0] matches not QWERTYQWERTYQWERTY" + register: result + +- assert: + that: + - "result.changed == false" + +- debug: msg="END common/not_comparison_operator.yaml on connection={{ ansible_connection }}" diff --git a/test/units/module_utils/network/common/test_parsing.py b/test/units/module_utils/network/common/test_parsing.py new file mode 100644 index 0000000000..b19fa9bd11 --- /dev/null +++ b/test/units/module_utils/network/common/test_parsing.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# +# (c) 2017 Red Hat, Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.compat.tests import unittest +from ansible.module_utils.network.common.parsing import Conditional + +test_results = ['result_1', 'result_2', 'result_3'] +c1 = Conditional('result[1] == result_2') +c2 = Conditional('result[2] not == result_2') +c3 = Conditional('result[0] neq not result_1') + + +class TestNotKeyword(unittest.TestCase): + def test_negate_instance_variable_assignment(self): + assert c1.negate is False and c2.negate is True + + def test_key_value_instance_variable_assignment(self): + c1_assignments = c1.key == 'result[1]' and c1.value == 'result_2' + c2_assignments = c2.key == 'result[2]' and c2.value == 'result_2' + assert c1_assignments and c2_assignments + + def test_conditionals_w_not_keyword(self): + assert c1(test_results) and c2(test_results) and c3(test_results) + +if __name__ == '__main__': + unittest.main()