@@ -0,0 +1,3049 @@
+# (c) 2015, Peter Sprygada %3Cpsprygada@ansible.com%3E%0D%0A#%0D%0A# This file is part of Ansible%0D%0A#%0D%0A# Ansible is free software: you can redistribute it and/or modify%0D%0A# it under the terms of the GNU General Public License as published by%0D%0A# the Free Software Foundation, either version 3 of the License, or%0D%0A# (at your option) any later version.%0D%0A#%0D%0A# Ansible is distributed in the hope that it will be useful,%0D%0A# but WITHOUT ANY WARRANTY; without even the implied warranty of%0D%0A# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the%0D%0A# GNU General Public License for more details.%0D%0A#%0D%0A# You should have received a copy of the GNU General Public License%0D%0A# along with Ansible.  If not, see %3Chttp://www.gnu.org/licenses/%3E.%0D%0A#%0D%0A%0D%0Afrom __future__ import (absolute_import, division, print_function)%0D%0A__metaclass__ = type%0D%0A%0D%0Aimport re%0D%0Aimport collections%0D%0A%0D%0Afrom ansible import errors%0D%0A%0D%0Adef parse_config(config, indent=1):%0D%0A    regexp = re.compile(r'%5E%5Cs*(.+)$')%0D%0A    line_re = re.compile(r'%5CS')%0D%0A    invalid_re = re.compile(r'%5E(!%7Cend)')%0D%0A%0D%0A    ancestors = list()%0D%0A    data = collections.OrderedDict()%0D%0A    banner = False%0D%0A%0D%0A    for line in config:%0D%0A        text = str(line).strip()%0D%0A%0D%0A        if not invalid_re.match(text):%0D%0A%0D%0A            # handle top level commands%0D%0A            if line_re.match(line):%0D%0A                data%5Btext%5D = collections.OrderedDict()%0D%0A                ancestors = %5Btext%5D%0D%0A%0D%0A            # handle sub level commands%0D%0A            else:%0D%0A                match = regexp.match(line)%0D%0A                if match:%0D%0A                    line_indent = match.start(1)%0D%0A                    level = int(line_indent / indent)%0D%0A%0D%0A                    try:%0D%0A                        ancestors%5Blevel%5D = text%0D%0A                    except IndexError:%0D%0A                        ancestors.append(text)%0D%0A%0D%0A                    try:%0D%0A                        child = data%5Bancestors%5B0%5D%5D%0D%0A                        for a in ancestors%5B1:level%5D:%0D%0A                            child = child%5Ba%5D%0D%0A                        child%5Btext%5D = collections.OrderedDict()%0D%0A                    except KeyError:%0D%0A                        # FIXME deal with indent inconsistencies%0D%0A                        if '_errors' not in data:%0D%0A                            data%5B'_errors'%5D = list()%0D%0A                        data%5B'_errors'%5D.append((ancestors, text))%0D%0A%0D%0A    return data%0D%0A%0D%0A%0D%0Adef config_block(value, ancestors, indent=1):%0D%0A    config = parse_config(value.split('%5Cn'), indent)%0D%0A    try:%0D%0A        for ancestor in ancestors.split('.'):%0D%0A            config = config%5Bancestor%5D%0D%0A        return config.keys()%0D%0A    except KeyError:%0D%0A        # raise errors.AnsibleFilterError('Config block not found for parent')%0D%0A        return None%0D%0A%0D%0A%0D%0Adef re_findall(value, regex):%0D%0A    return re.findall(regex, value, re.M)%0D%0A%0D%0A%0D%0Adef re_search(value, regex):%0D%0A    return re.search(regex, value, re.M)%0D%0A%0D%0A%0D%0Aclass FilterModule(object):%0D%0A%0D%0A    def filters(self):%0D%0A        return %7B%0D%0A            'config_block': config_block,%0D%0A            're_findall': re_findall,%0D%0A            're_search': re_search,%0D%0A        %7D%0D%0A

Initialize branch and add config_block filter