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

时间:2018-10-24 03:15:29

标签: perl parsing cisco

我一直在尝试创建配置文件解析器来解析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的存在,应用解释的过滤器。效果很好,到目前为止,以这段代码为荣。

当我想找到一个孩子的时候,问题就来了。在下面的示例中,“给定参数2”的子代将代表我的下一个挑战。

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)随着线的深入挖掘以及如何在数据结构中正确识别它们,我如何继续为孩子标记标签?

感谢您阅读以下内容:)

2 个答案:

答案 0 :(得分:0)

我写了一些东西来做到这一点。我不知道如何将其放在metacpan上。但是,如果我知道去哪里看看,我肯定那里已经好了。这是我在perl中写的第一本文章,所以有点混乱。但是基本上,您可以键入“ gettree -l Node interface”,并且在XR设备上它将拉出所有配置。 “ gettree -s节点Description_keyword”将提取所有单个接口配置。您还可以将其与STDIN一起使用,例如“ cat文件| gettree -l接口”。

程序

#!/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选项。匹配之后的所有行都会打印,直到空白<匹配的空白为止。因此,总而言之,它将在递增之前占用每个唯一的空格。它也适用于瞻博网络和阿尔卡特。我相信它也会对其他人起作用。

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

答案 1 :(得分:0)

此线程包含了我所希望的解决方案:)由于它可能会使他人受益,因此,这里是链接:

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

干杯!