IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Ansible 条件判断 循环 handlers 任务失败 文件管理的使用 -> 正文阅读

[系统运维]Ansible 条件判断 循环 handlers 任务失败 文件管理的使用

Ansible 条件判断 循环 handlers 任务失败 文件管理的使用

1.条件判断

1.1测试多个条件

一个when语句可用于评估多个条件。使用andor关键字组合条件,并使用括号分组条件。

如果任一条件为真时满足条件语句,则应当使用or语句。例如,如果计算机上运行的是红帽企业linux或Fedora,则下述条件得到满足:

when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"

使用and运算时,两个条件都必须为真,才能满足整个条件语句。例如,如果远程主机是红帽企业Linux7.5主机,并且安装的内核是指定版本,则将满足以下条件:

when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"

when关键字还支持使用列表来描述条件列表。向when关键字提供列表时,将使用and运算组合所有条件。下面的示例演示了使用and运算符组合多个条件语句的另一方式:

when:
  - ansible_distribution_version == "7.5"
  - ansible_kernel == "3.10.0-327.el7.x86_64"

这种格式提高了可读性,而可读性是良好编写Ansible Playbook的关键目标。

通过使用括号分组条件,可以表达更复杂的条件语句。例如,如果计算机上运行的是红帽企业Linux7或Fedora28,则下述条件语句得到满足。此示例使用大于字符,这样长条件就可以在playbook中分成多行,以便于阅读。

when: >
  ( ansible_distribution == "Redhat" and
    ansible_distribution_major_version == "7" )
  or
  ( ansible_distribution == "Fedora" and
    ansible_distribution_major_version == "28" )

1.2循环和有条件任务

//挂载到跟目录 和 文件大小大于100000000 (没有就跳过)
---
- hosts: 192.168.200.145
  tasks:
    - name:
      yum:
        name: mariadb
        state: install
      loop: "{{ ansible_mounts }}"
      when: item.mount == "/" and item.size.available >
10000000 

[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [yum] *********************************************************************
changed: [192.168.200.145] => (item={'mount': '/', 'device': '/dev/mapper/rhel-root', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 18238930944, 'size_available': 16646459392, 'block_size': 4096, 'block_total': 4452864, 'block_available': 4064077, 'block_used': 388787, 'inode_total': 8910848, 'inode_available': 8876476, 'inode_used': 34372, 'uuid': 'eb80880a-bf27-4cc7-8728-f8bc48b5834c'})
skipping: [192.168.200.145] => (item={'mount': '/boot', 'device': '/dev/nvme0n1p1', 'fstype': 'xfs', 'options': 'rw,seclabel,relatime,attr2,inode64,noquota', 'size_total': 1063256064, 'size_available': 876052480, 'block_size': 4096, 'block_total': 259584, 'block_available': 213880, 'block_used': 45704, 'inode_total': 524288, 'inode_available': 523987, 'inode_used': 301, 'uuid': '9d8f2355-8cc4-4c77-8fc8-592604911512'}) 

PLAY RECAP *********************************************************************
192.168.200.145            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//当vsftpd运行时,重启httpd
---
- hosts: 192.168.200.145
  tasks:
    - name:
      shell: systemctl status vsftpd
      ignore_errors: yes
      register: result
      
    - name:
      service:
        name: httpd
        state: restarted
      when: result.rc == 0

[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [shell] *******************************************************************
changed: [192.168.200.145]

TASK [service] *****************************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

2. handlers

Ansible模块设计为具有幂等性。这表示,在正确编写的playbook中,playbook及其任务可以运行多次而不会改变受管主机,除非需要进行更改使受管主机进入所需的状态。

但在时候,在任务确实更改系统时,可能需要运行进一步的任务。例如,更改服务配置文件时可能要求重新加载该服务以便使其更改的配置生效。

处理程序是响应由其他任务触发的通知的任务。仅当任务在受管主机上更改了某些内容时,任务才通知其处理程序。每个处理程序具有全局唯一的名称,在playbook中任务块的末尾触发。如果没有任务通过名称通知处理程序,处理程序就不会运行。如果一个或多个任务通知处理程序,处理程序就会在play中的所有其他任务完成后运行一次。因为处理程序就是任务,所以可以在处理程序中使用他们将用于任何其他任务的模块。通常而言,处理程序被用于重新引导主机和重启服务。

//将httpd的端口从80改成8080,并且在修改配置以后重启httpd
---
- hosts: 192.168.200.145
  tasks:
    - name:
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "Listen "
        line: "Listen 8080"

    - name:
      service:
        name: httpd
        state: restarted

//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [lineinfile] **************************************************************
changed: [192.168.200.145]

TASK [service] *****************************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//查看配置文件
#Listen 12.34.56.78:80
Listen 8080

//在执行一遍playbook 由于配置文件中的端口号已经是8088,所以,任务的状态为OK
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [lineinfile] **************************************************************
ok: [192.168.200.145]

TASK [service] *****************************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//可以看出,任务"restart httpd"也正常的执行了,而且是"真正的"执行了,换句话说就是它的确重启了对应的httpd服务,对远程主机进行了实际的操作
//handlers就是来解决这种问题的
---
- hosts: 192.168.200.145
  tasks:
    - name:
      lineinfile:
        path: /etc/httpd/conf/httpd.conf
        regexp: "^Listen"
        line: "Listen 8080"
        backrefs: yes
      notify:
        restart httpd

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

//运行可以发现没有发生任何变化 因为lineinfile没有改变所以不会重启httpd
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [lineinfile] **************************************************************
ok: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//handlers是另一种任务列表,所以handlers中可以有多个任务,被tasks中不同的任务notify
---
- hosts: 192.168.200.145
  tasks:
    - name:
      file: 
        path: /opt/abc
        state: directory
      notify: h1

    - name:
      file:
        path: /opt/123
        state: directory
      notify: h2

  handlers:
    - name: h1
      file: 
        path: /opt/qwe
        state: touch
    - name: h2
      file:
        path: /opt/456
        state: touch

//运行 handler执行的顺序与handler在playbook中定义的顺序是相同的,与"handler被notify"的顺序无关
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [file] ********************************************************************
changed: [192.168.200.145]

TASK [file] ********************************************************************
changed: [192.168.200.145]

RUNNING HANDLER [h1] ***********************************************************
changed: [192.168.200.145]

RUNNING HANDLER [h2] ***********************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

3. 任务失败

3.1忽略任务失败

Ansible 默认会检查命令和模块的返回状态,并进行相应的错误处理,默认是遇到错误就中断 playbook 的执行,这些默认行为都是可以改变的,可以通过 ignore_errors 忽略返回状态码

---
- hosts: 192.168.200.145
  tasks:
    - name:
      yum:  
        name: zzz //随便取得名字
        state: present
      ignore_errors: yes //忽略错误
      
//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [yum] *********************************************************************
fatal: [192.168.200.145]: FAILED! => {"changed": false, "failures": ["No package zzz available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring

PLAY RECAP *********************************************************************
192.168.200.145            : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=1   


3.2 任务失败后强制执行处理程序

---
- hosts: 192.168.200.145
  force_handlers: yes
  tasks:
    - name:
      command: echo "wsnd"
      notify: restart
      
    - name:
      yum:
        name: zzzz
        state: present
        
  handlers:
    - name: restart
      service:
        name: httpd
        state: restarted

//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [command] *****************************************************************
changed: [192.168.200.145]

TASK [yum] *********************************************************************
fatal: [192.168.200.145]: FAILED! => {"changed": false, "failures": ["No package zzzz available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []} //此处显示错误执行后仍然响应处理任务

RUNNING HANDLER [restart] ******************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=2    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

3.3 指定任务失败条件

//创建一个脚本
#! /bin/bash
 
ls 789
ls

---
- hosts: 192.168.200.145
  tasks:
    - name:
      script: 1.sh //指定任务失败条件
      register: result
      failed_when: "'没有那个文件或目录' in result.stdout"
    - debug:
        var: result

//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [script] ******************************************************************
fatal: [192.168.200.145]: FAILED! => {"changed": true, "failed_when_result": true, "rc": 0, "stderr": "Shared connection to 192.168.200.145 closed.\r\n", "stderr_lines": ["Shared connection to 192.168.200.145 closed."], "stdout": "ls: 无法访问'789': 没有那个文件或目录\r\nanaconda-ks.cfg\r\n", "stdout_lines": ["ls: 无法访问'789': 没有那个文件或目录", "anaconda-ks.cfg"]}

PLAY RECAP *********************************************************************
192.168.200.145            : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

//也可以使用fail模块来完成
---
- hosts: 192.168.200.145
  tasks:
    - name:
      script: 1.sh
      register: result

    - name:
      fail: 
        msg: "GG"
      when: "'没有那个文件或目录' in result.stdout"
      
//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [script] ******************************************************************
changed: [192.168.200.145]

TASK [fail] ********************************************************************
fatal: [192.168.200.145]: FAILED! => {"changed": false, "msg": "GG"} //错误变成了GG

PLAY RECAP *********************************************************************
192.168.200.145            : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

3.4 指定何时任务报告changed

---
- hosts: 192.168.200.145
  tasks:
    - name:
      script: 1.sh
      register: result
      changed_when: "'789' in result.stdout"
      notify:
        - restart
          
  handlers:
    - name: restart
      service:
        name: httpd
        state: restarted

//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [script] ******************************************************************
changed: [192.168.200.145]

RUNNING HANDLER [restart] ******************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

3.5 Ansible块和错误处理

在playbook中,块是对任务进行逻辑分组的子句,可用于控制任务的执行方式。也可结合rescue和always语句来处理错误。如果块中的任何任务失败,则执行其rescue块中的任务来进行恢复。在block子句中的任务以及rescue子句中的任务(如果出现故障)运行之后,always子句中的任务运行。

block:定义要运行的主要任务
rescue:定义要在block子句中定义的任务失败时运行的任务
always:定义始终都独立运行的任务,不论block和rescue子句中定义的任务是成功还是失败
任务执行流程如图:
在这里插入图片描述

---
- hosts: 192.168.200.145
  tasks:
    - name:
      block:
        - name: block
          shell: echo 'block'
          
          
      rescue:
        - name: rescue
          shell: echo 'rescue'
          
      always:
        - name: always
          shell: echo 'always'

//运行  当block没有报错时 不运行rescue
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [block] *******************************************************************
changed: [192.168.200.145]

TASK [always] ******************************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//故意写错block时 rescue就会运行
---
- hosts: 192.168.200.145
  tasks:
    - name:
      block:
        - name: block
          yum:
            name: zzzz
            state: present

      rescue:
        - name: rescue
          shell: echo 'rescue'

      always:
        - name: always
          shell: echo 'always'

4.文件管理

常用文件模块

模块名称模块说明
blockinfile插入、更新或删除由可自定义标记线包围的多行文本块
copy将文件从本地或远程计算机复制到受管主机上的某个位置。 类似于file模块,copy模块还可以设置文件属性,包括SELinux上下文件。
fetch此模块的作用和copy模块类似,但以相反方式工作。此模块用于从远程计算机获取文件到控制节点, 并将它们存储在按主机名组织的文件树中。
file设置权限、所有权、SELinux上下文以及常规文件、符号链接、硬链接和目录的时间戳等属性。 此模块还可以创建或删除常规文件、符号链接、硬链接和目录。其他多个与文件相关的 模块支持与file模块相同的属性设置选项,包括copy模块。
lineinfile确保特定行位于某文件中,或使用反向引用正则表达式来替换现有行。 此模块主要在用户想要更改文件的某一行时使用。
stat检索文件的状态信息,类似于Linux中的stat命令。
synchronize围绕rsync命令的一个打包程序,可加快和简化常见任务。 synchronize模块无法提供对rsync命令的完整功能的访问权限,但确实最常见的调用更容易实施。 用户可能仍需通过run command模块直接调用rsync命令。

4.1 file模块

在受管主机上创建、复制、编辑和删除文件是用户可以使用Files模块库中的模块实施的常见任务。

---
- hosts: 192.168.200.145
  tasks:
    - name:
      file: 
        path: /opt/zzz 
        owner: zzz
        group: zzz
        mode: 0644
        state: touch
        setype: samba_share_t

//运行
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [file] ********************************************************************
changed: [192.168.200.145]

PLAY RECAP *********************************************************************
192.168.200.145            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

//查看
 [root@localhost opt]# ls -Z zzz 
unconfined_u:object_r:samba_share_t:s0 zzz

4.2 copy

copy模块用于将位于控制节点的文件复制到选定的受管主机。

---
- hosts: 192.168.200.145
  tasks:
    - name:
      copy: 
        src: /opt/inventory
        dest: /opt/

//查看
[root@localhost opt]# ls
123  1.sh  456  abc  inventory  qwe  zzz

4.3 fetch

要从受管主机检索文件

---
- hosts: 192.168.200.145
  tasks:
    - name:
      fetch:
        src: /opt/zzz
        dest: /opt/

//查看
[root@localhost opt]# ls
192.168.200.145 
[root@localhost 192.168.200.145]# cd /opt/192.168.200.145/opt/
[root@localhost opt]# ls
zzz

4.4 lineinfile

确保现有文件中存在特定的单行文本

---
- hosts: 192.168.200.145
  tasks:
    - name:
      lineinfile:
        path: /opt/qwe
        line: 'Theshy bei ding zhu l'
        state: present

//查看
[root@localhost opt]# cat qwe
Theshy bei ding zhu l

4.5 blockfile

使用blockinfile模块时,注释块标记插入到块的开头和结尾,以确保幂等性。

---
- hosts: 192.168.200.145
  tasks:
    - name:
      blockinfile:
        path: /opt/456
        block: |
          Theshy bei ding zhu l
          na zhu ji you dian nan
        state: present

//查看
[root@localhost opt]# cat 456 
# BEGIN ANSIBLE MANAGED BLOCK
Theshy bei ding zhu l
na zhu ji you dian nan
# END ANSIBLE MANAGED BLOCK

4.6 删除文件

//使用file模块
---
- hosts: 192.168.200.145
  tasks:
    - name:
      file:
        path: /opt/qwe
        state: absent

4.7 stat

stat模块检索文件的事实,类似于Linux中的stat命令参数提供检索文件属性、确定文件检验和等功能

---
- hosts: 192.168.200.145
  tasks:
    - name:
      stat:
        path: /etc/passwd
      register: results
      
    - name:
      debug: 
        var: results

//查看
[root@localhost opt]# ansible-playbook fact.yml 

PLAY [192.168.200.145] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.200.145]

TASK [stat] ********************************************************************
ok: [192.168.200.145]

TASK [debug] *******************************************************************
ok: [192.168.200.145] => {
    "results": {
        "changed": false,
        "failed": false,
        "stat": {
            "atime": 1627304976.7480087,
            "attr_flags": "",
            "attributes": [],
            "block_size": 4096,
            "blocks": 8,
            "charset": "us-ascii",
            "checksum": "bedb5d9dcebd6966cd98948e8f1b887038369b3c",
            "ctime": 1627192777.9049947,
            "dev": 64768,
            "device_type": 0,
            "executable": false,
            "exists": true,
            "gid": 0,
            "gr_name": "root",
            "inode": 17281130,
            "isblk": false,
            "ischr": false,
            "isdir": false,
            "isfifo": false,
            "isgid": false,
            "islnk": false,
            "isreg": true,
            "issock": false,
            "isuid": false,
            "mimetype": "text/plain",
            "mode": "0644",
            "mtime": 1627192777.9039946,
            "nlink": 1,
            "path": "/etc/passwd",
            "pw_name": "root",
            "readable": true,
            "rgrp": true,
            "roth": true,
            "rusr": true,
            "size": 1411,
            "uid": 0,
            "version": "2593268142",
            "wgrp": false,
            "woth": false,
            "writeable": true,
            "wusr": true,
            "xgrp": false,
            "xoth": false,
            "xusr": false
        }
    }
}

PLAY RECAP *********************************************************************
192.168.200.145            : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

4.8 synchronize

synchronize模块是一个围绕rsync工具的打包程序,它简化了playbook中的常见文件管理任务

---
- hosts: 192.168.200.145
  tasks:
    - name:
      yum:  
        name: rsync
        state: present
        
    - name:
      synchronize:
          src: playbook
          dest: /opt/playbook

//查看
[root@localhost opt]# ls
123  1.sh  456  abc  inventory  playbook  zzz
[root@localhost opt]# cd playbook/
[root@localhost playbook]# ls
playbook
[root@localhost playbook]# cd /opt/playbook/playbook/
[root@localhost playbook]# ls
auto.yml  test.yml  user.yml

192.168.200.145 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0


### 4.8 synchronize

**synchronize**模块是一个围绕**rsync**工具的打包程序,它简化了**playbook**中的常见文件管理任务

```bash
---
- hosts: 192.168.200.145
  tasks:
    - name:
      yum:  
        name: rsync
        state: present
        
    - name:
      synchronize:
          src: playbook
          dest: /opt/playbook

//查看
[root@localhost opt]# ls
123  1.sh  456  abc  inventory  playbook  zzz
[root@localhost opt]# cd playbook/
[root@localhost playbook]# ls
playbook
[root@localhost playbook]# cd /opt/playbook/playbook/
[root@localhost playbook]# ls
auto.yml  test.yml  user.yml
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:38:53  更:2021-07-27 16:40:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 16:19:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码