使用空间深度来解析配置文件与Perl的上下文

问题描述 投票:0回答:2

我一直在尝试创建一个配置文件解析器来解析Cisco IOS配置等。最终目标是基于配置文件中的过滤器在上下文中显示相关数据。例如,使用这样的配置文件,它将显示我们发现“access vlan”行作为“interface”上下文的子节点的所有接口,并且仅显示包含“speed”,“duplex”和“description”的行。

{
'Context' => '^interface',
'Types' => [
'Switch',
],
'Condition' => 'access vlan',
'Filter' => [
'speed',
'duplex',
'description'
]
};

到现在为止还挺好。我读了“running-config”并且我在数组中索引了行深度(假设非空行,不是以空格(\ s)开头的深度为0)。

然后,在另一个读取中,我使用该索引再次读取数据,这次使用基于深度的相对位置来创建上下文的“子”。这是功能:

sub getDeep { 

    my @data = (@_);
    my ($bighash,$hash); 

    #First read
    foreach my $idx (0.. $#data) { 

                    my ($spaces, $content) = ($data[$idx] =~  m/^(\s*)(.*)/); 
                    my $depth = length $spaces; 

                    $bighash->{node}{$idx}{depth} = $depth; 
    } 

    # Variables for the first read
    my $ldepth = 0; 
    my $lcontext; 
    my $lid; 

    # Second read
    foreach my $id (0 .. $#data) { 

                    $data[$id] =~ s/^\s*//; 
                    next if ($data[$id] =~ /^!/); 

                    my $depth = $bighash->{node}{$id}{depth}; 

                    if ($depth eq 0) { 
                                    push (@{$hash->{global}} , $data[$id]); 
                                    $lcontext = $data[$id]; 
                                    $lid = $id; 
                    } 

                    if (($depth gt 0) && ($id - $lid eq 1)) { 
                                    push (@{$hash->{$lcontext}}, (" " x $depth. $data[$id])); 
                                    $lid = $id; 
                    } 

    } 

    return $hash; 

}

使用这个子,我可以返回一个哈希值,然后根据给定键的arrayref的存在,按照解释应用过滤器。这很好用,到目前为止这段代码非常自豪。

当我想找到孩子的孩子时会出现问题。在下面的例子中,“给定param2”的孩子将代表我的下一个挑战。

interface XYZ
  given param1 -> child of "interface XYZ"
  given param2 -> child of "interface XYZ"
    given param2.1 -> child of "given param2"
    given param2.2 -> child of "given param2"
  given param3 -> child of "interface XYZ"

因此,在考虑了一段时间并且采用不同方法失败之后,我的问题分为两个部分:

1)有没有更好的方法来做到这一点,我没有看到? 2)我如何继续标记孩子的孩子,因为线条深入挖掘并在数据结构中正确识别它们?

感谢您阅读此行:)

perl parsing cisco
2个回答
0
投票

我已经写了一些事情要做到这一点。我无法弄清楚如何把它放在metacpan上。但是,如果我知道在哪里看,我肯定已经有更好的了。这是我在perl写的第一个,所以它有点混乱。但基本上你可以输入“gettree -l Node interface”,在XR设备上它会拉出所有的配置。 “gettree -s Node Description_keyword”将提取所有单个接口配置。您也可以将它与STDIN一起使用,例如“cat file | gettree -l interface”。

程序

#!/usr/bin/perl

use lib '/PATH_TO_Gettree.pm_MODULE/';

use strict;
use warnings;
use Gettree;
use Getopt::Std;

my %opts;
getopts('eislnpm' , \%opts);


my $searchstr;
my $filename;
my $debug=0;

if($ARGV[0]=~m/help/ || $ARGV[0]=~m/^\?$/ )
{   die usage();    }

if($#ARGV<0||$#ARGV>1)
{
    usage();
    killapp("Please specifiy node and search term, use --help for the help menu");
}
elsif($#ARGV==0)
{
    Gettree::setopts( \%opts , \$ARGV[0] );
    while(<STDIN>)
    {
        Gettree::gettree_stream_passline( \$_ );
    }
    print Gettree::gettree_getreturnstring();
}
else
{
    $filename= $ARGV[0];
    $filename="/CONFIGS_DIR/".lc $filename if ! $opts{e};
    print Gettree::gettree_file ( \%opts , \$filename , \$ARGV[1]) ;  #\$filename , $searchstring

}



sub killapp
{

    print $_[0]."\n";
    exit;
}
sub usage
{
    print "
    Usage: gettree [OPTION]... [NODE] STRING
    Search for PATTERN in each FILE or standard input.

        usage gettree <options> <node> <string>
    -s      include same level
    -l      include lower levels
    -n      show line numbers (do not use with STDIN, it wont work)
    -i      case insensitive
    -e      exact file location (rather than just the nodename)
    -p      print parent's same level lines
    -m      minimal print, do not print parents
    Examples:
        gettree Node text
        gettree -sln NODE CCT_Ref
        gettree -l NODE POS8/0
    \n\n";
    exit;
}

#!/usr/bin/perl


package Gettree;
use strict;
use warnings;


my $line;
my $wsdiff      = 0;
my $iopt        = 0;
my $sopt        = 0;
my $lopt        = 0;
my $nopt        = 0;
my $popt        = 0;
my $mopt        = 0;
my $linecounter = 0;
my $matched     = -1;
my $debug       = 0;    ##remove later
my @arr;
my @sopt_arr;
my @popt_arr;

my $searchstr;
my $returnstring;


sub setopts   # \%opthash , $searchstring
{
    cleardata();
    push @arr, [ 0, "",0];
    my %hash=%{$_[0]};

    $iopt   =   1 if $hash{i};
    $sopt   =   1 if $hash{s};
    $lopt   =   1 if $hash{l};
    $nopt   =   1 if $hash{n};
    $popt   =   1 if $hash{p};
    $mopt   =   1 if $hash{m};
    if ( defined $hash{qopts} )
    {
        $iopt = 1 if $hash{qopts} =~ /i/;
        $lopt = 1 if $hash{qopts} =~ /l/;
        $nopt = 1 if $hash{qopts} =~ /n/;
        $sopt = 1 if $hash{qopts} =~ /s/;
        $popt = 1 if $hash{qopts} =~ /p/;
        $mopt = 1 if $hash{qopts} =~ /m/;
    }
    if ( ref($_[1]) )   {   $searchstr=$iopt? qr/${$_[1]}/i : qr/${$_[1]}/ ;    }
    else                {   $searchstr=$iopt? qr/$_[1]/i : qr/$_[1]/ ;          }
}

sub gettree_stream_passline  # \$line
{
    process_line(${$_[0]});
}
sub gettree_getreturnstring
{
    return $returnstring;
}
sub gettree_varable         #  \%opthash , \$text , $searchstring
{
    setopts($_[0] , $_[2]);
    my $str=${$_[1]};
    while($str=~m#(.*\n)#g)
    {
        process_line($1);
    }
    return $returnstring;
}
sub gettree_file        #  \%opthash , \$filename , $searchstring
{
    setopts($_[0] , $_[2]);
    my $filename;   
    if ( ref($_[1]) )   {   $filename=${$_[1]}; }
    else                {   $filename=$_[1] ;   }
    open FH, "<", $filename or die "\nFile ".$filename." cannot be found\nerror : ".$!."\n";

    while(my $text=<FH>)
    {
        process_line($text);
    }

    close FH;
    return $returnstring;
}

sub process_line
{
    $line=shift;
    if($line=~m/^([ \t]+)/) {   $wsdiff=length($1)  }
    else {              $wsdiff=0       };

    if($wsdiff>$arr[$#arr][0])
    {
        push @arr, [ $wsdiff , $line , $linecounter ];
        if ( $sopt || $popt )
        {
            @popt_arr=@sopt_arr if $popt;
            @sopt_arr=() if defined $sopt_arr[0];
        }
    }
    else
    {
        while( @arr && $arr[$#arr][0]>$wsdiff )
        {
            pop @arr;
            @sopt_arr=@popt_arr if ( $sopt || $popt );
            @popt_arr=() if $popt;
        }
        if($#arr<0)
        {
            push @arr, [ $wsdiff , $line, $linecounter ];
        }
        else
        {
            push @sopt_arr, $arr[$#arr] if $sopt || $popt ;
            $arr[$#arr]=[ $wsdiff , $line , $linecounter ];
        }
    }
@sopt_arr=() if $#sopt_arr>200;  ## to avoid filling the memory
@popt_arr=() if $#popt_arr>200; ## to avoid filling the memory
    ##used in l and s opts to print lines after match
    if($matched>=0)
    {
        if($wsdiff>$matched)
        {
            printline(\$line) if $lopt==1 ; 
        }
        elsif ($wsdiff<$matched)
        {
            $matched=-1;
        }
        else
        {
            if ($sopt )
            { printline(\$line) }
            else
            { $matched=-1  }
        }
    }
    if( $matched==-1 && $line=~m/$searchstr/ )
    {
        printtree();
        $matched=$wsdiff if $sopt || $lopt;
    }
    $linecounter++;

}
sub printtree 
{   
    if(!$mopt)
    {
        for (0..$#arr-(1+$popt))
        {
            printline( \$arr[$_][1] , \$arr[$_][2] );
        }
    }
    if($popt)
    {
        for (0..$#popt_arr)
        {
            printline( \$popt_arr[$_][1] , \$popt_arr[$_][2] );
        }
        printline(  \$arr[$#arr-1][1] , \$arr[$#arr-1][2] ); #print the parent
        @popt_arr=() ;
    }
    if($sopt)
    {
        for (0..$#sopt_arr)
        {
            printline( \$sopt_arr[$_][1] , \$sopt_arr[$_][2] );
        }
        @sopt_arr=() ;
    }
    printline( \$arr[$#arr][1] , \$arr[$#arr][2] );
    @arr=();
    push @arr, [ $wsdiff , $line , $linecounter ];
}


sub printline
{
    $nopt==1?   $returnstring.= ${$_[1]}+1 ." : ".${$_[0]}  :   $returnstring.= ${$_[0]};
}

sub cleardata
{
    $line="";
    $wsdiff         = 0;
    $iopt        = 0;
    $sopt        = 0;
    $lopt        = 0;
    $nopt        = 0;
    $popt        = 0;
    $mopt        = 0;
    $linecounter = 0;
    $matched     = -1;
    @arr=();
    @sopt_arr=();
    @popt_arr=();
    $searchstr="";
    $returnstring="";
}
1;

Breif解释它是如何工作的程序只是模块的链接。我是一个模块,因为我已经在许多程序中使用它并且独立存在。 Gettree.pm将逐行发送数据到process_line()。进程线将获得空白区域($ wsdiff)并将其用作标记。直接在空格中增加之前的任何行都将存储在@arr中。如果稍后发现匹配,则进行打印。所以存储了父级。 @sopt_arr用于同一行,因此它存储了相同空格的前一行。 @popt_arr用于父匹配,但这不能很好地工作(我真的不使用它,它可以删除)。当匹配搜索字符串时,打印@ arr,Sopt_arr和@popt_arr,设置$ matched,这将用于-l选项。打印匹配后的所有行,直到空白区域<匹配的空白区域。总而言之,它将在增量之前采用每个唯一的空白区域。它也适用于Juniper和Alcatel。我相信它也适用于其他人。

记得修改CONFIGS_DIR和PATH_TO_Gettree.pm_MODULE以匹配您的文件系统路径


0
投票

这个帖子包含我希望的解决方案:)因为它可能对其他人有益,这里是链接:

https://perlmonks.org/?node_id=1224600

干杯!

© www.soinside.com 2019 - 2024. All rights reserved.