perl合并yaml继承key
2013-05-21 10:23:43 阿炯

本站赞助商链接,请多关照。 前面的文章里,yaml做为程序的配置来使用,在定义不同的条目时,它们有一些选项是重复的,可以将它们的公共部分提取出来,让各个条目来引入公共定义的部分,这样yaml的记录行数会减少很多。

下边是在一个通过snmp方式取各个主机/交换机特定网口的流量,并设定其最大值,当超出开始报警。

---
default: &std
 ipaddr: '127.0.0.1'
 type: 'host'
 interface: ['eth0']
 tels: ['131620800xx','159019941yy']
 misc: {'rate': '5','period': [7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22],'unit': 'm'}
 threshold: {'in': 10,'out': 10}

hosts: ['sb83','sb78','zhru34']

sb83:  
 <<: *std
 ipaddr: '114.80.x.83'

sb78:  
 <<: *std
 ipaddr: '114.80.x.78'
 interface: ['eth0','eth1']
 
 ....

 zhru34:
 <<: *std
 ipaddr: '61.129.x.34'
 type: 'switch'
 threshold: {'in': 45,'out': 45}
 interface: ['GigabitEthernet0/1']
 
 ---
 
上面就是脚本的部分配置文件,使用了yaml格式:
 hosts:定义了具体的主机,而被定义的主机至少要提供其ip地址。

 default:相当于所有主机的模板,它定义了主机类型、网口、手机号码(用于向指定人发送短信)、进出流量阀值、其它选项(与crontab类似,定义了脚本的工作时间及频率)。

 余下的是各个主机的配置选项,它们都继承了模板中的定义,也可以重载这些定义,像zhru34那样:它是一台交换机的配置,与主机有较大的区别了。
 
但问题出现了,当取到某一主机的配置时,它并不能像如期那样定义项。取而代之的是用'<<'代替了模板中的项,模板中的项成为其hash value了。像这样:
  sb78:
  <<: *1
  interface:
  - eth0
  - eth1
  ipaddr: 114.80.x.78
 
这个'<<: *1'显然不是我们想要的,那只能写脚本来对其进行合并了:
sub merge{
my $rc=shift;
my $h=();
my %pm=%{$rc->{'<<'}};

for(keys %pm){
 #print "$_\n";
 $h->{$_}=$pm{$_};
}
delete $rc->{'<<'};
$h->{$_}=$rc->{$_} for( keys %{$rc});
return $h;
}#end of fun merge

for(@{get_main_qsh('hosts')}){
 my $s=merge(get_main_qsh($_));
 #print Dumper($s);
}

合并后Dump出的示例如下:
$VAR1 = {
  'threshold' => {
    'out' => 32,
    'in' => 16
  },
  'misc' => {
    'rate' => '5',
    'unit' => 'm',
    'period' => [
      12,
      16,
      19,
      20
    ]
  },
  'tels' => [
    '131620800xx','159019941yy'
  ],
  'interface' => [
    'eth0'
  ],
  'type' => 'host',
  'ipaddr' => '114.80.x.83'
};

注意:在调用merge函数之后,脚本里引入的的变量内容会发生改变:
my $mainconf=YAML::XS::LoadFile('ext.yaml');
print Dump($mainconf);
sub get_main_qsh{
 my $qsh=shift;
 return $mainconf->{$qsh};
}

即'$mainconf'的内容发生了改变,当再次使用'get_main_qsh'函数查询某主机的配置时,返回的内容仅有其定义的内容而没有模板中的了。

因此,解决的方法是在脚本里一次性生成主机的配置文件,或再次对yaml进行一次Load:
undef $mainconf;
$mainconf=YAML::XS::LoadFile('ext.yaml');

至此,解决了yaml格式文件里的继承问题。不知道这算不算perl处理yaml模块的一个bug,在perlmoks上也有人遇到同样的情况,作者也给出了解决方案。