Parsing dhcpd.conf with textX












0















I'm using https://github.com/igordejanovic/textX to parse dhcpd.conf file (no, https://pypi.org/project/iscconf/ does not work for me, it crashes on my dhcpd.conf file), specifically extract hosts with fixed addresses.



Records are like:



    host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}

host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}


Code:



def get_hosts(s):
grammar = """
config: hosts*=host ;

host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?

'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'

('option host-name' option_host_name=STRING';')?

('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(s)
for host in model.hosts:
print host.hostname, host.fixed_address


Now, I cannot parse entire dhcpd.conf with this grammar (obviously, I get syntax errors since there are so many other elements in the file that grammar does not account for); on the other hand, I do not want to construct complete grammar for this file, as I need only extraction of specific type of host records.



I can certainly extract just the host records using regular expressions and parse them separately, but I wonder if there is some way to make textX extract only host records out of the file and ignore the rest of the content?










share|improve this question























  • Why don't you ask the author of textX or go to his support/documentation page for information.

    – sln
    Nov 19 '18 at 18:39
















0















I'm using https://github.com/igordejanovic/textX to parse dhcpd.conf file (no, https://pypi.org/project/iscconf/ does not work for me, it crashes on my dhcpd.conf file), specifically extract hosts with fixed addresses.



Records are like:



    host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}

host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}


Code:



def get_hosts(s):
grammar = """
config: hosts*=host ;

host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?

'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'

('option host-name' option_host_name=STRING';')?

('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(s)
for host in model.hosts:
print host.hostname, host.fixed_address


Now, I cannot parse entire dhcpd.conf with this grammar (obviously, I get syntax errors since there are so many other elements in the file that grammar does not account for); on the other hand, I do not want to construct complete grammar for this file, as I need only extraction of specific type of host records.



I can certainly extract just the host records using regular expressions and parse them separately, but I wonder if there is some way to make textX extract only host records out of the file and ignore the rest of the content?










share|improve this question























  • Why don't you ask the author of textX or go to his support/documentation page for information.

    – sln
    Nov 19 '18 at 18:39














0












0








0








I'm using https://github.com/igordejanovic/textX to parse dhcpd.conf file (no, https://pypi.org/project/iscconf/ does not work for me, it crashes on my dhcpd.conf file), specifically extract hosts with fixed addresses.



Records are like:



    host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}

host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}


Code:



def get_hosts(s):
grammar = """
config: hosts*=host ;

host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?

'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'

('option host-name' option_host_name=STRING';')?

('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(s)
for host in model.hosts:
print host.hostname, host.fixed_address


Now, I cannot parse entire dhcpd.conf with this grammar (obviously, I get syntax errors since there are so many other elements in the file that grammar does not account for); on the other hand, I do not want to construct complete grammar for this file, as I need only extraction of specific type of host records.



I can certainly extract just the host records using regular expressions and parse them separately, but I wonder if there is some way to make textX extract only host records out of the file and ignore the rest of the content?










share|improve this question














I'm using https://github.com/igordejanovic/textX to parse dhcpd.conf file (no, https://pypi.org/project/iscconf/ does not work for me, it crashes on my dhcpd.conf file), specifically extract hosts with fixed addresses.



Records are like:



    host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}

host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}


Code:



def get_hosts(s):
grammar = """
config: hosts*=host ;

host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?

'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'

('option host-name' option_host_name=STRING';')?

('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(s)
for host in model.hosts:
print host.hostname, host.fixed_address


Now, I cannot parse entire dhcpd.conf with this grammar (obviously, I get syntax errors since there are so many other elements in the file that grammar does not account for); on the other hand, I do not want to construct complete grammar for this file, as I need only extraction of specific type of host records.



I can certainly extract just the host records using regular expressions and parse them separately, but I wonder if there is some way to make textX extract only host records out of the file and ignore the rest of the content?







python regex textx






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 19 '18 at 17:33









LetMeSOThat4ULetMeSOThat4U

2,61521951




2,61521951













  • Why don't you ask the author of textX or go to his support/documentation page for information.

    – sln
    Nov 19 '18 at 18:39



















  • Why don't you ask the author of textX or go to his support/documentation page for information.

    – sln
    Nov 19 '18 at 18:39

















Why don't you ask the author of textX or go to his support/documentation page for information.

– sln
Nov 19 '18 at 18:39





Why don't you ask the author of textX or go to his support/documentation page for information.

– sln
Nov 19 '18 at 18:39












1 Answer
1






active

oldest

votes


















0














textX author here. I'm not a frequent visitor of SO :). You can play around with regex matches and regex lookaheads to consume unwanted content. Here is a complete example that correctly handles intermediate text even if there is keyword host. Rule config first consume a char if there is not a word host ahead, and this repeats due to zero or more operator. When we get a word host we try do match host rule one or more times and collect all host objects, if the rule didn't succeed at least one (notice the usage of +=) we consume word host and repeat the process. This can probably be done better (more performant) but you get the idea. When doing this kind of stuff it's good to know that textX by default consume whitespaces but you can turn this off either globaly or per-rule using noskipws (see the docs).



from textx import metamodel_from_str


def test_get_hosts():
grammar = r"""
config: ( /(?!host)./ | hosts+=host | 'host' )* ;

host: 'host' hostname=ID '{'
(
('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'
('option host-name' option_host_name=STRING';')?
('ddns-hostname' ddns_hostname=STRING';')?
)#
'}'
;
"""
conf_file = r"""
host example1 {
option host-name "example1";
ddns-hostname "example1";
fixed-address 192.168.1.181;
}

some arbitrary content in between
with word host but that fails to match host config.

host example2 {
hardware ethernet aa:bb:ff:20:fa:13;
fixed-address 192.168.1.191;
option host-name "example2";
ddns-hostname "example2";
}
"""
mm = metamodel_from_str(grammar)
model = mm.model_from_str(conf_file)
assert len(model.hosts) == 2
for host in model.hosts:
print(host.hostname, host.fixed_address)


if __name__ == "__main__":
test_get_hosts()


Edit: Here are two more ideas for config rule:
A simple one:



config: ( hosts+=host | /./ )* ;


And (probably) a more performant that consume as much as it can using regex engine before trying host:



config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
/(?s).*/;





share|improve this answer

























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53379893%2fparsing-dhcpd-conf-with-textx%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    textX author here. I'm not a frequent visitor of SO :). You can play around with regex matches and regex lookaheads to consume unwanted content. Here is a complete example that correctly handles intermediate text even if there is keyword host. Rule config first consume a char if there is not a word host ahead, and this repeats due to zero or more operator. When we get a word host we try do match host rule one or more times and collect all host objects, if the rule didn't succeed at least one (notice the usage of +=) we consume word host and repeat the process. This can probably be done better (more performant) but you get the idea. When doing this kind of stuff it's good to know that textX by default consume whitespaces but you can turn this off either globaly or per-rule using noskipws (see the docs).



    from textx import metamodel_from_str


    def test_get_hosts():
    grammar = r"""
    config: ( /(?!host)./ | hosts+=host | 'host' )* ;

    host: 'host' hostname=ID '{'
    (
    ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
    'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'
    ('option host-name' option_host_name=STRING';')?
    ('ddns-hostname' ddns_hostname=STRING';')?
    )#
    '}'
    ;
    """
    conf_file = r"""
    host example1 {
    option host-name "example1";
    ddns-hostname "example1";
    fixed-address 192.168.1.181;
    }

    some arbitrary content in between
    with word host but that fails to match host config.

    host example2 {
    hardware ethernet aa:bb:ff:20:fa:13;
    fixed-address 192.168.1.191;
    option host-name "example2";
    ddns-hostname "example2";
    }
    """
    mm = metamodel_from_str(grammar)
    model = mm.model_from_str(conf_file)
    assert len(model.hosts) == 2
    for host in model.hosts:
    print(host.hostname, host.fixed_address)


    if __name__ == "__main__":
    test_get_hosts()


    Edit: Here are two more ideas for config rule:
    A simple one:



    config: ( hosts+=host | /./ )* ;


    And (probably) a more performant that consume as much as it can using regex engine before trying host:



    config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
    /(?s).*/;





    share|improve this answer






























      0














      textX author here. I'm not a frequent visitor of SO :). You can play around with regex matches and regex lookaheads to consume unwanted content. Here is a complete example that correctly handles intermediate text even if there is keyword host. Rule config first consume a char if there is not a word host ahead, and this repeats due to zero or more operator. When we get a word host we try do match host rule one or more times and collect all host objects, if the rule didn't succeed at least one (notice the usage of +=) we consume word host and repeat the process. This can probably be done better (more performant) but you get the idea. When doing this kind of stuff it's good to know that textX by default consume whitespaces but you can turn this off either globaly or per-rule using noskipws (see the docs).



      from textx import metamodel_from_str


      def test_get_hosts():
      grammar = r"""
      config: ( /(?!host)./ | hosts+=host | 'host' )* ;

      host: 'host' hostname=ID '{'
      (
      ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
      'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'
      ('option host-name' option_host_name=STRING';')?
      ('ddns-hostname' ddns_hostname=STRING';')?
      )#
      '}'
      ;
      """
      conf_file = r"""
      host example1 {
      option host-name "example1";
      ddns-hostname "example1";
      fixed-address 192.168.1.181;
      }

      some arbitrary content in between
      with word host but that fails to match host config.

      host example2 {
      hardware ethernet aa:bb:ff:20:fa:13;
      fixed-address 192.168.1.191;
      option host-name "example2";
      ddns-hostname "example2";
      }
      """
      mm = metamodel_from_str(grammar)
      model = mm.model_from_str(conf_file)
      assert len(model.hosts) == 2
      for host in model.hosts:
      print(host.hostname, host.fixed_address)


      if __name__ == "__main__":
      test_get_hosts()


      Edit: Here are two more ideas for config rule:
      A simple one:



      config: ( hosts+=host | /./ )* ;


      And (probably) a more performant that consume as much as it can using regex engine before trying host:



      config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
      /(?s).*/;





      share|improve this answer




























        0












        0








        0







        textX author here. I'm not a frequent visitor of SO :). You can play around with regex matches and regex lookaheads to consume unwanted content. Here is a complete example that correctly handles intermediate text even if there is keyword host. Rule config first consume a char if there is not a word host ahead, and this repeats due to zero or more operator. When we get a word host we try do match host rule one or more times and collect all host objects, if the rule didn't succeed at least one (notice the usage of +=) we consume word host and repeat the process. This can probably be done better (more performant) but you get the idea. When doing this kind of stuff it's good to know that textX by default consume whitespaces but you can turn this off either globaly or per-rule using noskipws (see the docs).



        from textx import metamodel_from_str


        def test_get_hosts():
        grammar = r"""
        config: ( /(?!host)./ | hosts+=host | 'host' )* ;

        host: 'host' hostname=ID '{'
        (
        ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
        'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'
        ('option host-name' option_host_name=STRING';')?
        ('ddns-hostname' ddns_hostname=STRING';')?
        )#
        '}'
        ;
        """
        conf_file = r"""
        host example1 {
        option host-name "example1";
        ddns-hostname "example1";
        fixed-address 192.168.1.181;
        }

        some arbitrary content in between
        with word host but that fails to match host config.

        host example2 {
        hardware ethernet aa:bb:ff:20:fa:13;
        fixed-address 192.168.1.191;
        option host-name "example2";
        ddns-hostname "example2";
        }
        """
        mm = metamodel_from_str(grammar)
        model = mm.model_from_str(conf_file)
        assert len(model.hosts) == 2
        for host in model.hosts:
        print(host.hostname, host.fixed_address)


        if __name__ == "__main__":
        test_get_hosts()


        Edit: Here are two more ideas for config rule:
        A simple one:



        config: ( hosts+=host | /./ )* ;


        And (probably) a more performant that consume as much as it can using regex engine before trying host:



        config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
        /(?s).*/;





        share|improve this answer















        textX author here. I'm not a frequent visitor of SO :). You can play around with regex matches and regex lookaheads to consume unwanted content. Here is a complete example that correctly handles intermediate text even if there is keyword host. Rule config first consume a char if there is not a word host ahead, and this repeats due to zero or more operator. When we get a word host we try do match host rule one or more times and collect all host objects, if the rule didn't succeed at least one (notice the usage of +=) we consume word host and repeat the process. This can probably be done better (more performant) but you get the idea. When doing this kind of stuff it's good to know that textX by default consume whitespaces but you can turn this off either globaly or per-rule using noskipws (see the docs).



        from textx import metamodel_from_str


        def test_get_hosts():
        grammar = r"""
        config: ( /(?!host)./ | hosts+=host | 'host' )* ;

        host: 'host' hostname=ID '{'
        (
        ('hardware ethernet' hardware_ethernet=/[0-9a-fA-F:]+/';')?
        'fixed-address' fixed_address=/([0-9]{1,3}.){3}[0-9]{1,3}/';'
        ('option host-name' option_host_name=STRING';')?
        ('ddns-hostname' ddns_hostname=STRING';')?
        )#
        '}'
        ;
        """
        conf_file = r"""
        host example1 {
        option host-name "example1";
        ddns-hostname "example1";
        fixed-address 192.168.1.181;
        }

        some arbitrary content in between
        with word host but that fails to match host config.

        host example2 {
        hardware ethernet aa:bb:ff:20:fa:13;
        fixed-address 192.168.1.191;
        option host-name "example2";
        ddns-hostname "example2";
        }
        """
        mm = metamodel_from_str(grammar)
        model = mm.model_from_str(conf_file)
        assert len(model.hosts) == 2
        for host in model.hosts:
        print(host.hostname, host.fixed_address)


        if __name__ == "__main__":
        test_get_hosts()


        Edit: Here are two more ideas for config rule:
        A simple one:



        config: ( hosts+=host | /./ )* ;


        And (probably) a more performant that consume as much as it can using regex engine before trying host:



        config: ( /(?s:.*?(?=host))/ hosts*=host | 'host' )*
        /(?s).*/;






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 19 '18 at 20:46

























        answered Nov 19 '18 at 20:03









        Igor DejanovićIgor Dejanović

        1264




        1264






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53379893%2fparsing-dhcpd-conf-with-textx%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?