puppet系列(三)类、变量以及属性
https://blog.51niux.com/?id=105和https://blog.51niux.com/?id=106 已经重记录了puppet的安装,命令,目录结构,配置文件,资源详解等。
这里呢,对于puppet的类,以及书写范式,以及正则等做一些记录。
puppet的注释支持两种风格,一种是shell注释风格#,一种是C语言风格注释/* */
一、类的介绍
class 类名。上一章已经大量了引用了类的知识。
1.1 无参数类和有参数类的区别
之前记录的都是无参数的类,如:class notifytest1::notify_hello{},就是class 类名{},然后{}里面定义各种资源,也是我们经常用到的一种方式。
有参数class类,就是有参数传入,不过不怎么常用,如下面的例子:
class apache_install ($stat = 'installed') {
package {'apache':
ensure => $stat, #这就是当执行的时候,installed的参数会传入给stat,这就是传参。
name => 'httpd',
}
}
1.2 继承
puppet通过inherits关键字引入了继承的概念,继续可以减少重用代码,如集群初始化肯定是一样的,但是初始化完毕呢有的是要做web的,有的是要做代理的等,就是有相同的部分有不同的部分。
节点继承:
# cat /etc/puppet/manifests/nodes/base.pp
node base {
include base_package
}
node /192.168.1.*/ inherits base { #正常的这里不应该是192.168.1什么什么,应该是主机名,如郑州web服务就是,zzidc.web*.uc什么什么,这里以IP比较醒目一点。这里的意思就是首先的所有192.168.1段的IP继承了base里面的类base_package操作,然后自身呢再执行一下类apache_install 。这里有一点不太清楚,不支持192.168.1.\d+这种写法,所以我就用了*这种写法。这里如果只是像某一个节点继承,如:'192.168.1.103'就单引号括起来。
include apache_install
}
# cat /etc/puppet/modules/base_package/manifests/init.pp #这里只是为了举个例子测试一下结果,正常的这里应该是一个exec和file资源,file资源负责将我们要安装基础包的脚本下发到每个要初始化的客户端,然后再通过exec来执行这个脚本来进行初始化的操作。
class base_package{
package {'base':
name => 'lrzsz',
ensure => installed,
}
}
# cat /etc/puppet/modules/apache_install/manifests/init.pp #这里就是192.168.1.*自己本身的类了,执行apache的安装。
class apache_install{
package{'apache':
ensure => installed,
name => 'httpd',
}
}
下面是客户端的操作截图:
类的继承:
类继承也就是定义一个base类,然后其他的类来继承这个base类,如果其他类里面的参数跟base类里面有不一样的,那么就会去覆盖基础类的内容。现在不支持多重继承。
下面是引用权威指南里面的例子(最后/etc/passwd文件的的权限就是0644,属主是root,属组就变为了wheel,如果引用base::freebsd类的话。):
class base::linux{
file {‘/etc/passwd’:
owner =>’root’,
group=>’root’,
mode=>’0644’,
}
}
class base::freebsd inherits base::unix {
file[‘/etc/passwd’] {
group =>’wheel’,
}
}
博文来自:www.51niux.com
二、变量
2.1 变量介绍
变量是什么大家都很清楚,puppet里面变量名可以由字母、数字和下划线组成,且大小写敏感。
puppet变量必须以“$”为前缀,后接“=”进行复制,如file资源里面加内容,可以先定义一个变量:$content = "this is a content",双引号用于里面不是一个单纯的字符串,单引号就是将里面的内容单纯的作为一个字符串 ,然后file资源里面再让content => $content。再第二篇的file资源详解关于模板那里就有关于变量的操作举例,当变量包含再字符串里面的时候用{}括起来能更好的识别变量名,另外puppet里面变量也不能重复赋值。
2.2 变量作用域
变量并非在所有的代码范围中都是有效可用的,而限定这个变量使用有效范围就是作用域。同一个作用域中不可以声明相同的变量名。
top作用域:声明变量后可以再class类内核node节点内调用的作用域被称为top作用域,也可以称为全局作用域。
node作用域:在node节点中声明变量后只能在node节点中被调用,这种作用域被称为节点作用域。在多个节点中声明相同的变量时,同一时间只能有一个node节点被调用。
local作用域:变量声明后只能在class类内使用,这种域被称为local作用域或本地作用域。
作用域的优先级:
当相同的变量名出现在同一个代码文件中时,它的优先级是local作用域最高,node作用域次之,top作用域级别最低。
2.3 Facter变量
facter主要的内置变量:
# facter
architecture => x86_64 #CPU的位数,这里是64位的操作系统
blockdevice_sda_model => ST3300657SS #硬盘型号,这里是希捷ST3300657SS
blockdevice_sda_size => 300000000000 #硬盘大小,这里是300G
hostname => localhost #系统主机名
interfaces => bond0,em1,em2,lo,p1p1,p1p2 #系统存在的网络接口
ipaddress => 192.168.1.103 #IP地址
is_virtual => false #是否是虚拟机,false代表不是,true代表是
kernel => Linux #内核版本
kernelrelease => 3.10.0-327.el7.x86_64 #内核版本
memoryfree => 53.25 GB #系统内存剩余
memorysize => 62.75 GB #系统总内存
operatingsystem => CentOS #操作系统
operatingsystemmajrelease => 7 #操作系统系列
operatingsystemrelease => 7.2.1511 #操作系统具体版本
physicalprocessorcount => 2 #CPU物理数量
processorcount => 16 #CPU总核数
productname => PowerEdge R410 #服务器型号
puppetversion => 3.8.7 #puppet版本
rubyversion => 2.0.0 #ruby版本
selinux => false #selinux默认是不是开启状态,false表示不开启,true表示默认开启
serialnumber => B7YB83X #服务器的序列号
swapfree => 8.00 GB #swap分区剩余
swapsize => 8.00 GB #swap分区总大小
system_uptime => {"seconds"=>24109675, "hours"=>6697, "days"=>279, "uptime"=>"279 days"} #系统的开机时间
uptime => 279 days #uptime天数
uptime_days => 279 #uptime天数
uptime_hours => 6697 #uptime小时数
uptime_seconds => 24109675 #uptime秒数
2.4 puppet的内置变量
由puppet.conf配置文件定义,并在puppet代码文件中可以直接使用的变量称为内置变量。内置变量氛围agent端和master端。
agent端:
$enviromnent : 指定是生产环境、开发环境还是测试环境。默认是生成环境:production.
$clientcert : agent的certname信息。也就是agent的配置文件里面设置的自己的名称。
master端:
$servername : master的fqdn
$serverip:master的IP
$module_name : master中的模块名
三、数据类型
3.1 字符串类型
字符串类型需要以双引号或单引号进行声明。puppet默认的数据类型就是字符串类型,不能使用Puppet关键字。这里有几个特殊的符号作为变量值时,需要进行转义:
\$ : $符号
\”: 双引号
\' : 单引号
\\ : 反斜杠
\n : 回车换行符
\r : 回车换行
\t : tab键,一个tab键默认是7个空格
\s : 空格
3.2 数值类型(很少用)
数值类型是指定义成的数值形式的数据,这种数据可以直接进行加、减、乘、除等数学运算。
下面是puppet运算符的优先级从高到底排序:
! : 取反
In : 范围
* 和 / : 乘和除
- 和 + : 减和加
<<和>> : 左移和右移
== 和 != :等于和不同于
>= <=和>< :大于等于、小于等于和大于、小于
And : 与
Or : 或
3.3 正则表达式
在前面我们也简单用了下正则表达式,正则表达式我们在node哪里正则定义多主机的时候是很有用的,因为随着主机的众多不可能一个个的node定义下去。
[] : 用于描述范围(如[A-Z],表示范围在A~Z之间的大写字母)。
(): 用于包含正则表达式。
\w:用于描述字母或数字,相当于[0-9a-zA-Z]。
\W : 非字母或数字。
\s: 匹配[\t\n\r\f],其中(\t)为制表符、(\r)为回车符、(\n)为换行符、(\f)为换页符、(\s)表示匹配这些符号的简写方式。
\S : 匹配非空字符。
\d:匹配[0-9]数字。
\D : 匹配非数字。
\b : 匹配退格符。
\B : 非字边界。
*:前面元素出现0次或多次。
+:前面元素出现1次或多次。
{m,n}:前面匀速最少出现m此,最多出现n次.
?: 前面元素最多出现1次,等价于{0,1}。
|:与前面或后面表达式匹配。
i :表示忽略大小写。
博文来自:www.51niux.com
四、资源的公有属性
4.1 before和require资源公有属性
(这两个属性很重要的,有的时候好多资源都写到一个pp文件也就是一个class类里面,对于资源执行先后的顺序要求又比依赖的话,就要加上这两个共有属性来限定资源执行的顺序。)
before资源公有属性(当前者资源成功执行后,再通知下一个资源执行):
# cat /etc/puppet/manifests/nodes/test.pp #这里定义一个node,去加载test1类./etc/puppet目录下的manifests和modules目录中文件要以.pp结尾
node /192.168.1.\d+$/{
include test1 #include函数会根据puppet.conf文件中的modulepath参数搜索基础模块的模块目录路径并自动加载基础模块目录中的init.pp文件。
}
# cat /etc/puppet/modules/test1/manifests/init.pp #首先我们创建的模块是存放在modules下面的,另外一定要以我们上面node里面include制定的类来创建以test1命名的目录。然后此目录下面的.pp文件都要放到manifests目录下面,并且一定要先用init.pp文件来定义文件之间的关联关系。
class test1{ #test1相当于基础类,下面的test1::beforetest1表示模块类
include test1::beforetest1 #这就相当于给test1模块test1::beforetest1为test1模块加载了一个模块beforetest1,init.pp文件会加载beforetest1.pp文件
}
# cat /etc/puppet/modules/test1/manifests/beforetest1.pp #因为init.pp文件加载的是beforetest1,所以文件也要是beforetest1.pp。不然找不到。
class test1::beforetest1{ #这里的类名就要跟init.pp里面定义的一致了。
user{'test9':
uid => 609,
before => Exec["homedir"], #主要是这里,这里就是表示在上面的user资源的test9用户创建成功后,去调用一个exec资源,注意第一个字母要大写,标准格式就是。资源["标题"]。这里homedir就是exec资源的标题。
}
exec{"homedir": #这里的exec资源就对应着上面的调用。
command => "mkdir /home/test9",
path => ["/usr/bin","/usr/sbin","/bin","/sbin"],
}
}
下面是客户端执行结果截图:
require资源公有属性(在本资源执行之前,需要确认其他资源是否已经被成功执行):
# cat /etc/puppet/modules/test1/manifests/require1.pp #做这些类文件更改,puppetmaster是不同重启服务的。
class test1::require1{
file {'/opt/scripts':
ensure => directory,
}
file {'/opt/scripts/test.sh':
source => 'puppet://master.puppet/test1/test.sh',
mode => '755',
require => File["/opt/scripts"],
}
}
#这里就是定义了第一个file类,用来创建/opt/scripts目录,然后第二个file类是要发送一个test.sh文件到客户端的/opt/scripts目录下面去,所以我们就要确保此目录存在,所以require => File["/opt/scripts"] 就是在本资源'/opt/scripts/test.sh'发送脚本到客户端的操作执行之前,确保File["/opt/scripts"]执行了
下面是客户端的执行结果:
4.2 notify和subscire资源共有属性
这两个的主要功能是资源与资源之间的状态的通知,notify资源共有属性为主动通知,subscire资源共有属性为被动通知。
notify资源公有属性(主要用来主动通知其他资源本资源的状态):
# cat /etc/puppet/modules/test1/manifests/notifytest1.pp
class test1::notifytest1{
package {'ntp':
ensure => installed,
notify => Service['ntpd'],
}
service {'ntpd':
ensure => running,
enable => true,
}
}
#package资源的ntp包如果安装成功,就会通知service资源,service资源就会启动ntpd服务并且添加到开机自启动,否则package资源失败,后面的service资源会跳过执行。
下面是我估计将DNS注释掉,搞的一个失败后的操作截图:
subscire资源公有属性(与notify相反,用于被动通知,当依赖检查资源发生变化时,主动更新所在资源符状态。)
# cat /etc/puppet/modules/test1/manifests/subsciretest1.pp
class test1::subsciretest1{
package {'ntp':
ensure => installed,
}
service {'ntpd':
ensure => running,
enable => true,
subscribe => Package['ntp'],
}
}
#这个就是检查到Package的ntp安装成功,或者说ntp是存在的,service资源就会去发送变化,如果ntpd服务没有启动会让其启动。下面是测试效果:
4.3 其他
其他的简单介绍一下,有兴趣可以自己查询一下。
还有两种还算比较常用是stage资源共有属性和audit资源共有属性。
还有两种描述方式:
->:表示资源之间的先后关闭,等同于before和require资源。如:File['/tmp/test1'] -> exec['bash_test']
~> : 用于表示资源之间的通知,等同于notify和subscire两个资源公有属性。如在两个资源之间加一行:~>,用来表示~>上面的资源执行成功后下面的资源才执行,不然就跳过。
五、条件判断语句
5.1 if...elsif...else语句
下面用一个很常用的例子,就是关闭防火墙功能,Centos6和Centos7的防火墙名称一个叫Iptables,一个叫firewalld。
# cat /etc/puppet/modules/test1/manifests/init.pp
class test1{
if $operatingsystemmajrelease == '6'
{ include test1::centos6 }
elsif $operatingsystemmajrelease == '7'
{ include test1::centos7 }
else
{ include test1::default }
}
#这句话很好理解,就是如果客户端的系统大版本是6就加载centos6类文件,如果大版本是7,就加载centos7类文件,然后如果是其他的版本如Centos5等就加载默认的类文件。
# cat /etc/puppet/modules/test1/manifests/centos6.pp #其他的都不粘贴了都是一个意思,另外标题那里可以相同,因为每次只会加载一种而不会同时三个都加载引起冲突。
class test1::centos6{
service {'iptables':
ensure => false,
enable => false,
}
}
#同时注意还有if...,if...else,if...elsif...elsif...else等语法。同时还可以与正则表达式匹配,如 if $hostname =~ /^zzidc.web\d+/{} 。或者通过in取范围方式,如if $operatingsystem in ["CentOS","Redhat"]{}
5.2 case语句
# cat /etc/puppet/modules/test1/manifests/init.pp #还是关闭防火墙的操作,case语句就看着更简洁一点。
class test1{
case $operatingsystemmajrelease {
'7': { include test1::centos7 }
'6','5' : { include test1::centos6 }
default : { include test1::default }
}
}
5.3 selector语句
它和C语言或Ruby语言中的三元运算类似,意思是在两个选项中任选其中一个赋值。
# cat /etc/puppet/modules/test1/manifests/iptables_off.pp
class test1::iptables_off {
service{'iptables_off':
name => $operatingsystemmajrelease ? {
'7' => 'firewalld',
/(6|5)/ => 'iptables',
default => 'iptables',
},
ensure => false,
enable => false,
}
}
#这种就用于赋值了,name根据大版本来赋值,如果大版本是7,那么name就是firewalld,如果大版本是6或者5,那么守护程序名称就是iptables,剩下的走默认也是iptables。