自动化运维工具——ansible详解（二） 自动化运维工具——ansible详解（二） - 博客园 https://www.cnblogs.com/keerya/p/8004566.html Ansible playbook 简介 playbook 是 ansible 用于配置，部署，和管理被控节点的剧本。 通过 playbook 的详细描述，执行其中的一系列 tasks ，可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的一系列 to do 2025-05-23 10:08 自动化运维工具——ansible详解（二） Ansible playbook 简介 playbook 是 ansible 用于配置，部署，和管理被控节点的剧本。 通过 playbook 的详细描述，执行其中的一系列 tasks ，可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的一系列 to-do-list ，而被控节点必须要完成。 也可以这么理解，playbook 字面意思，即剧本，现实中由演员按照剧本表演，在Ansible中，这次由计算机进行表演，由计算机安装，部署应用，提供对外服务，以及组织计算机处理各种各样的事情。 Ansible playbook使用场景 执行一些简单的任务，使用ad-hoc命令可以方便的解决问题，但是有时一个设施过于复杂，需要大量的操作时候，执行的ad-hoc命令是不适合的，这时最好使用playbook。 就像执行shell命令与写shell脚本一样，也可以理解为批处理任务，不过playbook有自己的语法格式。 使用playbook你可以方便的重用这些代码，可以移植到不同的机器上面，像函数一样，最大化的利用代码。在你使用Ansible的过程中，你也会发现，你所处理的大部分操作都是编写playbook。可以把常见的应用都编写成playbook，之后管理服务器会变得十分简单。 Ansible playbook格式 1）格式简介 playbook由YMAL语言编写。 YAML( /ˈjæməl/ )参考了其他多种语言，包括：XML、C语言、Python、Perl以及电子邮件格式RFC2822，Clark Evans在2001年5月在首次发表了这种语言，另外Ingy döt Net与OrenBen-Kiki也是这语言的共同设计者。 YMAL格式是类似于JSON的文件格式，便于人理解和阅读，同时便于书写。首先学习了解一下YMAL的格式，对我们后面书写playbook很有帮助。以下为playbook常用到的YMAL格式： 1、文件的第一行应该以 "---" (三个连字符)开始，表明YMAL文件的开始。 2、在同一行中，#之后的内容表示注释，类似于shell，python和ruby。 3、YMAL中的列表元素以”-”开头然后紧跟着一个空格，后面为元素内容。 4、同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。 5、play中hosts，variables，roles，tasks等对象的表示方法都是键值中间以":"分隔表示，":"后面还要增加一个空格。 下面是一个举例： --- #安装与运行mysql服务 - hosts: node1 remote_user: root tasks: - name: install mysql-server package yum: name=mysql-server state=present - name: starting mysqld service service: name=mysql state=started 我们的文件名称应该以.yml结尾，像我们上面的例子就是mysql.yml。其中，有三个部分组成： ​host部分：使用 hosts 指示使用哪个主机或主机组来运行下面的 tasks ，每个 playbook 都必须指定 hosts ，hosts也​可以使用通配符格式。主机或主机组在 inventory 清单中指定，可以使用系统默认的/etc/ansible/hosts，也可以自己编辑，在运行的时候加上-i选项，指定清单的位置即可。在运行清单文件的时候，–list-hosts选项会显示那些主机将会参与执行 task 的过程中。 ​remote_user：指定远端主机中的哪个用户来登录远端系统，在远端系统执行 task 的用户，可以任意指定，也可以使用 sudo，但是用户必须要有执行相应 task 的权限。 ​tasks：指定远端主机将要执行的一系列动作。tasks 的核心为 ansible 的模块，前面已经提到模块的用法。tasks 包含 name 和要执行的模块，name 是可选的，只是为了便于用户阅读，不过还是建议加上去，模块是必须的，同时也要给予模块相应的参数。 使用ansible-playbook运行playbook文件，得到如下输出信息，输出内容为JSON格式。并且由不同颜色组成，便于识别。一般而言 | 绿色代表执行成功，系统保持原样 | 黄色代表系统代表系统状态发生改变 | 红色代表执行失败，显示错误输出 执行有三个步骤：1、收集facts 2、执行tasks 3、报告结果 ​ 2）核心元素 Playbook的核心元素： ​Hosts：主机组； ​Tasks：任务列表； ​Variables：变量，设置方式有四种； ​Templates：包含了模板语法的文本文件； ​Handlers：由特定条件触发的任务； 3）基本组件 Playbooks配置文件的基础组件： ​Hosts：运行指定任务的目标主机 ​remoute_user：在远程主机上执行任务的用户； ​sudo_user： ​tasks：任务列表 格式： tasks： – name: TASK_NAME module: arguments notify: HANDLER_NAME handlers: – name: HANDLER_NAME module: arguments ​模块，模块参数： 格式： (1) action: module arguments (2) module: arguments 注意：shell和command模块后面直接跟命令，而非key=value类的参数列表； ​handlers：任务，在特定条件下触发；接收到其它任务的通知时被触发； (1) 某任务的状态在运行后为changed时，可通过“notify”通知给相应的handlers； (2) 任务可以通过“tags“打标签，而后可在ansible-playbook命令上使用-t指定进行调用； 举例 ① 定义playbook [root@server ~]# cd /etc/ansible [root@server ansible]# vim nginx.yml --- - hosts: web remote_user: root tasks: - name: install nginx yum: name=nginx state=present - name: copy nginx.conf copy: src=/tmp/nginx.conf dest=/etc/nginx/nginx.conf backup=yes notify: reload #当nginx.conf发生改变时，通知给相应的handlers tags: reloadnginx #打标签 - name: start nginx service service: name=nginx state=started tags: startnginx #打标签 handlers: #注意，前面没有-，是两个空格 - name: reload service: name=nginx state=restarted #为了在进程中能看出来 ② 测试运行结果 写完了以后，我们就可以运行了： [root@server ansible]# ansible-playbook nginx.yml 现在我们可以看看两台机器的端口是否开启： [root@server ansible]# ansible web -m shell -a 'ss -nutlp |grep nginx' 192.168.37.122 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=8304,fd=6),("nginx",pid=8303,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:80 : users:(("nginx",pid=9671,fd=6),("nginx",pid=9670,fd=6)) ③ 测试标签 我们在里面已经打上了一个标签，所以可以直接引用标签。但是我们需要先把服务关闭，再来运行剧本并引用标签： [root@server ansible]# ansible web -m shell -a 'systemctl stop nginx' [root@server ansible]# ansible-playbook nginx.yml -t startnginx ④ 测试notify 我们还做了一个notify，来测试一下： 首先，它的触发条件是配置文件被改变，所以我们去把配置文件中的端口改一下： [root@server ansible]# vim /tmp/nginx.conf listen 8080; 然后我们重新加载一下这个剧本： ​ 发现我们执行的就是reload段以及我们定义的notify部分。 我们来看一看我们的端口号： [root@server ansible]# ansible web -m shell -a 'ss -ntlp | grep nginx' 192.168.37.122 | SUCCESS | rc=0 >> LISTEN 0 128 *:8080 *:* users:(("nginx",pid=2097,fd=6),("nginx",pid=2096,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> LISTEN 0 128 *:8080 : users:(("nginx",pid=3061,fd=6),("nginx",pid=3060,fd=6)) 可以看出，我们的nginx端口已经变成了8080。 4）variables 部分 上文中，我们说到了variables是变量，有四种定义方法，现在我们就来说说这四种定义方法： ① facts ：可直接调用 上一篇中，我们有说到setup这个模块，这个模块就是通过调用facts组件来实现的。我们这里的variables也可以直接调用facts组件。 具体的facters我们可以使用setup模块来获取，然后直接放入我们的剧本中调用即可。 ② 用户自定义变量 我们也可以直接使用用户自定义变量，想要自定义变量有以下两种方式： 通过命令行传入 ansible-playbook命令的命令行中的-e VARS, --extra-vars=VARS，这样就可以直接把自定义的变量传入。 在playbook中定义变量 我们也可以直接在playbook中定义我们的变量： vars: - var1: value1 - - var2: value2 举例 ① 定义剧本 我们就使用全局替换把我们刚刚编辑的文件修改一下： [root@server ansible]# vim nginx.yml ​ 这样一来，我们的剧本就定义完成了。 ② 拷贝配置文件 我们想要在被监管的机器上安装什么服务的话，就直接在我们的server端上把该服务的配置文件拷贝到我们的/tmp/目录下。这样我们的剧本才能正常运行。 我们就以keepalived服务为例： [root@server ansible]# cp /etc/keepalived/keepalived.conf /tmp/keepalived.conf ③ 运行剧本，变量由命令行传入 [root@server ansible]# ansible-playbook nginx.yml -e rpmname=keepalived ④ 修改剧本，直接定义变量 同样的，我们可以直接在剧本中把变量定义好，这样就不需要在通过命令行传入了。以后想要安装不同的服务，直接在剧本里把变量修改一下即可。 [root@server ansible]# vim nginx.yml ⑤ 运行定义过变量的剧本 我们刚刚已经把变量定义在剧本里面了。现在我们来运行一下试试看： [root@server ansible]# ansible-playbook nginx.yml 发现这样也是可以的~ ③ 通过roles传递变量 具体的，我们下文中说到 roles 的时候再详细说明。这里是传送带 ④ Host Inventory 我们也可以直接在主机清单中定义。 定义的方法如下： 向不同的主机传递不同的变量： IP/HOSTNAME varaiable=value var2=value2 向组中的主机传递相同的变量： [groupname:vars] variable=value 5）模板 templates 模板是一个文本文件，嵌套有脚本（使用模板编程语言编写）。 Jinja2：Jinja2是python的一种模板语言，以Django的模板语言为原本。 模板支持： 字符串：使用单引号或双引号； 数字：整数，浮点数； 列表：[item1, item2, ...] 元组：(item1, item2, ...) 字典：{key1:value1, key2:value2, ...} 布尔型：true/false 算术运算： +, -, *, /, //, %, ** 比较操作： ==, !=, >, >=, <, <= 逻辑运算： and, or, not 通常来说，模板都是通过引用变量来运用的。 举例 ① 定义模板 我们直接把之前定义的/tmp/nginx.conf改个名，然后编辑一下，就可以定义成我们的模板文件了： [root@server ansible]# cd /tmp [root@server tmp]# mv nginx.conf nginx.conf.j2 [root@server tmp]# vim nginx.conf.j2 worker_processes {{ ansible_processor_vcpus }}; listen {{ nginxport }}; ② 修改剧本 我们现在需要去修改剧本来定义变量： [root@server ansible]# vim nginx.yml 需要修改的部分如图所示。 ③ 运行剧本 上面的准备工作完成后，我们就可以去运行剧本了： [root@server ansible]# ansible-playbook nginx.yml -t reloadnginx PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [copy nginx.conf] ********************************************************* ok: [192.168.37.122] ok: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=2 changed=0 unreachable=0 failed=0 192.168.37.133 : ok=2 changed=0 unreachable=0 failed=0 6）条件测试 when语句：在task中使用，jinja2的语法格式。 举例如下： tasks: - name: install conf file to centos7 template: src=files/nginx.conf.c7.j2 when: ansible_distribution_major_version == "7" - name: install conf file to centos6 template: src=files/nginx.conf.c6.j2 when: ansible_distribution_major_version == "6" 循环：迭代，需要重复执行的任务； 对迭代项的引用，固定变量名为"item"，而后，要在task中使用with_items给定要迭代的元素列表； 举例如下： tasks: - name: unstall web packages yum: name={{ item }} state=absent with_items: - httpd - php - php-mysql 7）字典 ansible playbook 还支持字典功能。举例如下： - name: install some packages yum: name={{ item }} state=present with_items: - nginx - memcached - php-fpm - name: add some groups group: name={{ item }} state=present with_items: - group11 - group12 - group13 - name: add some users user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user11', group: 'group11' } - { name: 'user12', group: 'group12' } - { name: 'user13', group: 'group13' } 8）角色订制：roles ① 简介 对于以上所有的方式有个弊端就是无法实现复用假设在同时部署Web、db、ha 时或不同服务器组合不同的应用就需要写多个yml文件。很难实现灵活的调用。 roles 用于层次性、结构化地组织playbook。roles 能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲，roles就是通过分别将变量(vars)、文件(file)、任务(tasks)、模块(modules)及处理器(handlers)放置于单独的目录中，并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中，但也可以是用于构建守护进程等场景中。 ② 角色集合 角色集合：roles/ mysql/ httpd/ nginx/ files/：存储由copy或script等模块调用的文件； tasks/：此目录中至少应该有一个名为main.yml的文件，用于定义各task；其它的文件需要由main.yml进行“包含”调用； handlers/：此目录中至少应该有一个名为main.yml的文件，用于定义各handler；其它的文件需要由main.yml进行“包含”调用； vars/：此目录中至少应该有一个名为main.yml的文件，用于定义各variable；其它的文件需要由main.yml进行“包含”调用； templates/：存储由template模块调用的模板文本； meta/：此目录中至少应该有一个名为main.yml的文件，定义当前角色的特殊设定及其依赖关系；其它的文件需要由main.yml进行“包含”调用； default/：此目录中至少应该有一个名为main.yml的文件，用于设定默认变量； ③ 角色定制实例 1. 在roles目录下生成对应的目录结构 [root@server ansible]# cd roles/ [root@server roles]# ls [root@server roles]# mkdir -pv ./{nginx,mysql,httpd}/{files,templates,vars,tasks,handlers,meta,default} [root@server roles]# tree . ├── httpd │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars ├── mysql │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── nginx ├── default ├── files ├── handlers ├── meta ├── tasks ├── templates └── vars 24 directories, 0 files 2. 定义配置文件 我们需要修改的配置文件为/tasks/main.yml，下面，我们就来修改一下： [root@server roles]# vim nginx/tasks/main.yml - name: cp copy: src=nginx-1.10.2-1.el7.ngx.x86_64.rpm dest=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm - name: install yum: name=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm state=latest - name: conf template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf tags: nginxconf notify: new conf to reload - name: start service service: name=nginx state=started enabled=true 3. 放置我们所需要的文件到指定目录 因为我们定义的角色已经有了新的组成方式，所以我们需要把文件都放到指定的位置，这样，才能让配置文件找到这些并进行加载。 rpm包放在files目录下，模板放在templates目录下： [root@server nginx]# cp /tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm ./files/ [root@server nginx]# cp /tmp/nginx.conf.j2 ./templates/ [root@server nginx]# tree . ├── default ├── files │ └── nginx-1.10.2-1.el7.ngx.x86_64.rpm ├── handlers ├── meta ├── tasks │ └── main.yml ├── templates │ └── nginx.conf.j2 └── vars 7 directories, 3 files 4. 修改变量文件 我们在模板中定义的变量，也要去配置文件中加上： [root@server nginx]# vim vars/main.yml nginxprot: 9999 5. 定义handlers文件 我们在配置文件中定义了notify，所以我么也需要定义handlers，我们来修改配置文件： [root@server nginx]# vim handlers/main.yml - name: new conf to reload service: name=nginx state=restarted 6. 定义剧本文件 接下来，我们就来定义剧本文件，由于大部分设置我们都单独配置在了roles里面，所以，接下来剧本就只需要写一点点内容即可： [root@server ansible]# vim roles.yml - hosts: web remote_user: root roles: - nginx 7. 启动服务 剧本定义完成以后，我们就可以来启动服务了： [root@server ansible]# ansible-playbook roles.yml PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : cp] ************************************************************** ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : install] ********************************************************* changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : conf] ************************************************************ changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : start service] *************************************************** changed: [192.168.37.122] changed: [192.168.37.133] RUNNING HANDLER [nginx : new conf to reload] *********************************** changed: [192.168.37.122] changed: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=6 changed=4 unreachable=0 failed=0 192.168.37.133 : ok=6 changed=4 unreachable=0 failed=0 启动过后照例查看端口号： [root@server ansible]# ansible web -m shell -a "ss -ntulp |grep 9999" 192.168.37.122 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:9999 *:* users:(("nginx",pid=7831,fd=6),("nginx",pid=7830,fd=6),("nginx",pid=7829,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:9999 : users:(("nginx",pid=9654,fd=6),("nginx",pid=9653,fd=6),("nginx",pid=9652,fd=6)) 可以看出我们的剧本已经执行成功。 作者：珂儿吖 出处：http://www.cnblogs.com/keerya/ 本文版权归作者和博客园共有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。 大家写文都不容易，希望尊重劳动成果哟~ 好文要顶 关注我 收藏该文 微信分享 +加关注 83 2 « 上一篇： 自动化运维工具——ansible详解（一） » 下一篇： 自动化运维工具——puppet详解（一） posted on 2017-12-08 14:18 珂儿吖 阅读(39916) 评论() 收藏 举报
自动化运维工具——ansible详解（一） 自动化运维工具——ansible详解（一） - 博客园 https://www.cnblogs.com/keerya/p/7987886.html ansible 简介 ansible 是什么？ ansible是新出现的自动化运维工具，基于Python开发，集合了众多运维工具（puppet、chef、func、fabric）的优点，实现了批量系统配置、批量程序部署、批量运行命令等功能。 ansible是基于 paramiko 开发的,并且基于模 2025-05-23 08:46 自动化运维工具——ansible详解（一） ansible 简介 ansible 是什么？ ansible是新出现的自动化运维工具，基于Python开发，集合了众多运维工具（puppet、chef、func、fabric）的优点，实现了批量系统配置、批量程序部署、批量运行命令等功能。 ansible是基于 paramiko 开发的,并且基于模块化工作，本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块，ansible只是提供一种框架。ansible不需要在远程主机上安装client/agents，因为它们是基于ssh来和远 程主机通讯的。ansible目前已经已经被红帽官方收购，是自动化运维工具中大家认可度最高的，并且上手容易，学习简单。是每位运维工程师必须掌握的技能之一。 ansible 特点 部署简单，只需在主控端部署Ansible环境，被控端无需做任何操作； 默认使用SSH协议对设备进行管理； 有大量常规运维操作模块，可实现日常绝大部分操作； 配置简单、功能强大、扩展性强； 支持API及自定义模块，可通过Python轻松扩展； 通过Playbooks来定制强大的配置、状态管理； 轻量级，无需在客户端安装agent，更新时，只需在操作机上进行一次更新即可； 提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。 ansible 架构图 上图中我们看到的主要模块如下： ​Ansible：Ansible核心程序。 ​HostInventory：记录由Ansible管理的主机信息，包括端口、密码、ip等。 ​Playbooks：“剧本”YAML格式文件，多个任务定义在一个文件中，定义主机需要调用哪些模块来完成的功能。 ​CoreModules：​核心模块，主要操作是通过调用核心模块来完成管理任务。 ​CustomModules：自定义模块，完成核心模块无法完成的功能，支持多种语言。 ​ConnectionPlugins：连接插件，Ansible和Host通信使用 ansible 任务执行 ansible 任务执行模式 Ansible 系统由控制主机对被管节点的操作方式可分为两类，即adhoc和playbook： ad-hoc模式(点对点模式) 使用单个模块，支持批量执行单条命令。ad-hoc 命令是一种可以快速输入的命令，而且不需要保存起来的命令。就相当于bash中的一句话shell。 playbook模式(剧本模式) 是Ansible主要管理方式，也是Ansible功能强大的关键所在。​playbook通过多个task集合完成一类功能，如Web服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作的配置文件。 ansible 执行流程 简单理解就是Ansible在运行时， 首先读取ansible.cfg中的配置， 根据规则获取Inventory中的管理主机列表， 并行的在这些主机中执行配置的任务， 最后等待执行返回的结果。 ansible 命令执行过程 加载自己的配置文件，默认/etc/ansible/ansible.cfg； 查找对应的主机配置文件，找到要执行的主机或者组； 加载自己对应的模块文件，如 command； 通过ansible将模块或命令生成对应的临时py文件(python脚本)， 并将该文件传输至远程服务器； 对应执行用户的家目录的.ansible/tmp/XXX/XXX.PY文件； 给文件 +x 执行权限； 执行并返回结果； 删除临时py文件，sleep 0退出； ansible 配置详解 ansible 安装方式 ansible安装常用两种方式，yum安装和pip程序安装。下面我们来详细介绍一下这两种安装方式。 使用 pip（python的包管理模块）安装 首先，我们需要安装一个python-pip包，安装完成以后，则直接使用pip命令来安装我们的包，具体操作过程如下： yum install python-pip pip install ansible 使用 yum 安装 yum 安装是我们很熟悉的安装方式了。我们需要先安装一个 epel-release包，然后再安装我们的 ansible 即可。 yum install epel-release -y yum install ansible –y ansible 程序结构 安装目录如下(yum安装)： 配置文件目录：/etc/ansible/ 执行文件目录：/usr/bin/ Lib库依赖目录：/usr/lib/pythonX.X/site-packages/ansible/ Help文档目录：/usr/share/doc/ansible-X.X.X/ Man文档目录：/usr/share/man/man1/ ansible配置文件查找顺序 ansible与我们其他的服务在这一点上有很大不同，这里的配置文件查找是从多个地方找的，顺序如下： 检查环境变量ANSIBLE_CONFIG指向的路径文件(export ANSIBLE_CONFIG=/etc/ansible.cfg)； ​~/.ansible.cfg，检查当前目录下的ansible.cfg配置文件； ​/etc/ansible.cfg检查etc目录的配置文件。 ansible配置文件 ansible 的配置文件为/etc/ansible/ansible.cfg，ansible 有许多参数，下面我们列出一些常见的参数： inventory = /etc/ansible/hosts #这个参数表示资源清单inventory文件的位置 library = /usr/share/ansible #指向存放Ansible模块的目录，支持多个目录方式，只要用冒号（：）隔开就可以 forks = 5 #并发连接数，默认为5 sudo_user = root #设置默认执行命令的用户 remote_port = 22 #指定连接被管节点的管理端口，默认为22端口，建议修改，能够更加安全 host_key_checking = False #设置是否检查SSH主机的密钥，值为True/False。关闭后第一次连接不会提示配置实例 timeout = 60 #设置SSH连接的超时时间，单位为秒 log_path = /var/log/ansible.log #指定一个存储ansible日志的文件（默认不记录日志） ansuble主机清单 在配置文件中，我们提到了资源清单，这个清单就是我们的主机清单，里面保存的是一些 ansible 需要连接管理的主机列表。我们可以来看看他的定义方式： 1、 直接指明主机地址或主机名： ## green.example.com# # blue.example.com# # 192.168.100.1 # 192.168.100.10 2、 定义一个主机组[组名]把地址或主机名加进去 [mysql_test] 192.168.253.159 192.168.253.160 192.168.253.153 需要注意的是，这里的组成员可以使用通配符来匹配，这样对于一些标准化的管理来说就很轻松方便了。 我们可以根据实际情况来配置我们的主机列表，具体操作如下： [root@server ~]# vim /etc/ansible/hosts [web] 192.168.37.122 192.168.37.133 ansible 常用命令 ansible 命令集 ​/usr/bin/ansible Ansibe AD-Hoc 临时命令执行工具，常用于临时命令的执行 ​/usr/bin/ansible-doc Ansible 模块功能查看工具 ​/usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块 的官网平台，基于网络的 ​/usr/bin/ansible-playbook Ansible 定制自动化的任务集编排工具 ​/usr/bin/ansible-pull Ansible远程执行命令的工具，拉取配置而非推送配置（使用较少，海量机器时使用，对运维的架构能力要求较高） ​/usr/bin/ansible-vault Ansible 文件加密工具 ​/usr/bin/ansible-console Ansible基于Linux Consoble界面可与用户交互的命令执行工具 其中，我们比较常用的是/usr/bin/ansible和/usr/bin/ansible-playbook。 ansible-doc 命令 ansible-doc 命令常用于获取模块信息及其使用帮助，一般用法如下： ansible-doc -l #获取全部模块的信息 ansible-doc -s MOD_NAME #获取指定模块的使用帮助 我们也可以查看一下ansible-doc的全部用法： [root@server ~]# ansible-doc Usage: ansible-doc [options] [module...] Options: -h, --help show this help message and exit # 显示命令参数API文档 -l, --list List available modules #列出可用的模块 -M MODULE_PATH, --module-path=MODULE_PATH #指定模块的路径 specify path(s) to module library (default=None) -s, --snippet Show playbook snippet for specified module(s) #显示playbook制定模块的用法 -v, --verbose verbose mode (-vvv for more, -vvvv to enable # 显示ansible-doc的版本号查看模块列表： connection debugging) --version show program's version number and exit 我们可以来看一下，以mysql相关的为例： [root@server ~]# ansible-doc -l |grep mysql mysql_db Add or remove MySQL databases from a remote... mysql_replication Manage MySQL replication mysql_user Adds or removes a user from a MySQL databas... mysql_variables Manage MySQL global variables [root@server ~]# ansible-doc -s mysql_user ansible 命令详解 命令的具体格式如下： ansible <host-pattern> [-f forks] [-m module_name] [-a args] 也可以通过ansible -h来查看帮助，下面我们列出一些比较常用的选项，并解释其含义： ​-a MODULE_ARGS #模块的参数，如果执行默认COMMAND的模块，即是命令参数，如： “date”，“pwd”等等#​#-k，#​--ask-passask for SSH password。登录密码，提示输入SSH密码而不是假设基于密钥的验证 ​--ask-su-pass​#ask for su password。su切换密码#​#-K，#​--ask-sudo-passask for sudo password。提示密码使用sudo，sudo表示提权操作 ​--ask-vault-pass​#ask for vault password。假设我们设定了加密的密码，则用该选项进行访问#​#-B SECONDS#后台运行超时时间 ​-C​#模拟运行环境并进行预运行，可以进行查错测试#​#-c CONNECTION#连接类型使用 ​-f FORKS​#并行任务数，默认为5#​#-i INVENTORY#指定主机清单的路径，默认为/etc/ansible/hosts ​--list-hosts​#查看有哪些主机组#​#-m MODULE_NAME#执行模块的名字，默认使用 command 模块，所以如果是只执行单一命令可以不用 -m参数 ​-o​#压缩输出，尝试将所有结果在一行输出，一般针对收集工具使用#​#-S#用 su 命令 ​-R SU_USER​#指定 su 的用户，默认为 root 用户#​#-s#用 sudo 命令 ​-U SUDO_USER​#指定 sudo 到哪个用户，默认为 root 用户#​#-T TIMEOUT#指定 ssh 默认超时时间，默认为10s，也可在配置文件中修改 ​-u REMOTE_USER​#远程用户，默认为 root 用户#​#-v#查看详细信息，同时支持-vvv，-vvvv可查看更详细信息 ansible 配置公私钥 上面我们已经提到过 ansible 是基于 ssh 协议实现的，所以其配置公私钥的方式与 ssh 协议的方式相同，具体操作步骤如下： #1.生成私钥 [root@server ~]# ssh-keygen #2.向主机分发私钥 [root@server ~]# ssh-copy-id root@192.168.37.122 [root@server ~]# ssh-copy-id root@192.168.37.133 这样的话，就可以实现无密码登录，我们的实验过程也会顺畅很多。 注意，如果出现了一下报错： -bash: ssh-copy-id: command not found 那么就证明我们需要安装一个包： yum -y install openssh-clientsansible 把包安装上即可。 ansible 常用模块 1）主机连通性测试 我们使用ansible web -m ping命令来进行主机连通性测试，效果如下： [root@server ~]# ansible web -m ping 192.168.37.122 | SUCCESS => { "changed": false, "ping": "pong" } 192.168.37.133 | SUCCESS => { "changed": false, "ping": "pong" } 这样就说明我们的主机是连通状态的。接下来的操作才可以正常进行。 2）command 模块 这个模块可以直接在远程主机上执行命令，并将结果返回本主机。 举例如下： [root@server ~]# ansible web -m command -a 'ss -ntl' 192.168.37.122 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:111 *:* LISTEN 0 5 192.168.122.1:53 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128 127.0.0.1:631 *:* LISTEN 0 128 *:23000 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::111 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 ::1:631 :::* LISTEN 0 100 ::1:25 :::* 192.168.37.133 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :111 : LISTEN 0 128 :22 : LISTEN 0 128 127.0.0.1:631 : LISTEN 0 128 :23000 : LISTEN 0 100 127.0.0.1:25 : LISTEN 0 128 :::111 ::: LISTEN 0 128 :::22 ::: LISTEN 0 128 ::1:631 ::: LISTEN 0 100 ::1:25 :::* 命令模块接受命令名称，后面是空格分隔的列表参数。给定的命令将在所有选定的节点上执行。它不会通过shell进行处理，比如$HOME和操作如"<"，">"，"|"，";"，"&" 工作（需要使用（shell）模块实现这些功能）。注意，该命令不支持| 管道命令。 下面来看一看该模块下常用的几个命令： chdir # 在执行命令之前，先切换到该目录 executable # 切换shell来执行命令，需要使用命令的绝对路径 free_form # 要执行的Linux指令，一般使用Ansible的-a参数代替。 creates # 一个文件名，当这个文件存在，则该命令不执行,可以 用来做判断 removes # 一个文件名，这个文件不存在，则该命令不执行 下面我们来看看这些命令的执行效果： [root@server ~]# ansible web -m command -a 'chdir=/data/ ls' #先切换到/data/ 目录，再执行“ls”命令 192.168.37.122 | SUCCESS | rc=0 >> aaa.jpg fastdfs mogdata tmp web wKgleloeYoCAMLtZAAAWEekAtkc497.jpg 192.168.37.133 | SUCCESS | rc=0 >> aaa.jpg fastdfs mogdata tmp web wKgleloeYoCAMLtZAAAWEekAtkc497.jpg [root@server ~]# ansible web -m command -a 'creates=/data/aaa.jpg ls' #如果/data/aaa.jpg存在，则不执行“ls”命令 192.168.37.122 | SUCCESS | rc=0 >> skipped, since /data/aaa.jpg exists 192.168.37.133 | SUCCESS | rc=0 >> skipped, since /data/aaa.jpg exists [root@server ~]# ansible web -m command -a 'removes=/data/aaa.jpg cat /data/a' #如果/data/aaa.jpg存在，则执行“cat /data/a”命令 192.168.37.122 | SUCCESS | rc=0 >> hello 192.168.37.133 | SUCCESS | rc=0 >> hello 3）shell 模块 shell模块可以在远程主机上调用shell解释器运行命令，支持shell的各种功能，例如管道等。 [root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep "keer"' 192.168.37.122 | SUCCESS | rc=0 >> keer:x:10001:1000:keer:/home/keer:/bin/sh 192.168.37.133 | SUCCESS | rc=0 >> keer:x:10001:10001::/home/keer:/bin/sh 只要是我们的shell命令，都可以通过这个模块在远程主机上运行，这里就不一一举例了。 4）copy 模块 这个模块用于将文件复制到远程主机，同时支持给定内容生成文件和修改权限等。 其相关选项如下： ​src #被复制到远程主机的本地文件。可以是绝对路径，也可以是相对路径。如果路径是一个目录，则会递归复制，用法类似于"rsync" ​content #用于替换"src"，可以直接指定文件的值 ​dest #必选项，将源文件复制到的远程主机的绝对路径 ​backup #当文件内容发生改变后，在覆盖之前把源文件备份，备份文件包含时间信息 ​directory_mode #递归设定目录的权限，默认为系统默认权限 ​force #当目标主机包含该文件，但内容不同时，设为"yes"，表示强制覆盖；设为"no"，表示目标主机的目标位置不存在该文件才复制。默认为"yes" ​others #所有的 file 模块中的选项可以在这里使用 用法举例如下： ① 复制文件： [root@server ~]# ansible web -m copy -a 'src=~/hello dest=/data/hello' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/hello", "gid": 0, "group": "root", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "mode": "0644", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512437093.55-228281064292921/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/hello", "gid": 0, "group": "root", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "mode": "0644", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512437093.74-44694985235189/source", "state": "file", "uid": 0 } ② 给定内容生成文件，并制定权限 [root@server ~]# ansible web -m copy -a 'content="I am keer\n" dest=/data/name mode=666' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "0421570938940ea784f9d8598dab87f07685b968", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "497fa8386590a5fc89090725b07f175c", "mode": "0666", "owner": "root", "size": 10, "src": "/root/.ansible/tmp/ansible-tmp-1512437327.37-199512601767687/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "0421570938940ea784f9d8598dab87f07685b968", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "497fa8386590a5fc89090725b07f175c", "mode": "0666", "owner": "root", "size": 10, "src": "/root/.ansible/tmp/ansible-tmp-1512437327.55-218104039503110/source", "state": "file", "uid": 0 } 我们现在可以去查看一下我们生成的文件及其权限： [root@server ~]# ansible web -m shell -a 'ls -l /data/' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-rw-rw- 1 root root 12 Dec 6 09:45 name 192.168.37.133 | SUCCESS | rc=0 >> total 40 -rw-rw-rw- 1 root root 12 Dec 5 09:45 name 可以看出我们的name文件已经生成，并且权限为666。 ③ 关于覆盖 我们把文件的内容修改一下，然后选择覆盖备份： [root@server ~]# ansible web -m copy -a 'content="I am keerya\n" backup=yes dest=/data/name mode=666' 192.168.37.122 | SUCCESS => { "backup_file": "/data/name.4394.2017-12-06@09:46:25~", "changed": true, "checksum": "064a68908ab9971ee85dbc08ea038387598e3778", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "8ca7c11385856155af52e560f608891c", "mode": "0666", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512438383.78-228128616784888/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "backup_file": "/data/name.5962.2017-12-05@09:46:24~", "changed": true, "checksum": "064a68908ab9971ee85dbc08ea038387598e3778", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "8ca7c11385856155af52e560f608891c", "mode": "0666", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512438384.0-170718946740009/source", "state": "file", "uid": 0 } 现在我们可以去查看一下： [root@server ~]# ansible web -m shell -a 'ls -l /data/' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-rw-rw- 1 root root 12 Dec 6 09:46 name -rw-rw-rw- 1 root root 10 Dec 6 09:45 name.4394.2017-12-06@09:46:25~ 192.168.37.133 | SUCCESS | rc=0 >> total 40 -rw-rw-rw- 1 root root 12 Dec 5 09:46 name -rw-rw-rw- 1 root root 10 Dec 5 09:45 name.5962.2017-12-05@09:46:24~ 可以看出，我们的源文件已经被备份，我们还可以查看一下name文件的内容： [root@server ~]# ansible web -m shell -a 'cat /data/name' 192.168.37.122 | SUCCESS | rc=0 >> I am keerya 192.168.37.133 | SUCCESS | rc=0 >> I am keerya 证明，这正是我们新导入的文件的内容。 5）file 模块 该模块主要用于设置文件的属性，比如创建文件、创建链接文件、删除文件等。 下面是一些常见的命令： ​force #需要在两种情况下强制创建软链接，一种是源文件不存在，但之后会建立的情况下；另一种是目标软链接已存在，需要先取消之前的软链，然后创建新的软链，有两个选项：yes|no ​group #定义文件/目录的属组。后面可以加上mode：定义文件/目录的权限 ​owner #定义文件/目录的属主。后面必须跟上path：定义文件/目录的路径 ​recurse #递归设置文件的属性，只对目录有效，后面跟上src：被链接的源文件路径，只应用于state=link的情况 ​dest #被链接到的路径，只应用于state=link的情况 ​state #状态，有以下选项： ​directory：如果目录不存在，就创建目录 ​file：即使文件不存在，也不会被创建 ​link：创建软链接 ​hard：创建硬链接 ​touch：如果文件不存在，则会创建一个新的文件，如果文件或目录已存在，则更新其最后修改时间 ​absent：删除目录、文件或者取消链接文件 用法举例如下： ① 创建目录： [root@server ~]# ansible web -m file -a 'path=/data/app state=directory' 192.168.37.122 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/data/app", "size": 6, "state": "directory", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/data/app", "size": 4096, "state": "directory", "uid": 0 } 我们可以查看一下： [root@server ~]# ansible web -m shell -a 'ls -l /data' 192.168.37.122 | SUCCESS | rc=0 >> total 28 drwxr-xr-x 2 root root 6 Dec 6 10:21 app 192.168.37.133 | SUCCESS | rc=0 >> total 44 drwxr-xr-x 2 root root 4096 Dec 5 10:21 app 可以看出，我们的目录已经创建完成。 ② 创建链接文件 [root@server ~]# ansible web -m file -a 'path=/data/bbb.jpg src=aaa.jpg state=link' 192.168.37.122 | SUCCESS => { "changed": true, "dest": "/data/bbb.jpg", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 7, "src": "aaa.jpg", "state": "link", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "dest": "/data/bbb.jpg", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 7, "src": "aaa.jpg", "state": "link", "uid": 0 } 我们可以去查看一下： [root@server ~]# ansible web -m shell -a 'ls -l /data' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-r--r-- 1 root root 5649 Dec 5 13:49 aaa.jpg lrwxrwxrwx 1 root root 7 Dec 6 10:25 bbb.jpg -> aaa.jpg 192.168.37.133 | SUCCESS | rc=0 >> total 44 -rw-r--r-- 1 root root 5649 Dec 4 14:44 aaa.jpg lrwxrwxrwx 1 root root 7 Dec 5 10:25 bbb.jpg -> aaa.jpg 我们的链接文件已经创建成功。 ③ 删除文件 [root@server ~]# ansible web -m file -a 'path=/data/a state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "path": "/data/a", "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "path": "/data/a", "state": "absent" } 我们可以查看一下： [root@server ~]# ansible web -m shell -a 'ls /data/a' 192.168.37.122 | FAILED | rc=2 >> ls: cannot access /data/a: No such file or directory 192.168.37.133 | FAILED | rc=2 >> ls: cannot access /data/a: No such file or directory 发现已经没有这个文件了。 6）fetch 模块 该模块用于从远程某主机获取（复制）文件到本地。 有两个选项： ​dest：用来存放文件的目录 ​src：在远程拉取的文件，并且必须是一个​file，不能是目录 具体举例如下： [root@server ~]# ansible web -m fetch -a 'src=/data/hello dest=/data' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/192.168.37.122/data/hello", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "remote_checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "remote_md5sum": null } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/192.168.37.133/data/hello", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "remote_checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "remote_md5sum": null } 我们可以在本机上查看一下文件是否复制成功。要注意，文件保存的路径是我们设置的接收目录下的被管制主机ip目录下： [root@server ~]# cd /data/ [root@server data]# ls 1 192.168.37.122 192.168.37.133 fastdfs web [root@server data]# cd 192.168.37.122 [root@server 192.168.37.122]# ls data [root@server 192.168.37.122]# cd data/ [root@server data]# ls hello [root@server data]# pwd /data/192.168.37.122/data 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 # 以哪个用户的身份执行 举例如下： ① 添加计划任务 [root@server ~]# ansible web -m cron -a 'name="ntp update every 5 min" minute=*/5 job="/sbin/ntpdate 172.17.0.1 &> /dev/null"' 192.168.37.122 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 192.168.37.133 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 我们可以去查看一下： [root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 可以看出，我们的计划任务已经设置成功了。 ② 删除计划任务 如果我们的计划任务添加错误，想要删除的话，则执行以下操作： 首先我们查看一下现有的计划任务： [root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null #Ansible: df everyday * 15 * * * df -lh >> /tmp/disk_total &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null #Ansible: df everyday 15 * * * df -lh >> /tmp/disk_total &> /dev/null 然后执行删除操作： [root@server ~]# ansible web -m cron -a 'name="df everyday" hour=15 job="df -lh >> /tmp/disk_total &> /dev/null" state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 192.168.37.133 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 删除完成后，我们再查看一下现有的计划任务确认一下： [root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 我们的删除操作已经成功。 8）yum 模块 顾名思义，该模块主要用于软件的安装。 其选项如下： ​name= #所安装的包的名称 ​state= #present--->安装， latest--->安装最新的, absent---> 卸载软件。 ​update_cache #强制更新yum的缓存 ​conf_file #指定远程yum安装时所依赖的配置文件（安装本地已有的包）。 ​disable_pgp_check #是否禁止GPG checking，只用于presentor latest。 ​disablerepo #临时禁止使用yum库。 只用于安装或更新时。 ​enablerepo #临时使用的yum库。只用于安装或更新时。 下面我们就来安装一个包试试看： [root@server ~]# ansible web -m yum -a 'name=htop state=present' 192.168.37.122 | 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 htop.x86_64 0:2.0.2-1.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n htop x86_64 2.0.2-1.el7 epel 98 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 98 k\nInstalled size: 207 k\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : htop-2.0.2-1.el7.x86_64 1/1 \n Verifying : htop-2.0.2-1.el7.x86_64 1/1 \n\nInstalled:\n htop.x86_64 0:2.0.2-1.el7 \n\nComplete!\n" ] } 192.168.37.133 | SUCCESS => { "changed": true, "msg": "Warning: RPMDB altered outside of yum.\n** Found 3 pre-existing rpmdb problem(s), 'yum check' output follows:\nipa-client-4.4.0-12.el7.centos.x86_64 has installed conflicts freeipa-client: ipa-client-4.4.0-12.el7.centos.x86_64\nipa-client-common-4.4.0-12.el7.centos.noarch has installed conflicts freeipa-client-common: ipa-client-common-4.4.0-12.el7.centos.noarch\nipa-common-4.4.0-12.el7.centos.noarch has installed conflicts freeipa-common: ipa-common-4.4.0-12.el7.centos.noarch\n", "rc": 0, "results": [ "Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--> Running transaction check\n---> Package htop.x86_64 0:2.0.2-1.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n htop x86_64 2.0.2-1.el7 epel 98 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 98 k\nInstalled size: 207 k\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : htop-2.0.2-1.el7.x86_64 1/1 \n Verifying : htop-2.0.2-1.el7.x86_64 1/1 \n\nInstalled:\n htop.x86_64 0:2.0.2-1.el7 \n\nComplete!\n" ] } 安装成功。 9）service 模块 该模块用于服务程序的管理。 其主要选项如下： ​arguments #命令行提供额外的参数 ​enabled #设置开机启动。 ​name= #服务名称 ​runlevel #开机启动的级别，一般不用指定。 ​sleep #在重启服务的过程中，是否等待。如在服务关闭以后等待2秒再启动。(定义在剧本中。) ​state #有四种状态，分别为：started--->启动服务， stopped--->停止服务， restarted--->重启服务， reloaded--->重载配置 下面是一些例子： ① 开启服务并设置自启动 [root@server ~]# ansible web -m service -a 'name=nginx state=started enabled=true' 192.168.37.122 | SUCCESS => { "changed": true, "enabled": true, "name": "nginx", "state": "started", …… } 192.168.37.133 | SUCCESS => { "changed": true, "enabled": true, "name": "nginx", "state": "started", …… } 我们可以去查看一下端口是否打开： [root@server ~]# ansible web -m shell -a 'ss -ntl' 192.168.37.122 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 *:* 192.168.37.133 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 : 可以看出我们的80端口已经打开。 ② 关闭服务 我们也可以通过该模块来关闭我们的服务： [root@server ~]# ansible web -m service -a 'name=nginx state=stopped' 192.168.37.122 | SUCCESS => { "changed": true, "name": "nginx", "state": "stopped", …… } 192.168.37.133 | SUCCESS => { "changed": true, "name": "nginx", "state": "stopped", …… } 一样的，我们来查看一下端口： [root@server ~]# ansible web -m shell -a 'ss -ntl | grep 80' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >> 可以看出，我们已经没有80端口了，说明我们的nginx服务已经关闭了。 10）user 模块 该模块主要是用来管理用户账号。 其主要选项如下： ​comment # 用户的描述信息 ​createhome # 是否创建家目录 ​force # 在使用state=absent时, 行为与userdel –force一致. ​group # 指定基本组 ​groups # 指定附加组，如果指定为(groups=)表示删除所有组 ​home # 指定用户家目录 ​move_home # 如果设置为home=时, 试图将用户主目录移动到指定的目录 ​name # 指定用户名 ​non_unique # 该选项允许改变非唯一的用户ID值 ​password # 指定用户密码 ​remove # 在使用state=absent时, 行为是与userdel –remove一致 ​shell # 指定默认shell ​state # 设置帐号状态，不指定为创建，指定值为absent表示删除 ​system # 当创建一个用户，设置这个用户是系统用户。这个设置不能更改现有用户 ​uid # 指定用户的uid 举例如下： ① 添加一个用户并指定其 uid [root@server ~]# ansible web -m user -a 'name=keer uid=11111' 192.168.37.122 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 11111, "home": "/home/keer", "name": "keer", "shell": "/bin/bash", "state": "present", "stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\nCreating mailbox file: File exists\n", "system": false, "uid": 11111 } 192.168.37.133 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 11111, "home": "/home/keer", "name": "keer", "shell": "/bin/bash", "state": "present", "stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\nCreating mailbox file: File exists\n", "system": false, "uid": 11111 } 添加完成，我们可以去查看一下： [root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep keer' 192.168.37.122 | SUCCESS | rc=0 >> keer:x:11111:11111::/home/keer:/bin/bash 192.168.37.133 | SUCCESS | rc=0 >> keer:x:11111:11111::/home/keer:/bin/bash ② 删除用户 [root@server ~]# ansible web -m user -a 'name=keer state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "force": false, "name": "keer", "remove": false, "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "force": false, "name": "keer", "remove": false, "state": "absent" } 一样的，删除之后，我们去看一下： [root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep keer' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >> 发现已经没有这个用户了。 11）group 模块 该模块主要用于添加或删除组。 常用的选项如下： ​gid= #设置组的GID号 ​name= #指定组的名称 ​state= #指定组的状态，默认为创建，设置值为absent为删除 ​system= #设置值为yes，表示创建为系统组 举例如下： ① 创建组 [root@server ~]# ansible web -m group -a 'name=sanguo gid=12222' 192.168.37.122 | SUCCESS => { "changed": true, "gid": 12222, "name": "sanguo", "state": "present", "system": false } 192.168.37.133 | SUCCESS => { "changed": true, "gid": 12222, "name": "sanguo", "state": "present", "system": false } 创建过后，我们来查看一下： [root@server ~]# ansible web -m shell -a 'cat /etc/group | grep 12222' 192.168.37.122 | SUCCESS | rc=0 >> sanguo:x:12222: 192.168.37.133 | SUCCESS | rc=0 >> sanguo:x:12222: 可以看出，我们的组已经创建成功了。 ② 删除组 [root@server ~]# ansible web -m group -a 'name=sanguo state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "name": "sanguo", "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "name": "sanguo", "state": "absent" } 照例查看一下： [root@server ~]# ansible web -m shell -a 'cat /etc/group | grep 12222' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >> 已经没有这个组的相关信息了。 12）script 模块 该模块用于将本机的脚本在被管理端的机器上运行。 该模块直接指定脚本的路径即可，我们通过例子来看一看到底如何使用的： 首先，我们写一个脚本，并给其加上执行权限： [root@server ~]# vim /tmp/df.sh #!/bin/bash date >> /tmp/disk_total.log df -lh >> /tmp/disk_total.log [root@server ~]# chmod +x /tmp/df.sh 然后，我们直接运行命令来实现在被管理端执行该脚本： [root@server ~]# ansible web -m script -a '/tmp/df.sh' 192.168.37.122 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.37.122 closed.\r\n", "stdout": "", "stdout_lines": [] } 192.168.37.133 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.37.133 closed.\r\n", "stdout": "", "stdout_lines": [] } 照例查看一下文件内容： [root@server ~]# ansible web -m shell -a 'cat /tmp/disk_total.log' 192.168.37.122 | SUCCESS | rc=0 >> Tue Dec 5 15:58:21 CST 2017 Filesystem Size Used Avail Use% Mounted on /dev/sda2 47G 4.4G 43G 10% / devtmpfs 978M 0 978M 0% /dev tmpfs 993M 84K 993M 1% /dev/shm tmpfs 993M 9.1M 984M 1% /run tmpfs 993M 0 993M 0% /sys/fs/cgroup /dev/sda3 47G 33M 47G 1% /app /dev/sda1 950M 153M 798M 17% /boot tmpfs 199M 16K 199M 1% /run/user/42 tmpfs 199M 0 199M 0% /run/user/0 192.168.37.133 | SUCCESS | rc=0 >> Tue Dec 5 15:58:21 CST 2017 Filesystem Size Used Avail Use% Mounted on /dev/sda2 46G 4.1G 40G 10% / devtmpfs 898M 0 898M 0% /dev tmpfs 912M 84K 912M 1% /dev/shm tmpfs 912M 9.0M 903M 1% /run tmpfs 912M 0 912M 0% /sys/fs/cgroup /dev/sda3 3.7G 15M 3.4G 1% /app /dev/sda1 1.9G 141M 1.6G 9% /boot tmpfs 183M 16K 183M 1% /run/user/42 tmpfs 183M 0 183M 0% /run/user/0 可以看出已经执行成功了。 13）setup 模块 该模块主要用于收集信息，是通过调用facts组件来实现的。 facts组件是Ansible用于采集被管机器设备信息的一个功能，我们可以使用setup模块查机器的所有facts信息，可以使用filter来查看指定信息。整个facts信息被包装在一个JSON格式的数据结构中，ansible_facts是最上层的值。 facts就是变量，内建变量 。每个主机的各种信息，cpu颗数、内存大小等。会存在facts中的某个变量中。调用后返回很多对应主机的信息，在后面的操作中可以根据不同的信息来做不同的操作。如redhat系列用yum安装，而debian系列用apt来安装软件。 ① 查看信息 我们可以直接用命令获取到变量的值，具体我们来看看例子： [root@server ~]# ansible web -m setup -a 'filter="*mem*"' #查看内存 192.168.37.122 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1116, "ansible_memory_mb": { "nocache": { "free": 1397, "used": 587 }, "real": { "free": 1116, "total": 1984, "used": 868 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1984 }, "changed": false } 192.168.37.133 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1203, "ansible_memory_mb": { "nocache": { "free": 1470, "used": 353 }, "real": { "free": 1203, "total": 1823, "used": 620 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1823 }, "changed": false } 我们可以通过命令查看一下内存的大小以确认一下是否一致： [root@server ~]# ansible web -m shell -a 'free -m' 192.168.37.122 | SUCCESS | rc=0 >> total used free shared buff/cache available Mem: 1984 404 1122 9 457 1346 Swap: 3813 0 3813 192.168.37.133 | SUCCESS | rc=0 >> total used free shared buff/cache available Mem: 1823 292 1207 9 323 1351 Swap: 3813 0 3813 可以看出信息是一致的。 ② 保存信息 我们的setup模块还有一个很好用的功能就是可以保存我们所筛选的信息至我们的主机上，同时，文件名为我们被管制的主机的IP，这样方便我们知道是哪台机器出的问题。 我们可以看一看例子： [root@server tmp]# ansible web -m setup -a 'filter="*mem*"' --tree /tmp/facts 192.168.37.122 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1115, "ansible_memory_mb": { "nocache": { "free": 1396, "used": 588 }, "real": { "free": 1115, "total": 1984, "used": 869 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1984 }, "changed": false } 192.168.37.133 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1199, "ansible_memory_mb": { "nocache": { "free": 1467, "used": 356 }, "real": { "free": 1199, "total": 1823, "used": 624 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1823 }, "changed": false } 然后我们可以去查看一下： [root@server ~]# cd /tmp/facts/ [root@server facts]# ls 192.168.37.122 192.168.37.133 [root@server facts]# cat 192.168.37.122 {"ansible_facts": {"ansible_memfree_mb": 1115, "ansible_memory_mb": {"nocache": {"free": 1396, "used": 588}, "real": {"free": 1115, "total": 1984, "used": 869}, "swap": {"cached": 0, "free": 3813, "total": 3813, "used": 0}}, "ansible_memtotal_mb": 1984}, "changed": false} 作者：珂儿吖 出处：http://www.cnblogs.com/keerya/ 本文版权归作者和博客园共有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。 大家写文都不容易，希望尊重劳动成果哟~ 好文要顶 关注我 收藏该文 微信分享 +加关注 342 15 « 上一篇： 项目实战——企业级Zabbix监控实战（一） » 下一篇： 自动化运维工具——ansible详解（二） posted on 2017-12-05 16:31 珂儿吖 阅读(454136) 评论() 收藏 举报
Python中使用变量，不需要声明，直接为变量赋值即可 在Python数值分成了三种：整数、浮点数（小数）、复数 整数无限大 所有整数都为int型 其他进制的整数，只要是数字打印时一定是以十进制的形式显示的 浮点数（小数），在Python中所有的小数都是float类型 字符串必须使用引号引起来，不使用不是字符串 引号可以是双引号，也可以是单引号，但是注意不要混着用 单引号和双引号不能跨行使用 三重引号可以换行，并且会保留字符串中的格式 可以使用 \ 作为转义字符，通过转义字符，可以在字符串中使用一些特殊的内容 格式化字符串，可以通过在字符串前添加一个f来创建一个格式化字符串 如果将字符串和数字相乘，则解释器会将字符串重复指定的次数并返回 布尔值实际上也属于整型，True就相当于1，False就相当于0 type()用来检查值的类型，该函数会将检查的结果作为返回值返回，可以通过变量来接收函数的返回值 类型转换 int() 可以用来将其他的对象转换为整型 布尔值：True -> 1 False -> 0 浮点数：直接取整，省略小数点后的内容 字符串：合法的整数字符串，直接转换为对应的数字 float() 和 int()基本一致，不同的是它会将对象转换为浮点数 str() 可以将对象转换为字符串 bool() 可以将对象转换为布尔值，任何对象都可以转换为布尔值 规则：对于所有表示空性的对象都会转换为False，其余的转换为True
#include <stdio.h> #include <string.h> #include <time.h> int main() { int filter[100]; //所有同学身高 int TxNumber; //同学数量 int MaxNumber = 0; //记录最大序列总人数 int LMax[3000]; //以最高同学为中间点，左侧最大同学数（不含最高同学本人） int RMax[3000]; while (scanf("%d", &TxNumber) != EOF) { // 注意 while 处理多个 case // 64 位输出请用 printf("%lld") to memset(LMax, 0 , 3000); memset(RMax, 0 , 3000); for (int i = 0; i < TxNumber; i++) { scanf("%d",&filter[i]); } //计算左侧最长序列 for (int i = 1; i < TxNumber; i++) { for (int j = 0; j < i; j++) { printf("LMax[%d] = %d\n",i,LMax[i]); if ((filter[j] < filter[i]) && (LMax[i] < RMax[j] + 1)) { LMax[i] = LMax[j] + 1; printf("LMax[%d] = %d\n",i,LMax[i]); } } } //计算右侧 for (int i = TxNumber - 2; i >= 0; i--) { for (int j = TxNumber - 1; j > i; j--) { if ((filter[j] < filter[i]) && (LMax[i] < RMax[j] + 1)) { RMax[i] = RMax[j] + 1; } } } //统计队列最长人数 for (int i = 0; i < TxNumber; i++) { int tmp = LMax[i] + RMax[i] + 1; if (MaxNumber < tmp) { printf("LMax[%d] = %d\n",i,LMax[i]); printf("RMax[%d] = %d\n",i,RMax[i]); printf("tmp = %d\n",tmp); MaxNumber = tmp; } } printf("TxNumber = %d\n",TxNumber); printf("MaxNumber = %d\n",MaxNumber); } return 0; }
概念 机器学习是实现人工智能的一种途径，深度学习是机器学习的一个子集 传统机器学习方法：线性回归、逻辑回归、决策树、向量机、贝叶斯模型 机器学习算法 有监督学习 通过训练数据学到或建立“函数”并依此推测新的实例 训练数据：输入特征和输出目标（tag） 典型算法：决策树、随机森林、逻辑回归 使用场景：学习输入和输出的关系 应用于标签数据易获取、维度关系复杂场景 基于分类算法的精准用户营销、用户迁移 图像识别 无监督学习 通过无标签数据推断“函数”。用于描述数据中隐藏结构 训练数据：用于引导学习算法 使用场景：基于训练合集的数据特征，学习数据集上有用的结构性质 概率分布、主成分分析 应用于标签数据获取困难，需要寻找数据间关系场景 基于聚类算法的业务质量异常检测 基于关联规则挖掘的KQI&KPI相关性学习 强化学习 与环境交互过程中通过学习策略函数已达成回报函数最大化 训练数据：与环境交互的试探和回报 典型算法：值迭代、策略迭代 使用场景：利用试错的方法训练主体在复杂环境中进行决策的过程，主体在训练过程中的典型目标是需要最大化在环境中所获取的奖励信息，而这些奖励则与大量的参数相关 应用于游戏AI、机器人学、空战博弈 深度学习 深度学习是一种主要使用深度神经网络为工具的机器学习算法。 深度学习源于人工神经网络的研究，通过组合低层特征形成更加抽象的高层来表示属性类别或特征，以发现数据的分布式特征表示。 广泛应用于计算机视觉、自然语言处理等领域。 算力单位 量级单位+每秒运算次数+数据类型 量级单位 10^3为量级 K-M-G-T-P-E 每秒运算次数 OPS：每秒整数运算次数 FLOPS：每秒浮点运算次数 数据类型 INT8:8位整数型数据 深度学习模型的推理运算 FP16:16位浮点型数据 深度学习模型的训练运算 FP32:32位浮点型数据 高性能计算
描述 在本题中，我们需要处理地址信息，其由 IP 地址和子网掩码组成，这两者均形如"*.*.*.*"，由四段数字组成（每一个 ‘*’表示一个数字），每一段的数字均为 0 到 255 之间的一个整数，每段数字之间以点分隔。 我们定义五类 IP 地址： A 类："1.0.0.0"∼"127.255.255.255"； B 类："128.0.0.0"∼"191.255.255.255"； C 类："192.0.0.0"∼"223.255.255.255"； D 类："224.0.0.0"∼"239.255.255.255"； E 类："240.0.0.0"∼"255.255.255.255"。 我们定义私有 IP 地址： "10.0.0.0"∼"10.255.255.255"； "172.16.0.0"∼"172.31.255.255"； "192.168.0.0"∼"192.168.255.255"。 我们定义合法的子网掩码为：将掩码的每一段数字依次转换为八位长度的二进制字符串并进行拼接，这个字符串必须由若干个连续的 1 后跟若干个连续的 0 组成，才视为子网掩码合法。 例如，掩码 "255.254.255.0" 转换拼接得到字符串 11111111 11111110 11111111 00000000，显然不合法；掩码"255.255.255.248" 转换拼接得到字符串 11111111 11111111 11111111 11111000，合法。注意，全为 1 或全为 00的掩码也视为非法。 我们定义错误的 IP 地址和错误的子网掩码为不符合上述定义的 IP 地址和子网掩码。例如，格式错误、数字超出范围等等。 现在，你需要分类统计 A、B、C、D、E 类地址的数量、错误 IP 或错误子网掩码的数量、私有 IP 的数量。 特别地： 类似 "0.*.*.*" 和 ""127.*.*.*" 的 IP 地址不计入任何类别，也不计入非法统计，直接跳过； 一个 IP 既可计入私有 IP，也可计入五类地址之一，二者分别累计。 输入描述： 本题将会给出1≦T≦1000 条地址信息，确切数字未知，您需要一直读取至文件结尾；您也可以参考 牛客网在线判题系统使用帮助 获得更多的使用帮助。每条地址信息描述如下： 每行输入一个"*.*.*.*" 形式的 IP 地址和一个"*.*.*.*" 形式的子网掩码，中间用波浪线（∼）分隔。保证‘*’ 要么为空，要么是一个 0 到 255 间的整数。 输出描述： 在一行上输出七个整数，分别代表 A 类地址数、B 类地址数、C 类地址数、D 类地址数、E 类地址数、错误 IP 或错误子网掩码数、私有 IP 数。 示例1 输入： 10.70.44.68~1.1.1.5 1.0.0.1~255.0.0.0 192.168.0.2~255.255.255.0 19..0.~255.255.255.0 输出：1 0 1 0 0 2 1 说明： 在这个样例中： 第一条地址信息：掩码非法； 第二条地址信息：IP 格式和掩码均合法，属于 A 类； 第三条地址信息：IP 格式和掩码均合法，属于 C 类私有地址； 第四条地址信息：IP 格式非法。 统计得到 11 个 A 类，00 个 B 类，11 个 C 类，00 个 D 类，00 个 E 类，22 个错误条目，11 个私有地址。 示例2 输入： 0.201.56.50~255.255.255.0 127.201.56.50~255.255.111.255 输出：0 0 0 0 0 0 0 说明：在这个样例中，两条地址信息均属于上方提示中提到的特殊 IP 地址，不需要处理，直接跳过。特别需要注意地，第二条地址的子网掩码是非法的。但是因为该条为特殊 IP 地址，此优先级更高，所以不进入统计。 思路 以4位IP数组和4位Mask数组记录IP和掩码 排除0和127开头IP 用状态码标记为不计数状态 掩码格式检查 排除首位非255情况 用状态码计数255和0的出现次数，排除全255、全0情况 十进制转二进制 二进制掩码格式为先1后0，用状态码记录是否有0出现，若出现过0且掩码出现1，则非法 IP格式检查 0-255范围 分类 按IP首位分类五类IP，对应分类计数+1 在五类IP分类中标注特定开头的私有IP，私有IP+1，对应分类+1 题解 #include <stdio.h> int mask_filter(int mask[4]); int mask_filter(int mask[4]) { //设置十进制临时掩码temp以及二进制临时掩码temp_mask[7] int temp; int temp_mask[7]; //设置二进制数状态量k，初始值为-1，当出现0时，修改状态量为1 int k = -1; //计数掩码为0和掩码为255的情况 int status_0 = 0; int status_255 = 0; //首位不为255，则非法 if (mask[0] != 255) { return -1; } //遍历，统计掩码为0和掩码为255情况 for (int i = 0; i < 4; i++) { if (mask[i] == 255) { status_255++; } else if (mask[i] == 0) { status_0++; } //将临时掩码转换为二进制 temp = mask[i]; for (int j = 7; j >= 0; j--) { temp_mask[j] = temp%2; temp /= 2; } //掩码为0，则状态量k修改为1，若出现掩码0状态量k1的情况，就表示1之间插入了0，非法 for (int j = 0; j < 8; j++) { if (temp_mask[j] == 1) { if (k == 1) { return -1; } } else if (temp_mask[j] == 0) { k = 1; } } } //掩码全为255或全为0，非法 if (status_0 == 4) { return -1; } else if (status_255 == 4) { return -1; } 非法情况均排除 return 1; } //IP不在0~255之间，非法 int ip_filter(int* ip) { for (int i = 0; i < 4; i++) { if (ip[i] < 0 || ip[i] > 255) { return -1; } } return 1; } int main() { //初始化ip和掩码数组 int ip[4] = {0}, mask[4] = {0}; int A_num = 0, B_num = 0, C_num = 0, D_num = 0, E_num = 0, error_num = 0,pri_num = 0; //skip为1时表示此IP不计数 int skip = 0; char str[1024]; //按行赋值str while (fgets(str, sizeof(str), stdin) != NULL) { //将'\n'换行符替换为'\0'，使后面内容无效 str[strcspn(str, "\n")] = '\0'; //开头为127.或0.时不计数 if ((str[0] == '1') && (str[1] == '2')&& (str[2] == '7')&& (str[3] == '.')) { skip = 1; } else if ((str[0] == '0') && (str[1] == '.')) { skip = 1; } //在输入内容格式标准无空缺，IP计数情况下 if ((sscanf(str, "%d.%d.%d.%d~%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3],&mask[0], &mask[1], &mask[2], &mask[3]) == 8) && skip != 1) { //掩码、IP格式判断 if (mask_filter(mask) == -1) { error_num++; } else if (ip_filter(ip) == -1) { error_num++; } //IP分类划分 else { if (ip[0] >= 1 && ip[0] <= 127) { if (ip[0] == 10) { pri_num++; A_num++; } else { A_num++; } } else if (ip[0] >= 128 && ip[0] <= 191) { if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) { pri_num++; B_num++; } else { B_num++; } } else if (ip[0] >= 192 && ip[0] <= 223) { if (ip[0] == 192 && ip[1] == 168) { pri_num++; C_num++; } else { C_num++; } } else if (ip[0] >= 224 && ip[0] <= 239) { D_num++; } else if (ip[0] >= 240 && ip[0] <= 255) { E_num++; } } } //输入IP格式非法但计数情况 else if (skip != 1) { error_num++; } skip = 0; } printf("%d %d %d %d %d %d %d", A_num, B_num, C_num, D_num, E_num, error_num,pri_num); return 0; }
描述 我们定义一个无限大的二维网格上有一个小人，小人初始位置为 (0,0)(0,0) 点，小人可以读取指令上下左右移动。一个合法的指令由三至四个符号组成： 第一个符号为 "A/D/W/S"中的一个，代表小人移动的方向；分别代表向左、向右、向上、向下移动；记某个时刻小人的坐标为 (x,y)(x,y)，向左移动一格即抵达(x−1,y)、向右移动一格即抵达(x+1,y)、向上移动一格即抵达(x,y+1)、向下移动一格即抵达 (x,y−1)。 最后一个符号为 ‘;’，代表指令的结束，该符号固定存在；中间为一个大于 00 且小于 100 的数字，代表小人移动的距离。特别地，如果这个数字小于 1010，那么它可能包含一个前导零，此时也视为合法。 如果你遇到了一个不合法的指令，则直接忽略；例如，指令 "A100;" 是不合法的，因为 100超出了规定的数字范围；"Y10;" 也是不合法的，因为 YY 不是 "A/D/W/S" 中的一个。 输出小人最终的坐标。 输入描述： 在一行上输入一个长度 1≦length(s)≦104，由大写字母、数字和分号（‘;’）构成的字符串 s，代表输入的指令序列。保证字符串中至少存在一个 ‘;’，且末尾一定为 ‘;’。 输出描述： 在一行上输出一个两个整数，代表小人最终位置的横纵坐标，使用逗号间隔。 示例1 输入：A10;S20;W10;D30;X;A1A;B10A11;;A10; 输出：10,-10 说明： 对于这个样例，我们模拟小人的移动过程： 第一个指令 "A10;""A10;" 是合法的，向左移动 1010 个单位，到达 (−10,0)(−10,0) 点； 第二个指令 "S20;""S20;" 是合法的，向下移动 2020 个单位，到达 (−10,−20)(−10,−20) 点； 第三个指令 "W10;""W10;" 是合法的，向上移动 1010 个单位，到达 (−10,−10)(−10,−10) 点； 第四个指令 "D30;""D30;" 是合法的，向右移动 3030 个单位，到达 (20,−10)(20,−10) 点； 第五个指令 "X;""X;" 不合法，跳过； 第六个指令 "A1A;""A1A;" 不合法，跳过； 第七个指令 "B10A11;""B10A11;" 不合法，跳过； 第八个指令 ";"";" 不合法，跳过； 第九个指令 "A10;""A10;" 是合法的，向左移动 1010 个单位，到达 (10,−10)(10,−10) 点。 ##示例2 输入：ABC;AKL;DA1;D001;W023;A100;S00; 输出：0,0 说明：在这个样例中，全部指令均不合法，因此小人不移动。 示例3 输入：A00;S01;W2; 输出：0,1 思路 分为三个模块 第一个模块将输入字符串分割，采用双指针识别，i指向位移长度开头，默认为1（i-1为位移方向），j指向‘；’ 第二个模块为单独函数，导入字符串和双指针，将char型位移长度转为int型 第三个模块用Switch判断位移方向，ASWD则计算坐标变化，否则跳过 题解 #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *str = (char *)malloc(10000); char direction; int step_size = 0; int i = 1, j; int x = 0, y = 0; scanf("%s", str); // 去掉\n，避免读取换行符的问题 for(j = 0; j <= strlen(str); j++) { if (str[j] == ';') { direction = str[i - 1]; if ((j - i) < 5) { // 指令长度不超过4 step_size = judgeNum(str, i, j); switch (direction) { case 'A': x -= step_size; break; case 'D': x += step_size; break; case 'W': y += step_size; break; case 'S': y -= step_size; break; default: break; } } i = j + 2; // 更新i到下一个指令的开始 } } printf("%d,%d\n", x, y); free(str); // 释放内存 return 0; } int judgeNum(char *str, int i, int j) { int num = 0; int k; for (k = i; k < j; k++) { if(str[k] >= '0' && str[k] <= '9') { num = num * 10 + (str[k] - '0'); } else { return 0; // 遇到非数字字符，返回0 } } return num; }