Repository: ansible/ansible

Issue:
add a regex Jinja2 filter
Folks would like to do

```
foo | search(some_regex)
```

in expressions from Jinja2 and re.search does not work.

Need to test if foo.search is already avail in regular templates and then it may just be a matter of importing in playbooks to enable foo.search



Discussion:
Jinja2 does not have any regex filters.  I have written `match` and `search` filters for use in my playbooks.  However for my use case, I am returning bool().  What are others looking for?  Would you prefer match groups or are sticking with booleans sufficient?

When statements want a True/False value, so I think that would cover nearly all of the cases, want to submit a pull request for "core.py" ?

Absolutely.  I am putting some final touches on the code and adding test cases.

Pull request has been added: https://github.com/ansible/ansible/pull/4288

``` shell
$ nosetests test/
.........................................................................S..............................................
----------------------------------------------------------------------
Ran 120 tests in 28.143s

OK (SKIP=1)
```

``` shell
$ nosetests -v test/TestFilters.py
test_bool_no (TestFilters.TestFilters) ... ok
test_bool_none (TestFilters.TestFilters) ... ok
test_bool_true (TestFilters.TestFilters) ... ok
test_bool_yes (TestFilters.TestFilters) ... ok
test_match_case_insensitive (TestFilters.TestFilters) ... ok
test_match_case_sensitive (TestFilters.TestFilters) ... ok
test_match_no_match (TestFilters.TestFilters) ... ok
test_quotes (TestFilters.TestFilters) ... ok
test_regex (TestFilters.TestFilters) ... ok
test_search_case_insensitive (TestFilters.TestFilters) ... ok
test_search_case_sensitive (TestFilters.TestFilters) ... ok

----------------------------------------------------------------------
Ran 11 tests in 0.005s

OK
```


Resolving patch:
diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py
--- a/lib/ansible/runner/filter_plugins/core.py
+++ b/lib/ansible/runner/filter_plugins/core.py
@@ -22,6 +22,7 @@
 import types
 import pipes
 import glob
+import re
 from ansible import errors
 from ansible.utils import md5s
 
@@ -98,6 +99,27 @@ def fileglob(pathname):
     ''' return list of matched files for glob '''
     return glob.glob(pathname)
 
+def regex(value='', pattern='', ignorecase=False, match_type='search'):
+    ''' Expose `re` as a boolean filter using the `search` method by default.
+        This is likely only useful for `search` and `match` which already
+        have their own filters.
+    '''
+    if ignorecase:
+        flags = re.I
+    else:
+        flags = 0
+    _re = re.compile(pattern, flags=flags)
+    _bool = __builtins__.get('bool')
+    return _bool(getattr(_re, match_type, 'search')(value))
+
+def match(value, pattern='', ignorecase=False):
+    ''' Perform a `re.match` returning a boolean '''
+    return regex(value, pattern, ignorecase, 'match')
+
+def search(value, pattern='', ignorecase=False):
+    ''' Perform a `re.search` returning a boolean '''
+    return regex(value, pattern, ignorecase, 'search')
+
 class FilterModule(object):
     ''' Ansible core jinja2 filters '''
 
@@ -145,5 +167,10 @@ def filters(self):
 
             # file glob
             'fileglob': fileglob,
+
+            # regex
+            'match': match,
+            'search': search,
+            'regex': regex,
         }
     


Test patch:
diff --git a/test/TestFilters.py b/test/TestFilters.py
--- a/test/TestFilters.py
+++ b/test/TestFilters.py
@@ -89,6 +89,33 @@ def test_fileglob(self):
         a = ansible.runner.filter_plugins.core.fileglob(pathname)
         assert __file__ in a
 
+    def test_regex(self):
+        a = ansible.runner.filter_plugins.core.regex('ansible', 'ansible',
+                                                     match_type='findall')
+        assert a == True
+
+    def test_match_case_sensitive(self):
+        a = ansible.runner.filter_plugins.core.match('ansible', 'ansible')
+        assert a == True
+
+    def test_match_case_insensitive(self):
+        a = ansible.runner.filter_plugins.core.match('ANSIBLE', 'ansible',
+                                                     True)
+        assert a == True
+
+    def test_match_no_match(self):
+        a = ansible.runner.filter_plugins.core.match(' ansible', 'ansible')
+        assert a == False
+
+    def test_search_case_sensitive(self):
+        a = ansible.runner.filter_plugins.core.search(' ansible ', 'ansible')
+        assert a == True
+
+    def test_search_case_insensitive(self):
+        a = ansible.runner.filter_plugins.core.search(' ANSIBLE ', 'ansible',
+                                                      True)
+        assert a == True
+
     #def test_filters(self):
 
         # this test is pretty low level using a playbook, hence I am disabling it for now -- MPD.