自动化运维工具(二)——ansible模块&palybook的使用

发布于:2023-01-09 ⋅ 阅读:(756) ⋅ 点赞:(0)

目录

一:ansible核心模块

1、主机连通性测试

2、command模块

3、shell模块

4、copy模块

5、file模块

6、fetch模块

7、cron模块

8、yum模块

9、service模块

10、script

二:playbook

1、playbook的核心元素

2、playbook运行方式

3、使用

4、template模块


一:ansible核心模块

1、主机连通性测试

我们使用ansible xxx -m ping命令来进行主机连通性测试(xxx是主机的组名),效果如下:

[root@kafka-1 ansible]# vim /etc/ansible/hosts

# Ex 2: A collection of hosts belonging to the 'webservers' group

[webservers]           #webservers为xxx
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110
192.168.149.152
192.168.149.153

[root@kafka-1 ansible]# ansible webservers -m ping
192.168.149.152 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.149.153 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

2、command模块

command模块可以直接在远程主机上执行命令,并将结果返回主机。如下:

[root@kafka-1 ansible]# ansible webservers -m command -a "ip a"
192.168.149.152 | CHANGED | 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:75:96:ae brd ff:ff:ff:ff:ff:ff
    inet 192.168.149.152/24 brd 192.168.149.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::7f6a:2f24:7ab1:fd87/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
192.168.149.153 | CHANGED | 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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:bf:e4:78 brd ff:ff:ff:ff:ff:ff
    inet 192.168.149.153/24 brd 192.168.149.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::a92b:91d7:cb23:cb92/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

在远程主机上执行命令,属于裸执行,非键值对显示;不进行shell解析、 不会解析它的管道符号  会认为ifconfig|grep 是一个命令。该模块还有如下几个命令:

chdir       # 在执行命令之前,先切换到该目录
executable # 切换shell来执行命令,需要使用命令的绝对路径
free_form   # 要执行的Linux指令,一般使用Ansible的-a参数代替。
creates  # 一个文件名,当这个文件存在,则该命令不执行,可以
用来做判断
removes # 一个文件名,这个文件不存在,则该命令不执行

[root@kafka-1 ansible]# ansible webservers -m command -a "chdir=/opt/ ls"
192.168.149.153 | CHANGED | rc=0 >>
apache-zookeeper-3.6.3-bin
apache-zookeeper-3.6.3-bin.tar.gz
dist
dist1
kafka_2.12-2.8.1
kafka_2.12-2.8.1.tgz
192.168.149.152 | CHANGED | rc=0 >>
apache-zookeeper-3.6.3-bin
apache-zookeeper-3.6.3-bin.tar.gz
celery_task
dist
dist1
kafka_2.12-2.8.1
kafka_2.12-2.8.1.tgz

3、shell模块

shell模块可以在远程主机上调用shell解释器运行命令,支持shell的各种功能,例如管道等。

[root@kafka-1 ansible]# ansible webservers -m shell -a "cat /etc/passwd|grep bash"
192.168.149.152 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash
192.168.149.153 | CHANGED | rc=0 >>
root:x:0:0:root:/root:/bin/bash

只要是我们的shell命令,都可以通过这个模块在远程主机上运行。

4、copy模块

这个模块用于将文件复制到远程主机,同时支持给定内容生成文件和修改权限。其相关选项如下:

src    #被复制到远程主机的本地文件。可以是绝对路径,也可以是相对路径。如果路径是一个目录,则会递归复制,用法类似于"rsync"
content   #用于替换"src",可以直接指定文件的值
dest    #必选项,将源文件复制到的远程主机的绝对路径
backup   #当文件内容发生改变后,在覆盖之前把源文件备份,备份文件包含时间信息
directory_mode    #递归设定目录的权限,默认为系统默认权限
force    #当目标主机包含该文件,但内容不同时,设为"yes",表示强制覆盖;设为"no",表示目标主机的目标位置不存在该文件才复制。默认为"yes"
others    #所有的 file 模块中的选项可以在这里使用

注意:#####src目录后面带/和不带/的区别
      #带/ 表示拷贝目录下的子文件或者子文件夹
      #不带/  表示拷贝整个目录

带/---同时如果目的主机没有文件夹,会被新建
[root@kafka-1 lianxi]# ansible webservers -m copy -a "src=/lianxi/ dest=/lianxi1"
192.168.149.152 | CHANGED => {
    "changed": true, 
    "dest": "/lianxi1/", 
    "src": "/lianxi/"
}
192.168.149.153 | CHANGED => {
    "changed": true, 
    "dest": "/lianxi1/", 
    "src": "/lianxi/"
}
[root@kafka-3 lianxi1]# pwd
/lianxi1
[root@kafka-3 lianxi1]# ls
2022-08-20-log.tar  ansible-copy-link-s  a.txt  nignx.sh  test.sh

不带/
[root@kafka-1 lianxi]# ansible webservers -m copy -a "src=/lianxi dest=/lianxi1"
192.168.149.153 | CHANGED => {
    "changed": true, 
    "dest": "/lianxi1/", 
    "src": "/lianxi"
}
192.168.149.152 | CHANGED => {
    "changed": true, 
    "dest": "/lianxi1/", 
    "src": "/lianxi"
}
[root@kafka-3 lianxi1]# ls
2022-08-20-log.tar  ansible-copy-link-s  a.txt  lianxi  nignx.sh  test.sh

5、file模块

该模块主要用于设置文件的属性,比如创建文件、创建链接文件、删除文件等。下面是一些常见的命令:

force  #需要在两种情况下强制创建软链接,一种是源文件不存在,但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no
group  #定义文件/目录的属组。后面可以加上mode:定义文件/目录的权限
owner  #定义文件/目录的属主。后面必须跟上path:定义文件/目录的路径
recurse  #递归设置文件的属性,只对目录有效,后面跟上src:被链接的源文件路径,只应用于state=link的情况
dest  #被链接到的路径,只应用于state=link的情况
path    #目标路径
src     #源文件路径
state  #状态,有以下选项:

directory:如果目录不存在,就创建目录
file:即使文件不存在,也不会被创建
link:创建软链接
hard:创建硬链接
touch:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间
absent:删除目录、文件或者取消链接文件

创建链接文件
[root@kafka-1 lianxi]# ansible webservers -m file -a "path=/lianxi1/test.sh-link src=/lianxi1/test.sh state=link"
192.168.149.153 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/lianxi1/test.sh-link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "size": 16, 
    "src": "/lianxi1/test.sh", 
    "state": "link", 
    "uid": 0
}
192.168.149.152 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/lianxi1/test.sh-link", 
    "gid": 0, 
    "group": "root", 
    "mode": "0777", 
    "owner": "root", 
    "size": 16, 
    "src": "/lianxi1/test.sh", 
    "state": "link", 
    "uid": 0
}
[root@kafka-3 lianxi1]# ls
2022-08-20-log.tar   a.txt   nignx.sh  test.sh-link
ansible-copy-link-s  lianxi  test.sh

6、fetch模块

该模块用于从远程某主机获取(复制)文件到本地。有两个选项:

dest:用来存放文件的目录
src:在远程拉取的文件,并且必须是一个file,不能是目录

复制远端文件;本地没有文件夹时会新建,并且文件重名,文件会以远端ip地址命令
[root@kafka-1 lianxi]# ansible webservers -m fetch -a "src=/lianxi1/test.sh dest=/lianxi1"
192.168.149.152 | CHANGED => {
    "changed": true, 
    "checksum": "e539312b99506f8532b313915f5b4a1a747f7c5c", 
    "dest": "/lianxi1/192.168.149.152/lianxi1/test.sh", 
    "md5sum": "0957874bbecbacc4aafd1905352a61e7", 
    "remote_checksum": "e539312b99506f8532b313915f5b4a1a747f7c5c", 
    "remote_md5sum": null
}
192.168.149.153 | CHANGED => {
    "changed": true, 
    "checksum": "e539312b99506f8532b313915f5b4a1a747f7c5c", 
    "dest": "/lianxi1/192.168.149.153/lianxi1/test.sh", 
    "md5sum": "0957874bbecbacc4aafd1905352a61e7", 
    "remote_checksum": "e539312b99506f8532b313915f5b4a1a747f7c5c", 
    "remote_md5sum": null
}
[root@kafka-1 lianxi]# ls /lianxi1
192.168.149.152  192.168.149.153

7、cron模块

该模块适用于管理cron计划任务的。其使用的语法跟我们的crontab文件中的语法一致,同时,可以指定以下选项:

day= #日应该运行的工作( 1-31, *, */2, )
hour= # 小时 ( 0-23, *, */2, )
minute= #分钟( 0-59, *, */2, )
month= # 月( 1-12, *, /2, )
weekday= # 周 ( 0-6 for Sunday-Saturday,, )
job= #指明运行的命令是什么
name= #定时任务描述
reboot # 任务在重启时运行,不建议使用,建议使用special_time
special_time #特殊的时间范围,参数:reboot(重启时),annually(每年),monthly(每月),weekly(每周),daily(每天),hourly(每小时)
state #指定状态,present表示添加定时任务,也是默认设置,absent表示删除定时任务
user # 以哪个用户的身份执行

开启任务:执行任务每一分钟输出data信息到/tmp/time.txt目录下
[root@kafka-1 lianxi]# ansible webservers -m cron -a "minute=*/1 job='date >>/tmp/time.txt' name=date_test  state=present"
192.168.149.152 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "date_test"
    ]
}
192.168.149.153 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": [
        "date_test"
    ]
}
[root@kafka-3 lianxi1]# cat /tmp/time.txt 
2022年 08月 21日 星期日 15:30:01 CST

查看有哪些任务:
[root@kafka-1 lianxi]# ansible webservers -m shell -a "crontab -l"
192.168.149.153 | CHANGED | rc=0 >>
#Ansible: date_test
*/1 * * * * date >>/tmp/time.txt
192.168.149.152 | CHANGED | rc=0 >>
#Ansible: date_test
*/1 * * * * date >>/tmp/time.txt

关闭任务:
[root@kafka-1 lianxi]# ansible webservers -m cron -a "name=date_test  state=absent"
192.168.149.152 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": []
}
192.168.149.153 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "envs": [], 
    "jobs": []
}
[root@kafka-1 lianxi]# ansible webservers -m shell -a "crontab -l"


8、yum模块

顾名思义,该模块主要用于软件的安装。其选项如下:

name=  #所安装的包的名称
state=  #present--->安装, latest--->安装最新的, absent---> 卸载软件。
update_cache  #强制更新yum的缓存
conf_file  #指定远程yum安装时所依赖的配置文件(安装本地已有的包)。
disable_pgp_check  #是否禁止GPG checking,只用于presentor latest。
disablerepo  #临时禁止使用yum库。 只用于安装或更新时。
enablerepo  #临时使用的yum库。只用于安装或更新时。

安装nginx服务,如果有,不会报错,提示我已经安装
[root@kafka-1 lianxi]# ansible webservers -m yum -a "name=nginx state=present"
192.168.149.153 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "1:nginx-1.20.1-9.el7.x86_64 providing nginx is already installed"
    ]
}
192.168.149.152 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "msg": "", 
    "rc": 0, 
    "results": [
        "1:nginx-1.20.1-9.el7.x86_64 providing nginx is already installed"
    ]
}

9、service模块

该模块用于服务程序的管理。其主要选项如下:

arguments #命令行提供额外的参数
enabled #设置开机启动。
name= #服务名称
runlevel #开机启动的级别,一般不用指定。
sleep #在重启服务的过程中,是否等待。如在服务关闭以后等待2秒再启动。(定义在剧本中。)
state #有四种状态,分别为:started--->启动服务, stopped--->停止服务, restarted--->重启服务, reloaded--->重载配置

关闭nginx服务
[root@kafka-1 lianxi]# ansible webservers -m service -a "name=nginx state=stopped"
192.168.149.152 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "name": "nginx", 
    "state": "stopped", 
    ...
}
[root@kafka-3 lianxi1]# ps -ef|grep nginx
root       4400   1507  0 15:43 pts/0    00:00:00 grep --color=auto nginx

开启nginx服务,并设置开启自启
[root@kafka-1 lianxi]# ansible webservers -m service -a "name=nginx state=started enabled=true"
192.168.149.152 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "enabled": true, 
    "name": "nginx", 
    "state": "started", 
    "status": {
    ...
}
[root@kafka-3 lianxi1]# ps -ef|grep nginx
root       4603      1  0 15:44 ?        00:00:00 nginx: master process /usr/sbin/ngin
nginx      4604   4603  0 15:44 ?        00:00:00 nginx: worker process
nginx      4605   4603  0 15:44 ?        00:00:00 nginx: worker process
root       4615   1507  0 15:45 pts/0    00:00:00 grep --color=auto nginx

10、script

该模块用于将本机的脚本在被管理端的机器上运行。该模块直接指定脚本的路径即可,我们通过例子来看一看到底如何使用的,首先,我们写一个脚本,并给其加上执行权限:

将字符串写入目的主机的/tmp/ansible.txt文件里
[root@kafka-1 lianxi]# cat test.sh 
#!/bin/bash
echo "test ansible" >> /tmp/ansible.txt
[root@kafka-1 lianxi]# chmod +x test.sh 
[root@kafka-1 lianxi]# ansible webservers -m script -a "/lianxi/test.sh"
192.168.149.152 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.149.152 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.149.152 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}
192.168.149.153 | CHANGED => {
    "changed": true, 
    "rc": 0, 
    "stderr": "Shared connection to 192.168.149.153 closed.\r\n", 
    "stderr_lines": [
        "Shared connection to 192.168.149.153 closed."
    ], 
    "stdout": "", 
    "stdout_lines": []
}
[root@kafka-3 lianxi1]# cat /tmp/ansible.txt 
test ansible

二:playbook

        如果ansible的各模块(能实现各种功能)是车间里的各工具;playbook就是指导手册,目标远程主机就是库存和原料对象。语法是 yaml 格式配置

1、playbook的核心元素

hosts : playbook配置文件作用的主机
tasks: 任务列表
variables: 变量 
templates:包含模板语法的文本文件
handlers :由特定条件触发的任务
roles :用于层次性、结构化地组织playbook。roles 能够根据层次型结构自动装载变量文件、tasks以及handlers等

2、playbook运行方式

ansible-playbook --check 只检测可能会发生的改变,但不真执行操作
ansible-playbook --list-hosts 列出运行任务的主机
ansible-playbook --syntax-check playbook.yaml 语法检测
ansible-playbook -t TAGS_NAME playbook.yaml 只执行TAGS_NAME任务
ansible-playbook playbook.yaml 运行

3、使用

创建yaml文件

[root@kafka-1 ansible]# pwd
/etc/ansible
[root@kafka-1 ansible]# ls
ansible.cfg                  ansible_playbook_sc.yaml  roles
ansible_playbook_nginx.yaml  hosts                     template_test.yaml
[root@kafka-1 ansible]# cat ansible_playbook_sc.yaml 
- hosts: webservers
  remote_user: root
  tasks:
  - name: upload file
    copy: src=/etc/passwd dest=/tmp/passwd_tmp1
  - name: ip get
    shell: ip a
    register: shell_result
  - debug:
       var: shell_result.stdout_lines

执行yaml文件

[root@kafka-1 ansible]# ansible-playbook ansible_playbook_sc.yaml 

PLAY [all] ****************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.149.152]
ok: [192.168.149.153]

TASK [upload file] ********************************************************************
ok: [192.168.149.152]
ok: [192.168.149.153]

TASK [ip get] *************************************************************************
changed: [192.168.149.153]
changed: [192.168.149.152]

TASK [debug] **************************************************************************
ok: [192.168.149.152] => {
    "shell_result.stdout_lines": [
        "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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000", 
        "    link/ether 00:0c:29:75:96:ae brd ff:ff:ff:ff:ff:ff", 
        "    inet 192.168.149.152/24 brd 192.168.149.255 scope global noprefixroute ens33", 
        "       valid_lft forever preferred_lft forever", 
        "    inet6 fe80::7f6a:2f24:7ab1:fd87/64 scope link noprefixroute ", 
        "       valid_lft forever preferred_lft forever"
    ]
}
ok: [192.168.149.153] => {
    "shell_result.stdout_lines": [
        "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: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000", 
        "    link/ether 00:0c:29:bf:e4:78 brd ff:ff:ff:ff:ff:ff", 
        "    inet 192.168.149.153/24 brd 192.168.149.255 scope global noprefixroute ens33", 
        "       valid_lft forever preferred_lft forever", 
        "    inet6 fe80::a92b:91d7:cb23:cb92/64 scope link noprefixroute ", 
        "       valid_lft forever preferred_lft forever"
    ]
}

PLAY RECAP ****************************************************************************
192.168.149.152            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.149.153            : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

查看任务结果
[root@kafka-3 lianxi1]# cat /tmp/passwd_tmp1
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
...

使用palybook批量部署web服务

[root@kafka-1 ansible]# cat ansible_playbook_nginx.yaml 
- hosts: webservers
  remote_user: root
  tasks:
  - name: load nginx
    yum: name=nginx state=installed
  - name: move conf
    copy: src=/opt/sc.conf dest=/etc/nginx/conf.d
  - name: move html
    copy: src=/opt/index.html dest=/opt/dist1/
  - name: start
    service: name=nginx state=started

4、template模块

根据一定的条件灵活的设置要复制文件中的部分关键内容,可以使用template模块,模板文件必须以j2结尾。

编写sc_template.conf.j2
[root@kafka-1 ansible]# cat sc_template.conf.j2 
server {
        listen       {{ listen_port }} ;
        server_name  www.sc.com;
        root         /opt/dist;
	access_log  /var/log/nginx/sc_access.log  main;
        location / {
	}
	location =/api {
	}
}

编写测试剧本
[root@kafka-1 ansible]# cat ansible_playbook_sc.yaml 
- hosts: all
  remote_user: root
  tasks:
  - name: upload file
    copy: src=/etc/passwd dest=/tmp/passwd_tmp1
  - name: ip get
    shell: ip a
    register: shell_result
  - debug:
       var: shell_result.stdout_lines

执行playbook
[root@kafka-1 ansible]# ansible-playbook ansible_playbook_sc.yaml 
...


也可以直接在/etc/ansible/hosts里修改
[root@kafka-1 ansible]# vim hosts
# Ex 2: A collection of hosts belonging to the 'webservers' group

[webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110
192.168.149.152 listen_port=666
192.168.149.153 listen_port=777

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到