Как я могу обрабатывать блоки входного файла по отдельности? - PullRequest
0 голосов
/ 16 июня 2020

Я пытаюсь прочитать конфигурацию брандмауэра и управлять объектами, которые он определяет, используя Python, чтобы затем изменить имя объекта и экспортировать его в новый файл конфигурации. Пример ввода:

edit "host1"
  set subnet 10.0.0.10 255.255.255.255  
next
edit "host2"
  set subnet 10.0.0.11 255.255.255.255 
next

При чтении файла мне нужен способ затем перебирать каждый экземпляр "edit [...]" со следующими установленными атрибутами (которых может быть больше одного ). По сути, строка, содержащая «редактировать», означает начало нового объекта, а «следующий» означает конец указанного объекта. Цель состоит в том, чтобы проверить содержимое каждого объекта и переименовать его в зависимости от того, что он из себя представляет, исходя из предыдущего примера, вывод будет следующим:

edit "10.0.0.10"
  set subnet 10.0.0.10 255.255.255.255  
next
edit "10.0.0.11"
  set subnet 10.0.0.11 255.255.255.255 
next

Манипулирование самим текстом не должно быть слишком сложным. проблема, но в настоящее время я застрял в поиске способа обрабатывать каждый блок конфигурации индивидуально.

Я думал о преобразовании ввода в XML, а затем использовать ElementTree, но я надеюсь, что есть более элегантный способ сделать это без этого дополнительного шага, который я просто не вижу из-за отсутствия опыта программирования.

Заранее благодарим за любые комментарии, как решить эту проблему.

1 Ответ

1 голос
/ 16 июня 2020

Время парсера с рекурсивным спуском!

Если эти файлы конфигурации действительно настолько просты, вы можете использовать следующую грамматику:

start: block*
block: EDIT HOSTNAME rule* NEXT
HOSTNAME: /"[^"]"/
rule: SET SUBNET IP IP
IP: /\d+\.\d+\.\d+\.\d+/

Тогда парсер будет выглядеть так. Он автоматически перезаписывает все блоки edit, используя хост из правила set.

import re


class Parser:
    def __init__(self, tokens: list):
        self.tokens = tokens

    def _next(self) -> str:
        token = self.tokens[0]
        del self.tokens[0]

        return token

    def _peek(self) -> str:
        return self.tokens[0]

    def consume(self, value: str) -> str:
        token = self._next()

        if token == value:
            return token

        raise SyntaxError(f"Could not consume {value!r} given {token!r} + {self.tokens}")

    def consume_regex(self, regex) -> str:
        token = self._next()

        match, = regex.fullmatch(token).groups()

        return match

    def parse(self):
        edits = self.parse_start()

        assert not self.tokens

        return edits

    def parse_start(self):
        "start: edit*"

        edits = []

        while self.tokens:
            edit = self.parse_edit()
            edits.append(edit)

        return edits

    def parse_edit(self) -> str:
        "edit: EDIT HOST rule NEXT"

        HOST_REGEX = re.compile(r'^"([^"]*)"$')

        _EDIT = self.consume('edit')
        _HOST = self.consume_regex(HOST_REGEX)
        _rule = self.parse_rule()
        _NEXT = self.consume('next')

        # REWRITE HERE!
        rewritten = f'''
{_EDIT} "{_rule['IP']}"
    {_rule['type']} {_rule['subnet']} {_rule['IP']} {_rule['mask']}
{_NEXT}
        '''.strip()

        return rewritten


    def parse_rule(self) -> dict:
        "rule: SET rule_set"  # other rules can be added
        tok = self._peek()

        if tok == 'set':
            return self.parse_rule_set()

        raise SyntaxError(f'Unknown rule {tok!r}')

    def parse_rule_set(self) -> dict:
        "rule_set: SET SUBNET IP MASK"

        # this regex doesn't check the validity of the IP address,
        # but it's good enough here
        IP_REGEX = re.compile(r"^(\d+\.\d+\.\d+\.\d+)$")

        _SET = self.consume('set')
        _SUBNET = self.consume('subnet')
        _IP = self.consume_regex(IP_REGEX)
        _MASK = self.consume_regex(IP_REGEX)

        return {'type': 'set', 'subnet': _SUBNET, 'IP': _IP, 'mask': _MASK}



if __name__ == '__main__':
    data = '''
edit "host1"
  set subnet 10.0.0.10 255.255.255.255  
next
edit "host2"
  set subnet 10.0.0.11 255.255.255.255 
next
    '''

    ret = Parser(data.split()).parse()

    print('\n'.join(ret))

Вывод:

$ python3 test.py
edit "10.0.0.10"
    set subnet 10.0.0.10 255.255.255.255
next
edit "10.0.0.11"
    set subnet 10.0.0.11 255.255.255.255
next
$ 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...