perl合并yaml继承key
在前面的文章里,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上也有人遇到同样的情况,作者也给出了解决方案。