예제 #1
0
class PFSenseModuleAggregate(object):
    """ module managing pfsense aggregated aliases, rules, rule separators, interfaces and vlans """
    def __init__(self, module):
        self.module = module
        self.pfsense = PFSenseModule(module)
        self.pfsense_aliases = PFSenseAliasModule(module, self.pfsense)
        self.pfsense_interfaces = PFSenseInterfaceModule(module, self.pfsense)
        self.pfsense_rules = PFSenseRuleModule(module, self.pfsense)
        self.pfsense_rule_separators = PFSenseRuleSeparatorModule(
            module, self.pfsense)
        self.pfsense_vlans = PFSenseVlanModule(module, self.pfsense)

    def _update(self):
        run = False
        cmd = 'require_once("filter.inc");\n'
        cmd += 'if (filter_configure() == 0) { \n'
        if self.pfsense_aliases.result['changed']:
            run = True
            cmd += 'clear_subsystem_dirty(\'aliases\');\n'
        if self.pfsense_interfaces.result['changed']:
            run = True
            cmd += 'clear_subsystem_dirty(\'interfaces\');\n'
        if self.pfsense_rules.changed or self.pfsense_rule_separators.result[
                'changed']:
            run = True
            cmd += 'clear_subsystem_dirty(\'filter\');\n'
        cmd += '}'
        if run:
            return self.pfsense.phpshell(cmd)

        return ('', '', '')

    def want_rule(self, rule_elt, rules):
        """ return True if we want to keep rule_elt """
        descr = rule_elt.find('descr')
        interface = rule_elt.find('interface')

        # probably not a rule
        if descr is None or interface is None:
            return True

        for rule in rules:
            if rule['state'] == 'absent':
                continue
            if rule['name'] == descr.text and self.pfsense.parse_interface(
                    rule['interface']) == interface.text:
                return True
        return False

    def want_rule_separator(self, separator_elt, rule_separators):
        """ return True if we want to keep separator_elt """
        name = separator_elt.find('text').text
        interface = separator_elt.find('if').text

        for separator in rule_separators:
            if separator['state'] == 'absent':
                continue
            if separator['name'] != name:
                continue
            if self.pfsense.parse_interface(
                    separator['interface']
            ) == interface or interface == 'floatingrules' and separator.get(
                    'floating'):
                return True
        return False

    @staticmethod
    def want_alias(alias_elt, aliases):
        """ return True if we want to keep alias_elt """
        name = alias_elt.find('name')
        alias_type = alias_elt.find('type')

        # probably not an alias
        if name is None or type is None:
            return True

        for alias in aliases:
            if alias['state'] == 'absent':
                continue
            if alias['name'] == name.text and alias['type'] == alias_type.text:
                return True
        return False

    def want_interface(self, interface_elt, interfaces):
        """ return True if we want to keep interface_elt """
        descr_elt = interface_elt.find('descr')
        if descr_elt is not None and descr_elt.text:
            name = descr_elt.text
        else:
            name = interface_elt.tag

        for interface in interfaces:
            if interface['state'] == 'absent':
                continue
            if interface['descr'] == name:
                return True
        return False

    @staticmethod
    def want_vlan(vlan_elt, vlans):
        """ return True if we want to keep vlan_elt """
        tag = int(vlan_elt.find('tag').text)
        interface = vlan_elt.find('if')

        for vlan in vlans:
            if vlan['state'] == 'absent':
                continue
            if vlan['vlan_id'] == tag and vlan['interface'] == interface.text:
                return True
        return False

    def run_rules(self):
        """ process input params to add/update/delete all rules """
        want = self.module.params['aggregated_rules']

        if want is None:
            return

        # delete every other rule if required
        if self.module.params['purge_rules']:
            todel = []
            for rule_elt in self.pfsense_rules.rules:
                if not self.want_rule(rule_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['name'] = rule_elt.find('descr').text
                    params['interface'] = rule_elt.find('interface').text
                    if rule_elt.find('floating') is not None:
                        params['floating'] = True
                    todel.append(params)

            for params in todel:
                self.pfsense_rules.run(params)

        # processing aggregated parameters
        for params in want:
            self.pfsense_rules.run(params)

    def run_aliases(self):
        """ process input params to add/update/delete all aliases """
        want = self.module.params['aggregated_aliases']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_aliases.run(param)

        # delete every other alias if required
        if self.module.params['purge_aliases']:
            todel = []
            for alias_elt in self.pfsense_aliases.aliases:
                if not self.want_alias(alias_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['name'] = alias_elt.find('name').text
                    todel.append(params)

            for params in todel:
                self.pfsense_aliases.run(params)

    def run_interfaces(self):
        """ process input params to add/update/delete all interfaces """
        want = self.module.params['aggregated_interfaces']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_interfaces.run(param)

        # delete every other if required
        if self.module.params['purge_interfaces']:
            todel = []
            for interface_elt in self.pfsense_interfaces.interfaces:
                if not self.want_interface(interface_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    descr_elt = interface_elt.find('descr')
                    if descr_elt is not None and descr_elt.text:
                        params['descr'] = descr_elt.text
                        todel.append(params)

            for params in todel:
                self.pfsense_interfaces.run(params)

    def run_rule_separators(self):
        """ process input params to add/update/delete all separators """
        want = self.module.params['aggregated_rule_separators']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_rule_separators.run(param)

        # delete every other if required
        if self.module.params['purge_rule_separators']:
            todel = []
            for interface_elt in self.pfsense_rule_separators.separators:
                for separator_elt in interface_elt:
                    if not self.want_rule_separator(separator_elt, want):
                        params = {}
                        params['state'] = 'absent'
                        params['name'] = separator_elt.find('text').text
                        if interface_elt.tag == 'floatingrules':
                            params['floating'] = True
                        else:
                            params['interface'] = interface_elt.tag
                        todel.append(params)

            for params in todel:
                self.pfsense_rule_separators.run(params)

    def run_vlans(self):
        """ process input params to add/update/delete all vlans """
        want = self.module.params['aggregated_vlans']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_vlans.run(param)

        # delete every other if required
        if self.module.params['purge_vlans']:
            todel = []
            for vlan_elt in self.pfsense_vlans.vlans:
                if not self.want_vlan(vlan_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['interface'] = vlan_elt.find('if').text
                    params['vlan_id'] = int(vlan_elt.find('tag').text)
                    todel.append(params)

            for params in todel:
                self.pfsense_vlans.run(params)

    def commit_changes(self):
        """ apply changes and exit module """
        stdout = ''
        stderr = ''
        changed = (self.pfsense_aliases.result['changed']
                   or self.pfsense_interfaces.result['changed']
                   or self.pfsense_rules.changed
                   or self.pfsense_rule_separators.result['changed']
                   or self.pfsense_vlans.result['changed'])

        if changed and not self.module.check_mode:
            self.pfsense.write_config(descr='aggregated change')
            (dummy, stdout, stderr) = self._update()

        result = {}
        result['result_aliases'] = self.pfsense_aliases.result['commands']
        result['result_interfaces'] = self.pfsense_interfaces.result[
            'commands']
        result['result_rules'] = self.pfsense_rules.result['commands']
        result['result_rule_separators'] = self.pfsense_rule_separators.result[
            'commands']
        result['result_vlans'] = self.pfsense_vlans.result['commands']
        result['changed'] = changed
        result['stdout'] = stdout
        result['stderr'] = stderr
        self.module.exit_json(**result)
class PFSenseModuleAggregate(object):
    """ module managing pfsense aggregated aliases, rules, rule separators, interfaces and vlans """

    def __init__(self, module):
        self.module = module
        self.pfsense = PFSenseModule(module)
        self.pfsense_aliases = PFSenseAliasModule(module, self.pfsense)
        self.pfsense_interfaces = PFSenseInterfaceModule(module, self.pfsense)
        self.pfsense_rules = PFSenseRuleModule(module, self.pfsense)
        self.pfsense_rule_separators = PFSenseRuleSeparatorModule(module, self.pfsense)
        self.pfsense_vlans = PFSenseVlanModule(module, self.pfsense)

    def _update(self):
        run = False
        cmd = 'require_once("filter.inc");\n'
        if self.pfsense_vlans.result['changed']:
            run = True
            cmd += self.pfsense_vlans.get_update_cmds()

        if self.pfsense_interfaces.result['changed']:
            run = True
            cmd += self.pfsense_interfaces.get_update_cmds()

        cmd += 'if (filter_configure() == 0) { \n'
        if self.pfsense_aliases.result['changed']:
            run = True
            cmd += 'clear_subsystem_dirty(\'aliases\');\n'
        if self.pfsense_rules.result['changed'] or self.pfsense_rule_separators.result['changed']:
            run = True
            cmd += 'clear_subsystem_dirty(\'filter\');\n'
        cmd += '}'
        if run:
            return self.pfsense.phpshell(cmd)

        return ('', '', '')

    def want_rule(self, rule_elt, rules):
        """ return True if we want to keep rule_elt """
        descr = rule_elt.find('descr')
        interface = rule_elt.find('interface')

        # probably not a rule
        if descr is None or interface is None:
            return True

        for rule in rules:
            if rule['state'] == 'absent':
                continue
            if rule['name'] == descr.text and self.pfsense.parse_interface(rule['interface']) == interface.text:
                return True
        return False

    def want_rule_separator(self, separator_elt, rule_separators):
        """ return True if we want to keep separator_elt """
        name = separator_elt.find('text').text
        interface = separator_elt.find('if').text

        for separator in rule_separators:
            if separator['state'] == 'absent':
                continue
            if separator['name'] != name:
                continue
            if self.pfsense.parse_interface(separator['interface']) == interface or interface == 'floatingrules' and separator.get('floating'):
                return True
        return False

    @staticmethod
    def want_alias(alias_elt, aliases):
        """ return True if we want to keep alias_elt """
        name = alias_elt.find('name')
        alias_type = alias_elt.find('type')

        # probably not an alias
        if name is None or type is None:
            return True

        for alias in aliases:
            if alias['state'] == 'absent':
                continue
            if alias['name'] == name.text and alias['type'] == alias_type.text:
                return True
        return False

    def want_interface(self, interface_elt, interfaces):
        """ return True if we want to keep interface_elt """
        descr_elt = interface_elt.find('descr')
        if descr_elt is not None and descr_elt.text:
            name = descr_elt.text
        else:
            name = interface_elt.tag

        for interface in interfaces:
            if interface['state'] == 'absent':
                continue
            if interface['descr'] == name:
                return True
        return False

    @staticmethod
    def want_vlan(vlan_elt, vlans):
        """ return True if we want to keep vlan_elt """
        tag = int(vlan_elt.find('tag').text)
        interface = vlan_elt.find('if')

        for vlan in vlans:
            if vlan['state'] == 'absent':
                continue
            if vlan['vlan_id'] == tag and vlan['interface'] == interface.text:
                return True
        return False

    @staticmethod
    def is_filtered(interface_filter, floating_filter, params):
        if interface_filter is None:
            return False

        if 'floating' in params:
            if isinstance(params['floating'], str):
                floating = params['floating'].lower()
            else:
                floating = 'true' if params['floating'] else 'false'

            if floating != 'false' and floating != 'no':
                return not floating_filter

        return floating_filter or params['interface'].lower() != interface_filter

    def run_rules(self):
        """ process input params to add/update/delete all rules """

        want = self.module.params['aggregated_rules']
        interface_filter = self.module.params['interface_filter'].lower() if self.module.params.get('interface_filter') is not None else None
        floating_filter = False
        if interface_filter is not None and interface_filter.lower() == 'floating':
            floating_filter = True

        if want is None:
            return

        # delete every other rule if required
        if self.module.params['purge_rules']:
            todel = []
            for rule_elt in self.pfsense_rules.root_elt:
                if not self.want_rule(rule_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['name'] = rule_elt.find('descr').text

                    if rule_elt.find('floating') is not None:
                        params['floating'] = True
                        interfaces = rule_elt.find('interface').text.split(',')
                        params['interface'] = list()
                        for interface in interfaces:
                            target = self.pfsense.get_interface_display_name(interface, return_none=True)
                            if target is not None:
                                params['interface'].append(target)
                            else:
                                params['interface'].append(interface)
                        params['interface'] = ','.join(params['interface'])
                    else:
                        params['interface'] = self.pfsense.get_interface_display_name(rule_elt.find('interface').text, return_none=True)

                    if params['interface'] is None:
                        continue

                    todel.append(params)

            for params in todel:
                if self.is_filtered(interface_filter, floating_filter, params):
                    continue
                self.pfsense_rules.run(params)

        # generating order if required
        if self.module.params.get('order_rules'):
            last_rules = dict()
            for params in want:
                if params.get('before') is not None or params.get('after') is not None:
                    self.module.fail_json(msg="You can't use after or before parameters on rules when using order_rules (see {0})".format(params['name']))

                if params.get('state') == 'absent':
                    continue

                if params.get('floating'):
                    key = 'floating'
                else:
                    key = params['interface']

                # first rule on interface
                if key not in last_rules:
                    params['after'] = 'top'
                    last_rules[key] = params['name']
                    continue

                params['after'] = last_rules[key]
                last_rules[key] = params['name']

        # processing aggregated parameters
        for params in want:
            if self.is_filtered(interface_filter, floating_filter, params):
                continue
            self.pfsense_rules.run(params)

    def run_aliases(self):
        """ process input params to add/update/delete all aliases """
        want = self.module.params['aggregated_aliases']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_aliases.run(param)

        # delete every other alias if required
        if self.module.params['purge_aliases']:
            todel = []
            for alias_elt in self.pfsense_aliases.root_elt:
                if not self.want_alias(alias_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['name'] = alias_elt.find('name').text
                    todel.append(params)

            for params in todel:
                self.pfsense_aliases.run(params)

    def run_interfaces(self):
        """ process input params to add/update/delete all interfaces """
        want = self.module.params['aggregated_interfaces']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_interfaces.run(param)

        # delete every other if required
        if self.module.params['purge_interfaces']:
            todel = []
            for interface_elt in self.pfsense_interfaces.root_elt:
                if not self.want_interface(interface_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    descr_elt = interface_elt.find('descr')
                    if descr_elt is not None and descr_elt.text:
                        params['descr'] = descr_elt.text
                        todel.append(params)

            for params in todel:
                self.pfsense_interfaces.run(params)

    def run_rule_separators(self):
        """ process input params to add/update/delete all separators """
        want = self.module.params['aggregated_rule_separators']
        interface_filter = self.module.params['interface_filter'].lower() if self.module.params.get('interface_filter') is not None else None
        floating_filter = False
        if interface_filter is not None and interface_filter.lower() == 'floating':
            floating_filter = True

        if want is None:
            return

        # processing aggregated parameter
        for params in want:
            if self.is_filtered(interface_filter, floating_filter, params):
                continue
            self.pfsense_rule_separators.run(params)

        # delete every other if required
        if self.module.params['purge_rule_separators']:
            todel = []
            for interface_elt in self.pfsense_rule_separators.separators:
                for separator_elt in interface_elt:
                    if not self.want_rule_separator(separator_elt, want):
                        params = {}
                        params['state'] = 'absent'
                        params['name'] = separator_elt.find('text').text
                        if interface_elt.tag == 'floatingrules':
                            params['floating'] = True
                        else:
                            params['interface'] = self.pfsense.get_interface_display_name(interface_elt.tag, return_none=True)
                            if params['interface'] is None:
                                continue
                        todel.append(params)

            for params in todel:
                if self.is_filtered(interface_filter, floating_filter, params):
                    continue
                self.pfsense_rule_separators.run(params)

    def run_vlans(self):
        """ process input params to add/update/delete all vlans """
        want = self.module.params['aggregated_vlans']

        if want is None:
            return

        # processing aggregated parameter
        for param in want:
            self.pfsense_vlans.run(param)

        # delete every other if required
        if self.module.params['purge_vlans']:
            todel = []
            for vlan_elt in self.pfsense_vlans.root_elt:
                if not self.want_vlan(vlan_elt, want):
                    params = {}
                    params['state'] = 'absent'
                    params['interface'] = vlan_elt.find('if').text
                    params['vlan_id'] = int(vlan_elt.find('tag').text)
                    todel.append(params)

            for params in todel:
                self.pfsense_vlans.run(params)

    def commit_changes(self):
        """ apply changes and exit module """
        stdout = ''
        stderr = ''
        changed = (
            self.pfsense_aliases.result['changed'] or self.pfsense_interfaces.result['changed'] or self.pfsense_rules.result['changed']
            or self.pfsense_rule_separators.result['changed'] or self.pfsense_vlans.result['changed']
        )

        if changed and not self.module.check_mode:
            self.pfsense.write_config(descr='aggregated change')
            (dummy, stdout, stderr) = self._update()

        result = {}
        result['result_aliases'] = self.pfsense_aliases.result['commands']
        result['result_interfaces'] = self.pfsense_interfaces.result['commands']
        result['result_rules'] = self.pfsense_rules.result['commands']
        result['result_rule_separators'] = self.pfsense_rule_separators.result['commands']
        result['result_vlans'] = self.pfsense_vlans.result['commands']
        result['changed'] = changed
        result['stdout'] = stdout
        result['stderr'] = stderr
        self.module.exit_json(**result)