Ansible模块

在上一篇博客《Ansible基础认识及安装使用详解(一)–技术流ken》中以及简单的介绍了一下ansible的模块。ansible是基于模块工作的,所以我们必须掌握几个常用的模块以便能够从容应对日常的工作。

相信大家在看完上一篇博客之后应该也已经知道可以使用ansible-doc -s 模块名,可以获取到模块的使用帮助,在本篇博客中就不再赘述。

Ansible常用模块介绍

ansible常用模块主要有如下12个:

ping 模块:            尝试连接主机,如果测试成功会返回‘pong’
command模块: 在远程节点执行命令
yum模块: 使用yum软件包管理工具管理软件包
shell模块: 和command模块类似,执行命令,支持变量等符号
cron模块 : 管理定时任务
service模块: 管理程序服务
file模块: 设置文件属性
copy模块: 复制本地文件到远程主机
script模块: 传送本地的一个脚本并在远程主机上执行
setup模块: 获取远程主机的参数信息
user模块: 管理用户账户
group模块: 添加或者删除用户组

Ansible常用模块使用详解

下面就针对每个模块的使用进行一一演示

(一)command模块

command的模块是在远程主机执行命令。默认使用此模块,所以可以省略

例:获取远程主机的ip信息

[root@ken ~]# ansible all -m command -a "ip a"
10.220.5.138 | SUCCESS | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
link/ether 00:0c:29:a9:90:16 brd ff:ff:ff:ff:ff:ff
inet 10.220.5.138/24 brd 10.220.5.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fea9:9016/64 scope link
valid_lft forever preferred_lft forever 10.220.5.139 | SUCCESS | rc=0 >>
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
link/ether 00:0c:29:65:31:ad brd ff:ff:ff:ff:ff:ff
inet 10.220.5.139/24 brd 10.220.5.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe65:31ad/64 scope link
valid_lft forever preferred_lft forever

(二)cron模块

cron模块是管理定时任务

例:在远程节点每隔5分钟往、/tmp/ken.txt输入111

[root@ken ~]# ansible all -m cron -a "minute=*/5 job='echo 111>/tmp/ken.txt'  state=present"
10.220.5.139 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"None",
"None"
]
}
10.220.5.138 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"None",
"None"
]
}

可以查看是否已经设置成功。可以看到已经安装成功

[root@ken ~]# ansible all  -a "crontab -l"
10.220.5.138 | SUCCESS | rc=0 >>
#Ansible: None
*/5 * * * * echo 111>/tmp/ken.txt 10.220.5.139 | SUCCESS | rc=0 >>
#Ansible: None
*/5 * * * * echo 111>/tmp/ken.txt

移除计划任务

[root@ken ~]# ansible all  -a "crontab -r"
10.220.5.138 | SUCCESS | rc=0 >> 10.220.5.139 | SUCCESS | rc=0 >> [root@ken ~]# ansible all -a "crontab -l"
10.220.5.139 | FAILED | rc=1 >>
no crontab for rootnon-zero return code 10.220.5.138 | FAILED | rc=1 >>
no crontab for rootnon-zero return code

(三)copy模块

copy模块是复制本机文件到远程节点之上

例:复制本机/tmp/ken.sh 到远程节点上的/tmp下

[root@ken ~]# ansible all -m copy -a "src=/tmp/ken.sh dest=/tmp"
10.220.5.138 | SUCCESS => {
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/tmp/ken.sh",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/root/.ansible/tmp/ansible-tmp-1542373625.27-167828199145082/source",
"state": "file",
"uid": 0
}
10.220.5.139 | SUCCESS => {
"changed": true,
"checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"dest": "/tmp/ken.sh",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/root/.ansible/tmp/ansible-tmp-1542373625.3-279713897725048/source",
"state": "file",
"uid": 0
}

查看是否已经真的传送过去了。发现在远程主机的/tmp目录下面已经有个刚刚我们传送过去的文件了

[root@ken ~]# ansible all -m shell -a "ls /tmp | grep ken.sh"
10.220.5.138 | SUCCESS | rc=0 >>
ken.sh 10.220.5.139 | SUCCESS | rc=0 >>
ken.sh

(四)yum模块

yum模块是用来管理远程安装包的

例:在远程节点下载httpd服务

[root@ken ~]# ansible all -m yum -a "name=httpd state=present"
10.220.5.138 | SUCCESS => {
"changed": true,
"msg": "file:///mnt/repodata/repomd.xml: [Errno 14] curl#37 - \"Couldn't open file /mnt/repodata/repomd.xml\"\nTrying other mirror.\n",
"rc": 0,
"results": [
"Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-80.el7.centos.1 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n httpd x86_64 2.4.6-80.el7.centos.1 updates 2.7 M\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 2.7 M\nInstalled size: 9.4 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : httpd-2.4.6-80.el7.centos.1.x86_64 1/1 \n Verifying : httpd-2.4.6-80.el7.centos.1.x86_64 1/1 \n\nInstalled:\n httpd.x86_64 0:2.4.6-80.el7.centos.1 \n\nComplete!\n"
]
}
10.220.5.139 | SUCCESS => {
"changed": true,
"msg": "",
"rc": 0,
"results": [
"Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--> Running transaction check\n---> Package httpd.x86_64 0:2.4.6-80.el7.centos.1 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n httpd x86_64 2.4.6-80.el7.centos.1 updates 2.7 M\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 2.7 M\nInstalled size: 9.4 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : httpd-2.4.6-80.el7.centos.1.x86_64 1/1 \n Verifying : httpd-2.4.6-80.el7.centos.1.x86_64 1/1 \n\nInstalled:\n httpd.x86_64 0:2.4.6-80.el7.centos.1 \n\nComplete!\n"
]
}

查看是否已经安装成功

[root@ken ~]# ansible all  -m yum -a "list=httpd"
10.220.5.138 | SUCCESS => {
"changed": false,
"results": [
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.1.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos.1",
"repo": "installed",
"version": "2.4.6",
"yumstate": "installed"
},
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.1.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos.1",
"repo": "updates",
"version": "2.4.6",
"yumstate": "available"
},
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos",
"repo": "centos7",
"version": "2.4.6",
"yumstate": "available"
},
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos",
"repo": "ken",
"version": "2.4.6",
"yumstate": "available"
}
]
}
10.220.5.139 | SUCCESS => {
"changed": false,
"results": [
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.1.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos.1",
"repo": "installed",
"version": "2.4.6",
"yumstate": "installed"
},
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.1.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos.1",
"repo": "updates",
"version": "2.4.6",
"yumstate": "available"
},
{
"arch": "x86_64",
"envra": "0:httpd-2.4.6-80.el7.centos.x86_64",
"epoch": "0",
"name": "httpd",
"release": "80.el7.centos",
"repo": "centos7",
"version": "2.4.6",
"yumstate": "available"
}
]
}

(五)service模块

service模块是用来管理服务程序的

例:启动远程节点的httpd服务

[root@ken ~]# ansible all  -m service -a "name=httpd state=restarted"
10.220.5.138 | SUCCESS => {
"changed": true,
"name": "httpd",
"state": "started",
"status": {
"ActiveEnterTimestampMonotonic": "0",
"ActiveExitTimestampMonotonic": "0",
"ActiveState": "inactive",
"After": "nss-lookup.target systemd-journald.socket network.target tmp.mount system.slice remote-fs.target -.mount basic.target",
"AllowIsolate": "no",
"AmbientCapabilities": "0",
"AssertResult": "no",
"AssertTimestampMonotonic": "0",
"Before": "shutdown.target",
"BlockIOAccounting": "no",
....

输出信息很长,我就省略了。现在查看是否已经启动成功

[root@ken ~]# ansible all  -m shell -a "ss -tnl | grep 80"
10.220.5.139 | SUCCESS | rc=0 >>
LISTEN 0 128 :::80 :::* 10.220.5.138 | SUCCESS | rc=0 >>
LISTEN 0 128 :::80 :::*

(六)file模块

file模块是用来设置文件属性的

例:在远程节点的/tmp下创建一个test.txt文件

[root@ken ~]# ansible all  -m file  -a "state=touch path=/tmp/test.txt"
10.220.5.139 | SUCCESS => {
"changed": true,
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}
10.220.5.138 | SUCCESS => {
"changed": true,
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"mode": "0644",
"owner": "root",
"size": 0,
"state": "file",
"uid": 0
}

查看文件是否已经创建成功

[root@ken ~]# ansible all  -m shell -a "ls /tmp | grep test.txt"
10.220.5.138 | SUCCESS | rc=0 >>
test.txt 10.220.5.139 | SUCCESS | rc=0 >>
test.txt

(七)shell模块

shell模块和command模块类似即远程执行命令

但是比command更强大

例如:统计远程节点/tmp目录下有多少文件

我们首先使用command看下效果

[root@ken ~]# ansible all  -a "ls /tmp | wc -l"
10.220.5.139 | FAILED | rc=2 >>
/tmp:
total 4
drwx------ 2 root root 65 Nov 17 05:25 ansible_aIMVHi
-rw-r--r-- 1 root root 0 Nov 17 05:07 ken.sh
-rw-r--r-- 1 root root 0 Nov 17 05:00 ken.txt
drwx------ 3 root root 17 Nov 7 16:04 systemd-private-2e376cd91398450f85a81bc060207ef8-chronyd.service-TxdhUO
drwx------ 3 root root 17 Nov 7 16:05 systemd-private-2e376cd91398450f85a81bc060207ef8-httpd.service-k8IZOZ
drwx------ 3 root root 17 Nov 15 15:58 systemd-private-5c9f32d6cff64520b10075e086d943ab-chronyd.service-iAH3c0
drwx------ 3 root root 17 Nov 15 15:58 systemd-private-5c9f32d6cff64520b10075e086d943ab-httpd.service-dsAqeg
drwx------ 3 root root 17 Nov 14 15:56 systemd-private-65ded84926e64a90b0a201a805f752ca-chronyd.service-eSj3iR
drwx------ 3 root root 17 Nov 16 16:00 systemd-private-6706ba5361284cd4a0c91f3c8b68c606-chronyd.service-sLgAei
drwx------ 3 root root 17 Nov 17 05:17 systemd-private-6706ba5361284cd4a0c91f3c8b68c606-httpd.service-u6vla7
-rw-r--r-- 1 root root 0 Nov 17 05:22 test.txt
drwx------ 2 root root 6 Nov 15 15:58 vmware-root
-rw------- 1 root root 467 Nov 15 16:02 yum_save_tx.2018-11-15.16-02.KHC9kd.yumtxls: cannot access |: No such file or directory
ls: cannot access wc: No such file or directorynon-zero return code 10.220.5.138 | FAILED | rc=2 >>
/tmp:
total 0
drwx------ 2 root root 65 Nov 16 21:25 ansible_v4MF1q
drwxr-xr-x 2 root root 19 Nov 7 09:35 hsperfdata_root
drwxr-xr-x 2 zabbix zabbix 19 Nov 7 08:48 hsperfdata_zabbix
...

可以看到命令执行失败

现在我们再使用shell执行相同的操作看下效果

可以发现这次获取到了我们所需要的信息

[root@ken ~]# ansible all  -m shell -a "ls /tmp | wc -l"
10.220.5.138 | SUCCESS | rc=0 >>
13 10.220.5.139 | SUCCESS | rc=0 >>
13

(八)ping模块

ping模块可以探测远程主机

不用加任何的参数信息

获取成功就会返回pong

[root@ken ~]# ansible all -m ping
10.220.5.139 | SUCCESS => {
"changed": false,
"ping": "pong"
}
10.220.5.138 | SUCCESS => {
"changed": false,
"ping": "pong"
}

接下来还有四个常用模块,因为我换了自己的电脑,所以IP地址不再是上面的那些。

(九)setup模块

setup模块用来获取节点的参数信息

获取到的信息很详细,大家如果感兴趣可以研究一下

[root@ken ~]# ansible all -m setup
192.168.43.176 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.11.5",
"192.168.43.176"
],
"ansible_all_ipv6_addresses": [
"fe80::20c:29ff:fea5:e9ae",
"2408:84f4:83:54f1:20c:29ff:fea5:e9a4",
"fe80::20c:29ff:fea5:e9a4"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "07/02/2015",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
"BOOT_IMAGE": "/vmlinuz-3.10.0-862.el7.x86_64",
"biosdevname": "0",
"crashkernel": "auto",
"net.ifnames": "0",
"quiet": true,
"rd.lvm.lv": "centos/swap",
"rhgb": true,
"ro": true,
"root": "/dev/mapper/centos-root"
},
"ansible_date_time": {
"date": "2018-11-16",
"day": "16",
"epoch": "1542378922",
"hour": "22",
"iso8601": "2018-11-16T14:35:22Z",
"iso8601_basic": "20181116T223522739565",
"iso8601_basic_short": "20181116T223522",
"iso8601_micro": "2018-11-16T14:35:22.739656Z",
"minute": "35",
"month": "11",
"second": "22",
"time": "22:35:22",
"tz": "CST",
"tz_offset": "+0800",
"weekday": "Friday",
"weekday_number": "5",
"weeknumber": "46",
"year": "2018"
},
...

(十)script模块

作用是将本地的一个脚本传送至远程主机上面并运行

无需加多余参数,只需要在-a后面加上本地脚本路径即可

[root@ken ~]# ansible all -m script -a /tmp/test.sh
192.168.43.175 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.43.175 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.43.175 closed."
],
"stdout": "server\r\n",
"stdout_lines": [
"server"
]
}
192.168.43.176 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.43.176 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.43.176 closed."
],
"stdout": "agent\r\n",
"stdout_lines": [
"agent"
]
}

(十一)user模块

user模块是请求的是useradd, userdel, usermod三个指令

如下的命令含义是创建一个ken用户,shell类型为/sbin/nologin,uid号为454,系统用户

[root@ken ~]# ansible all -m user -a "name=ken shell=/sbin/nologin uid=454 state=present"
192.168.43.176 | FAILED! => {
"changed": false,
"msg": "useradd: UID 454 is not unique\n",
"name": "ken",
"rc": 4
}
192.168.43.175 | CHANGED => {
"changed": true,
"comment": "",
"create_home": true,
"group": 100,
"home": "/home/ken",
"name": "ken",
"shell": "/sbin/nologin",
"state": "present",
"system": false,
"uid": 454
}

从上面的执行结果来看192.168.43.176执行失败了,根据提示可知uid454的用户可能已经存在,让我们来看一下是否真的存在

命令返回结果显示uid454为nginx用户

[root@ken ~]# ansible 192.168.43.176 -a "grep 454 /etc/passwd"
192.168.43.176 | CHANGED | rc=0 >>
nginx:x:454:454:Nginx web server:/var/lib/nginx:/sbin/nologin

再来看一下192.168.43.175执行成功的

[root@ken ~]# ansible 192.168.43.175 -a "tail -1 /etc/passwd"
192.168.43.175 | CHANGED | rc=0 >>
ken:x:454:100::/home/ken:/sbin/nologin

(十二)group模块

goup模块请求的是groupadd, groupdel, groupmod 三个指令

如下命令含义是创建一个名为test1的组,gid为1122,在远程主机可用

[root@ken ~]# ansible all -m group -a "name=test1 gid=1122 state=present"
192.168.43.176 | CHANGED => {
"changed": true,
"gid": 1122,
"name": "test1",
"state": "present",
"system": false
}
192.168.43.175 | CHANGED => {
"changed": true,
"gid": 1122,
"name": "test1",
"state": "present",
"system": false
}

Ansible常用模块介绍及使用(2)的更多相关文章

  1. Ansible常用模块介绍及使用(week5_day1_part2)--技术流ken

    Ansible模块 在上一篇博客<Ansible基础认识及安装使用详解(一)--技术流ken>中以及简单的介绍了一下ansible的模块.ansible是基于模块工作的,所以我们必须掌握几 ...

  2. Ansible常用模块介绍

    ansible < HOST-PATTERN > [ -f FORKS ] [ -m MOUDULE ] [ -a "ARGS" ] [ -o ] MOUDULE: p ...

  3. ansible常用模块用法

    ansible常用模块用法 2015-07-21 10:25 24458人阅读 评论(1) 收藏 举报  分类: Linux(44)   ansible 版权声明:本文为博主原创文章,未经博主允许不得 ...

  4. Ansible 常见模块介绍

    目录 Ansible 常见模块介绍 ping 模块 command 模块 cron 模块 user 模块 group 模块 copy 模块 file 模块 service 模块 shell 模块 sc ...

  5. Ansible常用模块命令

    Ansible常用模块命令 一.安装ansible yum install epel-release yum install ansible 二.配置文件配置 vi /etc/ansible/ansi ...

  6. ansible 常用模块的使用

    安装 yum -y install ansible 配置文件/etc/ansible/hosts 模块介绍与使用 ping模块 [root@node1 config]# ansible k8s -m ...

  7. python基础31[常用模块介绍]

    python基础31[常用模块介绍]   python除了关键字(keywords)和内置的类型和函数(builtins),更多的功能是通过libraries(即modules)来提供的. 常用的li ...

  8. Ansible 常用模块详解

    经过前面的介绍,我们已经熟悉了 Ansible 的一些常识性的东西和如何编译安装Ansible,从本章开始我们将全面介绍 Ansible 的各种生产常用模块,这些也是我们使用 Ansible 的过程中 ...

  9. ansible常用模块详解(三)

    1.模块介绍 明确一点:模块的执行就类似是linux命令的一条命令,就单单的是为了执行一条语句,不是批量的操作,批量操作需要用到playbook内类似shell编写脚本进行批量. 1.1 模块的使用方 ...

随机推荐

  1. 【算法•日更•第十期】树型动态规划&区间动态规划:加分二叉树题解

    废话不多说,直接上题: 1580:加分二叉树 时间限制: 1000 ms         内存限制: 524288 KB提交数: 121     通过数: 91 [题目描述] 原题来自:NOIP 20 ...

  2. JavaScript学习系列博客_8_JavaScript中的关系运算符、赋值运算符

    关系运算符 - 关系运算符用来比较两个值之间的关系的 >.<.<=.>= 先说这四个.如果关系成立则返回true,关系不成立则返回false. - 如果比较的两个值是非numb ...

  3. OpenStack 服务心跳机制和状态监控

    参考链接: OpenStack服务心跳机制和状态监控 https://blog.csdn.net/qqhappy8/article/details/79304221

  4. Centos7 KVM启用嵌套虚拟化

    [root@kvm-hypervisor ~]# cat /etc/modprobe.d/kvm-nested.conf options kvm-intel nested= options kvm-i ...

  5. ARM开发板挂载Ubuntu18.04主机的NFS共享文件夹

    环境 ubuntu主机环境:Window10 下装VMWare下装的 ubuntu18.04LTS x64 IP 192.168.10.119 Window10下配置192.168.10该网段 开发板 ...

  6. Python语言中的关键字(自己做的读书笔记)

    电脑配置:联想笔记本电脑 windows8系统 Python版本:2.7.8 本文章撰写时间:2015.1.1 作者:陈东陈 阅读说明: 1.本文都是先解释,后放图片: 2.文中斜体部分要么为需要输入 ...

  7. Trie详解

    Trie,又名字典树.单词查找树,可以较高效地实现统计.排序和保存大量的字符串. 顾名思义,Trie是一个树状的结构,按照树型结构来存储字符串,显然是一种以空间换时间的方法.整体上理解和实现都不会很难 ...

  8. seo如何发外链

    http://www.wocaoseo.com/thread-228-1-1.html 在做外链方面博主并没有什么太多的经验,做为一位seo,下面武汉seo把自己做外链的大条列出来,都是经过本身实践并 ...

  9. SEO诊断方案以及执行方案

    http://www.wocaoseo.com/thread-127-1-1.html 今天和大家一起讨论一下SEO诊断方案以及SEO执行方案要怎么写,主要从哪些方面进行呢,做SEO的朋友们一直在探讨 ...

  10. Appium框架

    1.通过TestNG单元测试框架管理/执行测试用例 2.TestNG参数化,App启动的四个配置存到Testng.xml中 3.通过page object (PO)设计模式,测试用例和页面元素分离,页 ...