0
点赞
收藏
分享

微信扫一扫

centos7.4之saltstack的系列(三)之state相关

一、管理对象

  saltstack系统中管理对象叫做Target,在master上可以采用不同的Tatget去管理不同的minion。这些Target都是通过去管理和匹配Minion的ID来做一些集合。

  1.1、正则表达式

  # salt -E '[a-z].*' test.ping  #直接就是匹配字母开头的minion

  # salt -E 'a.*' test.ping    #匹配a开头的minion

  # salt -E '(a|z).*' test.ping   #匹配a或者z开头的minion,切记是开头而不是包含。

  centos7.4之saltstack的系列(三)之state相关_centos

  1.2  -L : 列表匹配

  #salt  -L  'dasha_ceshi_172.16.5.239,dasha_ceshi_172.16.5.240'  test.ping  ##同时让多个minion去执行,多minion之间用逗号隔开(这里只是ID,组的不行)。

  1.3  -G : grains匹配

  minions的Grains信息时在Minions服务启动的时候汇总给Master的,Grains是saltstack组建中非常重要的,因为在配置部署的过程中会经常使用它,Grains是saltstack记录minion的一些静态信息的组件,grains里面记录着每台minion的一些常用属性如CPU、内存、操作系统类型和版本等,可以通过grains.item查看某台minion的所有grains信息。

  # salt -G 'os:CentOS' test.ping  #让minion端操作系统是CentOS去执行,当然也支持正则表达式的方式

  # salt -G 'os:C*' test.ping   #如匹配操作系统是C开头的

  # salt -G 'osmajorrelease:[1-9]' test.ping   #匹配系统版本是数字的。

  centos7.4之saltstack的系列(三)之state相关_centos_02

  1.4  -N : 组匹配

cat  /etc/salt/master | grep nodegroups

  这里还可以使用正则及其他匹配方式,我这里写的是最简单,也是最繁琐的一种。

nodegroups:
ceshi1: dasha_ceshi_172.16.5.239
ceshi2: dasha_ceshi_172.16.5.240

  #salt -N ceshi1 test.ping #测试test这组。

  1.5  -C : 复合匹配

   解释:复合匹配的意思是,使用不同的判断条件精准的在指定的minion中执行。

#salt -C 'G@os:CentOS and G@osmajorrelease:7' test.ping  #让os是CentOS并且系统版本是7的去执行

#salt -C 'G@os:CentOS and G@osmajorrelease:7 and E@dasha_ceshi*' test.ping # 在操作系统是CentOS版本是7,并且id开头是dasha_ceshi的机器是执行test.ping操作。

  1.6  -S : CIDR网段匹配

[root@bogon ~]# salt -S '172.16.5.0/24' test.ping  #匹配IP是172.16.5.0网段的的所有IP地址,执行test.ping操作
dasha_ceshi_172.16.5.240:
True
dasha_ceshi_172.16.5.239:
True
[root@bogon ~]# salt -S '172.16.5.239' test.ping #在IP是172.16.5.239服务器上执行test.ping操作。
dasha_ceshi_172.16.5.239:
True

二、Grains介绍

   Grains上面已经介绍过了,类似于facter。简单的说就是把minion端在启动的时候把自身的的各种属性信息采集汇报给master端,可以使用 salt ‘id’ grains.items查看。

  2.1 grains模块的一些命令行的使用方法:

   # salt '*' grains.ls  #可以查看有哪些属性可以查看,显示的是属性的名字,类似于os之类的。

  #salt 'dasha_ceshi_172.16.5.239' grains.item os osrelease oscodename ##知道了属性的名称,我们就可以查看属性的值,这里就是查看os,osrelease,oscodename这三个属性的值,多属性用空格隔开。

  # salt '*' grains.items  #这种就是将所有minion端属性以及属性的值全打印出来,建议打印是指定某个minion id,否则打印的东西太多。

  2.2 通过minion端配置文件自定义grains:

  上面我们说了,在master上执行grains.items能查看到很多minion的参数,但是比如设备的具体位置,业务名称等信息就显的力不从心了,但是有了minion自定义grains后,就没有那么难了。

  minion端的操作:

grains:    #以grains开头
roles: #设置一个属性叫roles,下面如果多条就像下面一样- value值,这里的意思是标注服务器属于什么业务
- webserver
- memcache
deployment: bj-zb-5F #这里是标注所在机房的位置
cabinet: B11 #标注所在机柜的位置
cab_u: 19-21 #标注所在机柜的U位

  centos7.4之saltstack的系列(三)之state相关_apache_03

  # systemctl restart salt-minion   #上面已经说了,grains信息是在minion端启动的时候才会发送,所以要重启minion端。

   master端的查看:

  # salt 'dasha_ceshi_172.16.5.240'  grains.ls #用这个查看会发现我们自定义的哪几个grains的属性已经出现了。

  #salt '*' grains.item roles deployment cabinet cab_u  #查看我们在minion自定义的grains信息。

  centos7.4之saltstack的系列(三)之state相关_apache_04

  2.3 命令自定义grains(前面我们说了更改配置文件的自定义,下面我们来说使用命令自定义)

#salt 'dasha_ceshi_172.16.5.239' grains.append  purpose  'ceshi'  #通过grains.append方法为dasha_ceshi_172.16.5.239机器添加了一个属性,purpose  用途,后面跟的值是ceshi,注意空格和引号。

  这种自定义的方法配置文件位置再minion的/etc/salt/grains配置文件中,永久生效,删除的话,可以直接去minion操作。还可以使用其他的方法,如grains.setvals来定义多个属性,或者还可以删除自定义的grains。这都是立马生效并永久生效的。

三、Pillar介绍

  Pillar是saltstack组件中非常重要的一个,是数据管理中心,经常配合states在大规模的配置管理工作中使用它,它的主要作用是存储和定义配置管理中需要的一些数据。它的定义存储格式跟grains类似,都是YAML格式。

  下面的操作都是在master端的操作:

# mkdir /srv/pillar #这是master配置文件里面#pillar_roots:默认指定的路径。默认不存在需要手工创建。

# cat /srv/pillar/top.sls #top.sls是配置管理的入口文件,一切都是从这里开始,这里是默认位置,在这里定义谁来执行那个方法或者说模块。
base: #top.sls 默认从 base 标签开始解析执行,下一级是操作的目标,可以通过正则,grain模块,或分组名,来进行匹配,再下一级是要执行的state文件,不包换扩展名。

'*':
- pkgs
# cat /srv/pillar/pkgs.sls #一个简单的根据minion端的grains信息,动态的设置apache软件包对应的name名称。前面我们学习saltstack的minion,我们知道给客户端定义新的属性时候,是需要在客户端文件加内容的,但是pillar则是在服务器给客户端定义新属性,而且不用重启服务,
pkgs:

{% if grains['os_family'] == 'RedHat' %}
apache: httpd
{% elif grains['os_family'] == 'Debian' %}
apache: apache2
{% elif grains['os'] == 'Arch' %}
apache: apache
{% endif %}

# salt '*' pillar.item pkgs

  有结果来看,pillar数据是在Salt master上生成的并被安全地分布到minions上。Salt当定义pillar的时候,不必限制在sls文件,也可以从外部资源获得数据,我们可以把Pillar数据。pillar中最强大的抽象之一就是将states参数化的能力。

四、States介绍

  States是SaltStack中的配置语言,在日常进行配置管理时需要编写大量的States文件。如安装软件啊,更改配置啊等,就需要编写states sls文件(描述状态配置的文件)去描述和实现这些功能。编写states sls文件一般是是YAML语法格式,也支持使用Python语言来编写。

  4.1 相关的states内容

  #salt 'dasha_ceshi_172.16.5.239' sys.list_state_modules  ##查看minion支持的所有states列表。

centos7.4之saltstack的系列(三)之state相关_配置文件_05centos7.4之saltstack的系列(三)之state相关_配置文件_06

1 - acl
2 - alias
3 - alternatives
4 - archive
5 - artifactory
6 - beacon
7 - bigip
8 - blockdev
9 - buildout
10 - ceph
11 - chronos_job
12 - cloud
13 - cmd
14 - composer
15 - cron
16 - cryptdev
17 - csf
18 - disk
19 - elasticsearch
20 - elasticsearch_index
21 - elasticsearch_index_template
22 - environ
23 - esxdatacenter
24 - etcd
25 - ethtool
26 - event
27 - file
28 - firewall
29 - firewalld
30 - gem
31 - glassfish
32 - gnomedesktop
33 - gpg
34 - grafana4_dashboard
35 - grafana4_datasource
36 - grafana4_org
37 - grafana4_user
38 - grains
39 - group
40 - highstate_doc
41 - hipchat
42 - host
43 - http
44 - incron
45 - infoblox_a
46 - infoblox_cname
47 - infoblox_host_record
48 - infoblox_range
49 - ini
50 - ipset
51 - iptables
52 - jboss7
53 - jenkins
54 - junos
55 - k8s
56 - kernelpkg
57 - keyboard
58 - kmod
59 - ldap
60 - libcloud_dns
61 - libcloud_loadbalancer
62 - libcloud_storage
63 - locale
64 - logrotate
65 - loop
66 - lxc
67 - marathon_app
68 - modjk
69 - modjk_worker
70 - module
71 - mount
72 - msteams
73 - network
74 - nexus
75 - openstack_config
76 - opsgenie
77 - pagerduty
78 - pagerduty_escalation_policy
79 - pagerduty_schedule
80 - pagerduty_service
81 - pagerduty_user
82 - pkg
83 - pkgbuild
84 - pkgng
85 - pkgrepo
86 - powerpath
87 - process
88 - pushover
89 - pyenv
90 - rbenv
91 - rvm
92 - salt
93 - salt_proxy
94 - schedule
95 - serverdensity_device
96 - service
97 - slack
98 - smtp
99 - solrcloud
100 - sqlite3
101 - ssh_auth
102 - ssh_known_hosts
103 - stateconf
104 - status
105 - statuspage
106 - supervisord
107 - sysctl
108 - syslog_ng
109 - telemetry_alert
110 - test
111 - timezone
112 - tuned
113 - uptime
114 - user
115 - vault
116 - vbox_guest
117 - virtualenv
118 - winrepo
119 - zenoss

所有的state_modules

  #salt 'dasha_ceshi_172.16.5.239' sys.list_state_functions file host   ##查看file和host的所有function,多模块用空格隔开

centos7.4之saltstack的系列(三)之state相关_配置文件_05centos7.4之saltstack的系列(三)之state相关_配置文件_06

1 dasha_ceshi_172.16.5.239:
2 - file.absent
3 - file.accumulated
4 - file.append
5 - file.blockreplace
6 - file.cached
7 - file.comment
8 - file.copy
9 - file.decode
10 - file.directory
11 - file.exists
12 - file.line
13 - file.managed
14 - file.missing
15 - file.mknod
16 - file.mod_run_check_cmd
17 - file.not_cached
18 - file.patch
19 - file.prepend
20 - file.recurse
21 - file.rename
22 - file.replace
23 - file.retention_schedule
24 - file.sdecode
25 - file.serialize
26 - file.shortcut
27 - file.symlink
28 - file.touch
29 - file.uncomment
30 - host.absent
31 - host.only
32 - host.present

详细

  4.2查看相关的states内容的方法

  # salt 'agent1.salt' sys.list_state_modules  #查看minion支持的所有states列表。

  # salt 'agent1.salt' sys.list_state_functions file host  #查看file和host的所有function,多模块用空格隔开

  # salt 'agent1.salt' sys.state_doc file #查看指定模块的function的用法与例子,日常编写states按照例子编写便可。

  # salt 'agent1.salt' sys.state_doc file.managed #查看file.managed的详细用法与例子,这也是我们经常用到的function。

  4.2 编写一个简单的sls文件并执行

  现在我们有个需求,往minion的特定目录下传一个配置文件,前面我们说过使用cp.get_file方法就可以传文件,但是我们经常要给某些minion传特定的配置文件的话,使用state的文件传送就很方便。

  首先我们先来看一下master里面state路径定义的方法,我们一共定义了三个路径,base是必须要有的,剩下的随意,注意空格。注意:salt://这几个字符分别代表了里面的三个路径,比如salt://file/test,绝对路径就是/srv/salt/base/file/test,/srv/salt/prod/file/test,/srv/salt/test/file/test。

  centos7.4之saltstack的系列(三)之state相关_apache_09

  分别创建配置文件里面的三个路径,最后完成后一定要重启master服务。

  现在我们来看看写法,首先你要定义个需要传送到客户端的文件,可以新建,随便写点内容即可,首先我们分析一下基本环境,只需要看你标红位置即可。

  我在base里面又新建了一个目录files,我把my.cnf配置文件放在了这里面,然后我在base下面新建了衣蛾cp_file.sls文件,在这个文件中写了点传送文件的内容。

  centos7.4之saltstack的系列(三)之state相关_配置文件_10

  基本写法介绍:

cp-file-my.cnf:   #唯一表示ID
file.managed: #方法,我用哪个方法,这里用file.managed方法传送文件
- name: /root/my.cnf #目标机器上的哪个文件,一定要加文件名。
- source: salt://files/my.cnf #master端的路径,上线我们说过,这里的绝对路径是/srv/salt/base/files/my.cnf
- user: root #文件属主
- group: root #文件属组
- mode: 644 #文件权限

注意:salt所有配置文件里面禁止用tab键,层级关系用两个空格隔开,-符号后面还有个空格,例如- name: 冒号后面也得有空格。

  写好具体要做什么以后,这里要发送了

#state.sls是用state.sls这个方法,cp_file是只cp_file.sls这个文件,这里去掉文件.后面的内容即可,test=True指先测试一下,如果确定要发送的话,去掉test=True即可。
salt '*' state.sls cp_file test=True

这里可能有同学要说了,好麻烦啊,我直接scp过去就ok了,你这里还得写这么东东西,别着急,如果有1000台机器,你也一个一个scp么,而这里我只需要匹配哪些服务器需要即可。

centos7.4之saltstack的系列(三)之state相关_配置文件_11

#如果你源文件没有发生改变,再次执行的话,Comment会提示你这个文件是正确的。Changes:下面没内容。Succeeded: 1,这就是区别,也就是不更新


五、SLS文件介绍

SLS(代表SaLt State文件)是Salt State系统的核心。SLS描述了系统的目标状态,由格式简单的数据构成。这经常被称作配置管理,简单的讲就说你写了那么多的路径和sls文件,到底要让谁来执行top里面可以写谁来执行哪些sls文件。

5.1 YAML编写技巧

因为我们用YAML格式来编写sls文件,所以掌握编写技巧利于后面少出错。

5.1.1 什么是YAML?

YAML 语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式。语法很简单,结构通过空格来展示,项目使用“-”来代表,键值对使用“:”分隔。

它的基本语法规则如下:

大小写敏感,以“:”来分隔key和value。对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)。

使用固定的缩进风格表示层级关系。缩进时不允许使用Tab键,只允许使用空格,一般每个缩进级别由两个空格组成。

# 表示注释,从这个字符一直到行尾,都会被解析器忽略。

想要表示列表项,使用一个短横杠加一个空格。多个项目使用同样的缩进级别作为同一列表的一部分。列表可以作为一个键值对的value。

5.2 top.sls

top.sls是配置管理的入口文件,这个很重要在生产中很重要,默认存放在/srv/salt/目录.默认从base标签开始解析执行,下一级是操作的目标也就是要对哪些主机进行操作,可以通过正则、grain模块或分组名等来进行匹配,然后再下一级是要执行的state文件(不包含.sls扩展名)。

  需求分析:   

  这里需要说明一下,比如需要装一个LAMP环境,我分别在新建了/srv/salt/base,/srv/salt/prod/连个salt路径,我在prod里面又新建了web,php,mysql三个目录,分别在三个目录里面写我要做的事情。然后呢?我们要怎么传给所需的minion呢?难道要像上面讲的一个一个去执行么,No!太费事了,我在top文件里面定义好,谁要干什么活,然后执行top文件就行,这样一条命令解决。那问题来了,top里面如果去匹配哪些minion呢?下面有几种不同的配置方法。

5.2.1 正则匹配示例:

  基本写法1:

  首先top文件必须写在base环境下,必须要叫top.sls,然后我们看一下基本写法:

base:  #下面的sls在哪个路径这里就写什么
'dasha_ceshi_172.16.5.239' #哪个minion去执行
- cp_file #执行哪个方法

  基本写法2:

base:  
'dasha_ceshi_172.16.5.239'
- cp_file
'dasha_ceshi_172.16.5.240'
- cp_file

  正则写法1:

base:  #下面的sls在哪个路径这里就写什么
'dasha_ceshi*' #哪个minion去执行,不会正则的同学可以去看看
- cp_file #执行哪个方法

5.2.2 通过分组名进行匹配示例:

# cat /srv/salt/base/top.sls 

base:

   centosgroups:  #这里就是我们上面在master配置文件里面定义的那个组名

   - match: nodegroup  #这句话是必须要有的,指定以组匹配

   - cp_file 


5.2.3 通过grain模块匹配的示例:

# cat /srv/salt/base/top.sls   #同上-match: grain也是必须要带的,指定让其按照grain进行匹配,指定让os是Centos的操作系统来执行。    

base:

    'os:CentOS':

      - match: grain

      - cp_file 


5.2.4 通过复合匹配的示例:

# cat /srv/salt/base/top.sls #上面的grain模块匹配可能满足不了我们的匹配条件,我想要更加精确的让所有Centos6.4的操作系统去执行,就是我下面的设置。

base:

    'G@os:CentOS and G@osrelease:6.4':

        - match: compound

        - cp_file 


5.2.5 某一个网段匹配示例:

# cat /srv/salt/base/top.sls 

base:

    '192.168.1.0/24':

        - match: ipcidr

        - cp_file

5.3 state.highstate

# salt '*' state.highstate  #master将会指导所有的目标minions运行 state.highstate。当minion执行highstate,它将会下载top文件中匹配的内容,minion将表达式中匹配的内容下载、编译、执行。一旦完成,minion将返回所有的动作执行结果和所有更改。基本意思就是谁来执行什么操作,去看top文件里面是怎么写的,就怎么执行。

# salt '*' state.highstate test=True  #只是测试执行,类似于模拟,不会在minion真正执行,便于我们编写测试时使用,建议每次执行都测试一下。


5.5 state的层级关系

  include:将别的SLS添加到当前文件中,所以可以require或watch被引用的SLS中定义的内容,还可以extend覆盖其内容。include语句使得state可以跨文件引用。使用include相当于把被引用的内容文件添加到自身。

  extend:扩展被引用的SLS数据。不需要重头写新的SLS,可以先直接include sls文件,然后在其基础上增加或者覆盖内容。

include(包含):

  首先说应用场景,现在我在prod下面新建了三个目录web,php,mysql,里面分别写了init.sls(初始化环境配置),按照上面的写法,我需要一键执行的话,首先需要在top里面把它们加进去,如下图

  centos7.4之saltstack的系列(三)之state相关_配置文件_12

  top.sls

  centos7.4之saltstack的系列(三)之state相关_配置文件_13

  这样的话,如果我要安装的环境特别的多,这里都直接下载top里面就比较乱了,所以为了醒目,我在prod下直接新建一个lamp.sls文件,然后把这些操作文件都写进去,然后就方便了,然后只需要在top文件里面写上lamp就oK了,是不是很方便。

  centos7.4之saltstack的系列(三)之state相关_配置文件_14

  这里只会一种用法,生产中还有很多种用法。

extend(扩展):

   环境需求,如果已经写好了lamp的安装配置环境了,然后突然发现还有安装一个软件,这个时候直接去改文件就显的有点粗暴了,这里可以直接在include的里面写,例如,这里我需要给lamp环境下安装一个tree的包。

  其它的什么都不需要动,只需要在lamp下添加extend即可。

  这里注意,既然是扩展,那就得知道给谁扩展,include的中我们已经标注了要执行的操作是web,php,mysql下的init操作。但是扩展的还怎么写呢?extend:是标准写法,层级关系是两个空格,那这个id怎么写呢?有人说随便写,你写个试试,肯定不行。既然是扩展你得指定给谁扩展。我在php里面的init文件中写过php-install这个id,这里我指定是给它扩展,所以必须得把它的id写在这里,然后再写是什么方法,要干什么。这里需要注意的就是这个id写法,其实也不难理解,既然是扩展,肯定是要写给谁扩展的,否则不是乱套了。

  centos7.4之saltstack的系列(三)之state相关_centos_15

5.6 state的逻辑关系 

  match   : 配模某个模块,比如 match: grain match: nodegroup

  require: 依赖某个state,在运行此state前,先运行依赖的state,依赖可以有多个,被谁依赖是require_in

  watch  : 在某个state变化时运行此模块,watch除具备require功能外,还增了关注状态的功能,被谁依赖是watch_in。

  order   : 优先级比require和watch低,有order指定的state比没有order指定的优先级高,假如一个state模块内安装多个服务,或者其他依赖关系,可以使用

 require和require_in示例:

   应用场景,首先我们是确定哪些操作都做了以后才能执行此操作,比如所有的安装操作都完成,最后要重启一些服务,就可以使用此参数

  如下,如果file这个模块下的id是apache-config这个操作成功的话,就执行id是apache-service操作,如果有enable就是重启,如果有reload的参数就是重载配置。

  require:

  centos7.4之saltstack的系列(三)之state相关_centos_16

  require_in:

  写法如下,可以理解为被谁依赖,如下,如果我这个文件操作成功的话就执行service这个模块的id是apache-service的操作。

  centos7.4之saltstack的系列(三)之state相关_apache_17

watch和watch_in示例:

   这个就NB了,它具备require的特性。应用场景,如果我们服务都安装完了,这个时候我需要给所有的minion修改一下配置文件,修改完成后重新加载配置文件,用watch就可以解决,用法和require一模一样。

  如果file这个模块下的id是apache-config这个操作成功(首先是从source这个路径传送到minino端的name路径下后,重新加载httpd这个服务)。watch_in用法刚好相反,类似于require_in0。

  centos7.4之saltstack的系列(三)之state相关_配置文件_18


六、jinja2的基本使用

  6.1、介绍

  Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全:

  参考地址:http://docs.jinkan.org/docs/jinja2/

  Jinja的使用分为三个步骤:

  1. File状态使用template参数 - template: jinja。
  2. 模板文件里面变量使用{{名称}},比如{{PORT}}.
  3. File状态模块里面指定变量列表。

  6.2、jinji的基本使用

  环境介绍,为了区分,我在master配置文件中新加一个/srv/salt/jiaja2的模块。

  centos7.4之saltstack的系列(三)之state相关_配置文件_19

  基本写法

{% set iplist= grains['ipv4'] %}  #申明变量名称
/files/minion.conf: #定义传输文件id
file.managed: #执行方法
- name: /root/minion.conf #目的路径
- source: salt://files/minion.conf.jinja #文件存放位置
- template: jinja #申请只用jinja模板
- defaults: #定义默认将值传递给模板
IP_ADDR: {{iplist[1]}} #获取iplist这个变量的值,因为返回的是一个列表,所以我们需要取第2个值。

  cat files/minion.conf.jinja 这里为了明显,我只写了一个值,

  ipaddr:{{IP_ADDR}}

  minion效果

  centos7.4之saltstack的系列(三)之state相关_apache_20

  总结,这就很厉害了,比如我定义安装minion客户端的某个服务要根据写入自己的ip地址或者固定端口,就可以在这里定义多个变量defaults下面新加就行。它不但可以写变量还支持条件判断和循环。

  6.3、逻辑判断

  Jinja还主要用来给状态增加逻辑关系,如for循环,或者if判断,也是经常使用到的。

  salt,grains,pillar是salt中jinja里面的三个特殊字典,salt是包含所有salt函数对象的字典,grains是包含minion上grains的字典,pillar是包含minion上pillar的字典。上面已经介绍了如何使用了。

  if...elif...endif的示例:

  # cat /srv/pillar/pkgs.sls

apache:
pkg.installed: #这是根据系统的不同,判断安装的软件名称。如果有变量就放到{{}}中
{% if grains['os_family'] == 'RedHat' %} #jinja中判断,循环等标签是放在{% %}中
- name: httpd
{% elif grains['os_family'] == 'Debian' %}
- name: apache2
{% elif grains['os'] == 'Arch' %}
- name: apache
{% endif %} #也会有结束标签{% end** %}

  #当然还支持:{% if %}...{% elif %}...{% else %}...{% endif %}之类的写法。

  for循环的示例:

{% set userlist = ['yunwei','cacti','www'] %}
{% for user in userlist %}
{{ user }}:
user.present:
- shell: /bin/bash
- home: /home/{{ user }}
{% endfor %}

  参考地址:http://www.51niux.com/?id=117




举报

相关推荐

0 条评论