更佳编程之路: 第 9 章. 类和缺省解析器_VMware, Unix及操作系统讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  VMware, Unix及操作系统讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3338 | 回复: 0   主题: 更佳编程之路: 第 9 章. 类和缺省解析器        下一篇 
谁是天蝎
注册用户
等级:大元帅
经验:90210
发帖:106
精华:0
注册:2011-7-21
状态:离线
发送短消息息给谁是天蝎 加好友    发送短消息息给谁是天蝎 发消息
发表于: IP:您无权察看 2011-8-25 16:36:22 | [全部帖] [楼主帖] 楼主

groups 节是 cfengine 和 cfperl 不可或缺的部分。通过定义包含几组机器的类,可以向用户提供功能强大而又灵活的类机制。

作为全捕捉(catch-all)处理程序,缺省解析器是必要的。当 cfperl 没有专用于节的处理程序时,则将节传递给缺省解析器。

一切都是从全局解析器开始

全局解析器(我在先前的章节中讨论过它)创建 cfrun 原子。 cfrun 原子是单一的配置行(例如,“add this user”),它带有附加节和类条件执行(class-conditional execution)。例如,“add user”可能在“users”节中,并有可能仅用于“linux”类。

cfrun() 函数执行这些 cfrun 原子。但是,在执行功能原子之前, cfrun() 必需处理 import 和 group 语句。在 cfperl 的未来版本中,导入将和 cfengine 中一样可以根据条件完成。在这个版本中,导入是无条件的。

清单 1. 样本 import 和 group 语句


import:
# import the file 'cf.extra'
cf.extra
groups:
# declare the classes 'internal' and 'external' with
# corresponding member machines
internal = ( server1 server2 )
external = ( server3 server4 )
# declare the class 'primary' with member 'server5'
primary = ( server5 )
# this line will be processed only when 'primary' is defined
primary::
# define the 'secondary' class if we're running on a Sunday
secondary = ( Sunday )


清单 2. 在 cfrun() 中处理 import 和 control/group 语句

foreach my $cfrun_atom ( grep { $_->{section} eq IMPORT_SECTION } @cfrun_queue)
{
       dispatch(\%parsers, $cfrun_atom )
if allowed_cfrun_atom({ section => $cfrun_atom->{section},
       classes => undef, actual => $cfrun_atom->{section} }, $cfrun_atom);
       }
foreach my $cfrun_atom ( grep { $_->{section} eq CONTROL_SECTION ||
       $_->{section} eq GROUPS_SECTION } @cfrun_queue)
             {
                   dispatch(\%parsers, $cfrun_atom )
             if allowed_cfrun_atom({ section => $cfrun_atom->{section},
                   classes => undef, actual => $cfrun_atom->{section} }, $cfrun_atom);
                   }
                  


请参阅 cfperl CVS 资源库中的样本配置 cftest.conf 或用于说明导入如何在用户端工作的示例的 cfengine 样本配置(两者都在 参考资料一节中)。在 cfperl 中,由顶级解析器使用 load_file() 函数来处理导入。要简化程序流,必须首先执行导入。

在导入之后但在用户端功能原子之前处理 control 和 groups 节。例如, control: cfengine 节定义了 actionsequence 和用户定义的类。 actionsequence 声明 cfengine/cfperl 节将拥有何种顺序。cfperl 用 cfqueue 原子实现了 actionsequence ,该原子被插入称为 @cfrun_order 的数组中。为了声明 cfengine/cfperl 稍后将为条件执行解释的类, groups 节是必要的。

dispatch() 函数接受 cfrun 原子,并将它分派给适当的二级解析器。在本章稍后的部分中,我将解释 group 节二级解析器和空的缺省二级解析器。



缺省解析器

任何传递到 dispatch() 函数的 cfrun 原子都具有 section 属性。 dispatch() 函数将根据节为每个原子寻找适当的解析器。例如,专用于 users: 节的解析器能理解象 add_user 这样的命令。

在没有显式地定义处理 cfrun 原子的解析器的情况下,则调用缺省解析器。缺省解析器根本不进行任何处理 — 它只是个占位符。cfperl 是显式设计的,这样它可以补充 cfengine,所以根本不应该由 cfperl 处理诸如 editfiles: 和 files: 之类的 cfengine 节。因此,缺省解析器是由 dispatch() 调用的:

清单 3. 缺省解析器,有点象禅语,但更为简洁

$parsers{DEFAULT_SECTION()} = new Parse::RecDescent(q{ input: });


清单 4. dispatch() 调用适当的解析器,否则就调用缺省解析器

if (exists $parsers->{$section})
{
       out (3, "dispatch: Invoking specific parser for section $section");
$retval = $parsers->{$section}->input($line);
}
else
{
die "No default parser found, quitting" unless exists $parsers->{DEFAULT_SECTION()};
out(3, "dispatch: Given section $section does not have a parser,
using default for command [$line]");
$retval = $parsers->{DEFAULT_SECTION()}->input($line);
}


作为有趣的副注:必须将 DEFAULT_SECTION 常数作为函数调用,否则 Perl 会认为您所指的是 字符串“DEFAULT_SECTION”而不是名为 DEFAULT_SECTION 的常数。



group 节解析器

groups: 节的解析器会根据其它类的存在来定义类。例如,仅当已经定义“b”的情况下,“a = ( b )”才会定义“a”。cfengine/cfperl 的基于组的类定义能力具有十分强大的功能。

清单 5. group 节解析器

$parsers{GROUPS_SECTION()} = new Parse::RecDescent (q{
       input: define_group
       define_group: class '=' '(' class_definition ')'
{ ::define_group($item{class}, $item{class_definition}); 1; }
             class_definition: class(s)
             class: word
             word: /\w+/
      });
      


与所有其它 cfperl 解析器一样,这个解析器也有 input() 方法。这里, input() 只能做一件事情:定义组。请记住,使用 Parse::RecDescent 模块时,所有规则同时也是方法,因此我们将 input() 看作方法。

define_group 规则是基于类和 class_definition 规则的。类只能是一个字,这个字是由 Perl 认为是字( \w )的字符组成的,但如有必要,我们可以更改该定义。例如,如果希望连字符也成为有效的类字符,则可以将“word: /[\w-]+/”作为规则。

class_definition 规则至少由一个类名组成。这样,在 cfperl 的 groups: 节中,“groupname = ()”将不是有效的行。如果希望允许上述情况,则在规则中使用“class_definition: class(s?)”。但是我们没有这样做,因为这样没有意义。

define_group 规则使用 class 和 class_definition 来构建组定义操作。这比 cfengine 的组定义规则宽松得多,后者要求在圆括号前后留空格,因此“groupname = (linux)”是无效的。而 cfperl 将顺利地解析该行。如果希望强制 cfengine 的行为,可以使用下列代码:

清单 6. 更严格的 define_group 规则

define_group: class '=' '(' /\s+/ class_definition /\s+/ ')'
{ ::define_group($item{class}, $item{class_definition}); 1; }


但是,在我看来,不需要那种更严格的行为。

define_group 规则使用 Parse::RecDescent 数据结构来构建外部 define_group() 函数的两个参数。注:正如其名称前面的两个冒号所示,外部 define_group() 函数是全局的。



define_group() 函数

全局(因此在 groups 节解析器外部) define_group() 函数接受两个变量。第一个是可能被定义的新类的名称,第二个是对现有类名的数组引用。

Parse::RecDescent 可以自动完成类名验证,但在我看来,外部函数更容易编码和理解。

清单 7. 全局 define_group() 函数

# {{{ define_group: insert a new group into the list of defined groups,
                  # if one of its conditionals is defined
                  sub define_group($$)
                  {
                         my $group_name = shift @_ || return undef;
                         my $conditional_names = shift @_ || return undef;
                         foreach my $conditional (@$conditional_names)
                         {
                         next unless exists $classes{$conditional};
                         $classes{$group_name} = 1;
                               return $group_name;
                         }
                         return undef;
                  }
            # }}}
            


注:“next unless”控制流以及我们调用 return() 的方式,我们对 @$conditional_names 数组进行最快的可能的遍历,然后对成功的匹配调用 return() 。



结束语

本章总结了对 cfperl 内幕的讨论。在接下来的章节中,我将讨论 cfperl 的功能,从 cfperl 的添加、删除以及更改用户和组的工具入手。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论