Ansible----playbook模块之templates模块、tags模块、roles模块

news2025/2/26 5:03:42

目录

引言

一、templates模块

(一)关键信息

(二)实际操作

1.定义主机组

2.设置免密登录

3.分别建立访问目录

4.定义模板文件

5.创建playbook文件

6.执行剧本

7.验证结果

二、tags模块

(一)创建playbook文件

(二)调用标签

(三)跳过标签

(四)特殊标签

(五)play级标签

三、role模块

(一)角色的优势

(二)基本构成

(三)模块使用

1.定义主机组

2.创建目录组件

3.编写各类文件

3.1 定义变量

3.2 制定任务

3.3 调用角色模块

4.执行模块

(四)多角色搭建服务

1.创建目录

2.编写模块文件

2.1 编写mysql模块文件

2.2 编写php模块

3.调用角色模块

4.执行模块


引言

Ansible 是一款强大的自动化运维工具,它通过简单的配置文件(playbooks)和可复用的模块(modules)以及角色(roles)来描述 IT 基础架构的配置和部署过程。下面我将详细介绍如何编写一个Ansible Playbook,并展示如何使用模块和角色来组织和简化配置管理

一、templates模块

在 Ansible 中,templates 模块不是一个直接可调用的模块,而是通过 template 操作(action)来实现模板文件处理的功能。template 操作是 Ansible 的核心功能之一,它允许用户使用 Jinja2 模板语言来动态生成配置文件或其他文本文件,并将这些文件分发到远程主机上

Jinja是基于Python的模板引擎。Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记

(一)关键信息

模板文件:模板文件需要存储在 playbook 目录下的 templates 子目录中,并且文件名以 .j2 结尾,表明这是 Jinja2 模板文件。例如,一个名为 httpd.conf.j2 的文件可能包含了 Apache 配置,其中包含了一些由 Ansible 变量填充的占位符。

变量注入:Jinja2 模板支持从 Ansible 的变量中提取值,这些变量可以是 playbook 中定义的,也可以是从 inventory、角色默认变量、主机变量等来源获取的。变量使用双大括号 {{ variable_name }} 进行引用。

动态生成配置:当 playbook 运行时,Ansible 会处理这些模板文件,用实际的变量值替换模板中的占位符,从而生成最终的配置文件内容。

(二)实际操作

1.定义主机组

[root@ansible opt]#sed -n '32,34p' /etc/ansible/hosts 
[web]
192.168.83.80 http_port=192.168.83.80:10000 server_name=www.pla.com:10000  dir_path=/data/html/
192.168.83.90 http_port=192.168.83.90:10001 server_name=www.china.com:10001  dir_path=/data/html/

[web]
#主机组名

第一条记录:

IP地址:192.168.83.80

HTTP端口配置:http_port=192.168.83.80:10000
#定义httpd_port变量值为192.168.83.80:10000,在后面通过编写.j2文件调用
#意味着这个服务器上的Web服务将在IP地址192.168.83.80的10000端口上监听

服务器名称:server_name=www.pla.com:10000
#同样定义变量值,在后面通过编写.j2文件调用
#表示通过域名www.pla.com和端口10000可以访问到这个服务器。

根目录:dir_path=/data/html/
#指出该Web服务器存储网页文件的根目录位于/data/html/下。

第二条记录:

IP地址:192.168.83.90 

HTTP端口配置:http_port=192.168.83.90:10001
#这个服务器上的Web服务将在IP地址192.168.83.80的10001端口上监听

服务器名称:server_name=www.pla.com:10001
#与第一条记录相同

根目录:dir_path=/data/html/
#与第一个服务器意思相同

2.设置免密登录

[root@ansible opt]#ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:NMIiOsO4IB4KL4WshtpL8gk/U+uY6a1rT9nE3xKqZlE root@ansible
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|     .           |
|  . . o o        |
|+o . oEo .       |
|X+.  .o S        |
|O*. o+ o o       |
|Ooo.ooo o .      |
|oO+B+.   .       |
|.o&@=            |
+----[SHA256]-----+
[root@ansible opt]#sshpass -p '123' ssh-copy-id root@192.168.83.80
[root@ansible opt]#sshpass -p '123' ssh-copy-id root@192.168.83.90

3.分别建立访问目录

[root@ansible opt]#echo "welcome to 192.168.83.80" >index.html
[root@ansible opt]#echo "welcome to 192.168.83.90" >/data/index.html

4.定义模板文件

[root@ansible data]#ls
httpd.conf
[root@ansible data]#cp  httpd.conf httpd.conf.j2
[root@ansible data]#ls
httpd.conf  httpd.conf.j2
[root@ansible data]#vim  httpd.conf.j2
[root@ansible data]#cat -n httpd.conf.j2| sed -n '42p;95p;118,125p'
    42	Listen {{http_port}}          
    95	ServerName {{server_name}}
   118	#
   119	DocumentRoot "{{dir_path}}"
   120	
   121	#
   122	# Relax access to content within /var/www.
   123	#
   124	<Directory "{{dir_path}}">
   125	    AllowOverride None
#所有的变量,取自于/etc/ansible/hosts文件中主机后面定义的变量值
-------------------------------------------------------------------------------------
Listen {{http_port}}  
#指定HTTP服务监听的端口,这里使用模板变量{{http_port}}动态设定        

ServerName {{server_name}}
#设置服务器的主机名或IP与端口号,同样使用模板变量{{server_name}}动态设定

DocumentRoot "{{dir_path}}"
#设置访问目录

<Directory "{{dir_path}}">
#同样设置访问目录,给目录授权,使httpd程序可以访问站点目录

5.创建playbook文件

name: httpd
#指定了剧本名称,

hosts: web
#指定了操作的目标为主机组web,且以root用户身份远程登录执行任务。

Vars: 
#变量定义部分,用于设定剧本中将会引用的变量值。
#这里定义了两个变量:package为需要安装的软件包名(httpd),service为服务名(也是httpd)

tasks:
  - name: firewalld
    service: name=firewalld state=stopped
#关闭防火墙服务firewalld,以便HTTP服务可以无障碍地接收外部请求。
  - name: install httpd
      yum: name={{package}}
#使用YUM包管理器安装package定义的变量名的服务

  - name: config
    template: src=/data/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
#使用template模板功能将/data/httpd.conf.j2模板文件复制并到/etc/httpd/conf/httpd.conf
#作为HTTP服务器的配置文件。template模板会自动将httpd.conf.j2模板文件中定义的变量,替换为
#ansible的hosts文件中定义的各个变量值

    notify: restart httpd
#此步骤通过notify: restart httpd来通知处理程序在配置更改后重启httpd服务。

  - name: dir_path
    file: path=/data/html/ state=directory
#确保/data/html/目录存在,用于存放网页文件。

  - name: start httpd
    service: name=httpd state=started
#启动httpd服务。

  - name: index01
    copy: src=/opt/index.html dest=/data/html/
    when: ansible_default_ipv4.address == '192.168.83.80'
  - name: index02
    copy: src=/data/index.html dest=/data/html/
    when: ansible_default_ipv4.address == '192.168.83.90'
#根据远程主机的IPv4地址条件性地从不同源路径拷贝index.html文件到/data/html/目录下。
#如果主机IP为192.168.83.80,则从/opt/index.html拷贝;
#如果为192.168.83.90,则从/data/index.html拷贝。
handlers:
  - name: restart httpd
    service: name={{service}} state=restarted
#handlers处理程序块,定义了在特定条件下执行的操作。这里只有一个处理程序restart httpd,
#当有任务通知(通过notify: restart httpd)时,会重启httpd服务,确保配置变更生效。

6.执行剧本

7.验证结果

7.1 访问验证

7.2 查看配置文件

二、tags模块

在Ansible中,tags模块并不是一个实际的模块,而是Playbooks中一个非常有用的功能,它允许用户为任务或者播放(plays)分配标签(tags)。这些标签可以帮助你在执行Playbook时,有选择性地执行特定的任务集,而不是默认执行所有任务。这对于大型Playbook的管理和维护特别有帮助,因为你可以在不同的场景下,快速执行或跳过某些任务

(一)创建playbook文件

[root@ansible data]#vim tags.yaml
[root@ansible data]#cat tags.yaml 
---
- name: tags_server
  hosts: web
  tasks:
    - name:
      file: path=/opt/test/ state=directory
      tags:
        - dir   #设置标签名称为dir
    - name:
      file: path=/opt/test01/ state=directory
      tags:
        - dira  #设置标签名称为dira
    - name:
      file: path=/opt/test02/ state=directory
      tags:
        - dirb  #设置标签名称为dirb

(二)调用标签

当你运行Playbook时,可以通过--tags选项指定你想要执行的标签。Ansible将仅执行那些匹配指定标签的任务

ansible-playbook 剧本名 --tags="标签名"

去远程主机查看

[root@client01 opt]#ls
rh  test01
#只执行了指定标签的任务,建立了test01目录文件

(三)跳过标签

如果你想执行除了某些标签之外的所有任务,可以使用--skip-tags选项

在远程主机查看

(四)特殊标签

Ansible还支持一些特殊标签,如always和never。带有always标签的任务总会被执行,而带有never标签的任务则永远不会被执行,无论命令行中指定了哪些标签

重新定义一个playbook剧本

执行剧本

指定执行标签名为dir的的任务,即使不指定always标签,它的任务也同样会执行

执行顺序默认时从上往下执行,即使你指定标签,如果always标签在指定标签的上方,那么它就会先执行always标签的任务

当指定never标签,它的任务才会被执行

(五)play级标签

除了在任务级别,还可以在Play级别定义标签,这样该Play下的所有任务(除非被特定任务的标签覆盖)都将继承该Play的标签

指定需要执行的play任务

只会执行指定的play任务,下面的任务并不会被执行

三、roles模块

在Ansible中,roles 是一种强大的组织和复用配置管理代码的方法,它从 Ansible 版本1.2 开始被引入。角色设计的目的是为了实现配置的层次化和结构化,使得 Playbooks 更加清晰、易于管理和重复利用

(一)角色的优势

  • 模块化: 角色使得复杂的配置任务得以分解,每个角色负责一部分功能,便于管理和维护。
  • 重用性: 可以在多个项目或不同环境中重用相同的角色。
  • 团队协作: 团队成员可以独立开发和测试各自负责的角色,然后集成到更大的自动化流程中。
  • 清晰性: Playbooks 变得更简洁,因为具体的配置细节被封装在角色中

(二)基本构成

ansible的角色目录,yum安装默认存放在/etc/ansible/目录下

一个典型的 Ansible 角色目录结构可能包含以下几个部分

files: 用来存放由 copy 模块或 script 模块调用的文件。

templates:存放Jinja2模板文件,用于动态生成配置文件。

tasks: (tasks/main.yml) :此目录应当包含一个 main.yml 文件。包含了该角色需要执行的任务列表。

handlers: (handlers/main.yml):此目录应当包含一个 main.yml 文件 。定义了在特定条件触发时执行的操作,如服务重启。

vars: (vars/main.yml): 此目录应当包含一个 main.yml 文件 。定义角色内部使用的变量。

defaults: (defaults/main.yml):此目录应当包含一个 main.yml 文件 。提供角色的默认变量值,这些值可以被覆盖。

meta: (meta/main.yml):此目录应当包含一个 main.yml 文件 。包含角色的元数据,比如角色的依赖关系。

(三)模块使用

1.定义主机组

[root@ansible data]#vim /etc/ansible/hosts
[root@ansible data]#sed -n '32,35p' /etc/ansible/hosts
[web]
192.168.83.80
[webserver]
192.168.83.90

2.创建目录组件

[root@ansible data]#mkdir -p /etc/ansible/roles/nginx/{files,templates,tasks,handlers,vars,defaults,meta}
[root@ansible data]#ll /etc/ansible/roles/
总用量 0
drwxr-xr-x 9 root root 105 5月   8 21:29 nginx
[root@ansible data]#ll /etc/ansible/roles/nginx/
总用量 0
drwxr-xr-x 2 root root 6 5月   8 21:29 defaults
drwxr-xr-x 2 root root 6 5月   8 21:29 files
drwxr-xr-x 2 root root 6 5月   8 21:29 handlers
drwxr-xr-x 2 root root 6 5月   8 21:29 meta
drwxr-xr-x 2 root root 6 5月   8 21:29 tasks
drwxr-xr-x 2 root root 6 5月   8 21:29 templates
drwxr-xr-x 2 root root 6 5月   8 21:29 vars

3.编写各类文件

3.1 定义变量

定义变量,需要在vars目录下建立mian.yml文件

[root@ansible data]#vim /etc/ansible/roles/nginx/vars/main.yml
[root@ansible data]#cat /etc/ansible/roles/nginx/vars/main.yml
epel: epel-release.noarch
package: nginx
3.2 制定任务

制定任务需要在tasks目录下建立main.yml文件

[root@ansible data]#vim /etc/ansible/roles/nginx/tasks/main.yml
[root@ansible data]#cat /etc/ansible/roles/nginx/tasks/main.yml
- name: install epel
  yum: name={{epel}}      #调用vars中定义的变量,下载epel源
- name: install nginx
  yum: name={{package}} state=latest   #下载nginx服务
- name: start nginx
  service: enabled=true name={{package}} state=started  #启动nginx服务

3.3 调用角色模块

在/etc/ansible/目录下,编写yml文件

[root@ansible data]#vim /etc/ansible/nginx.yml
[root@ansible data]#cat /etc/ansible/nginx.yml
---
- hosts: web         #指定主机组
  remote_user: root  #指定远程连接用户
  roles:             #使用roles模块
    - nginx          #使用模块目录名,为nginx

4.执行模块

访问测试

(四)多角色搭建服务

1.创建目录

[root@ansible data]#mkdir -p /etc/ansible/roles/{mysql,php}/{files,templates,tasks,handlers,vars,defaults,meta}
[root@ansible data]#ls /etc/ansible/roles/mysql/
defaults  files  handlers  meta  tasks  templates  vars
[root@ansible data]#ls /etc/ansible/roles/php/
defaults  files  handlers  meta  tasks  templates  vars
[root@ansible data]#ls /etc/ansible/roles/nginx/
defaults  files  handlers  meta  tasks  templates  vars

2.编写模块文件

2.1 编写mysql模块文件

2.1.1 定义变量

[root@ansible data]#cat /etc/ansible/roles/mysql/vars/main.yml
package:
  - mariadb
  - mariadb-server
server: mariadb

2.1.2 制定任务

[root@ansible data]#cat /etc/ansible/roles/mysql/tasks/main.yml
- name: install mariadb
  yum: name={{package}} state=latest
- name: start mariadb
  service: enabled=true name={{server}} state=started

2.2 编写php模块

2.1.1 定义变量

[root@ansible data]#vim /etc/ansible/roles/php/vars/main.yml
[root@ansible data]#cat /etc/ansible/roles/php/vars/main.yml
package:
  - php
  - php-fpm
server: php-fpm

2.1.2 制定任务

[root@ansible data]#vim /etc/ansible/roles/php/tasks/main.yml
[root@ansible data]#cat /etc/ansible/roles/php/tasks/main.yml
- name: install php
  yum: name={{package}} state=latest
- name: start php-fpm
  service: enabled=true name={{server}} state=started

3.调用角色模块

[root@ansible data]#vim  /etc/ansible/nmp.yml 
[root@ansible data]#cat  /etc/ansible/nmp.yml 
---
- hosts: webserver
  remote_user: root
  roles:
    - nginx     #按顺序,依次执行,先执行nginx角色模块
    - mysql
    - php

4.执行模块

远端主机查看是否安装

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1654087.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自动驾驶|毫米波雷达】逻辑化讲清快时间与慢时间傅里叶变换

碎碎念&#xff1a;实习过程中发现在进行雷达知识交流时&#xff0c;大部分同事都会用英文简称代替中文的一些称呼&#xff0c;比如Chirp、FFT等等。起初我觉得是因为很多英伟达、TI芯片的开发教程都是英文的&#xff0c;所以看得多了大家都习惯这样称呼&#xff0c;后来在和指…

有没有不使用技术分析工具的?众汇10年交易经验发现还真有

不知道各位投资者有没有遇见过不使用技术分析工具的投资者&#xff0c;众汇用自己的10年外汇交易经验保证&#xff0c;别不信还真有&#xff0c;并且不在少数。 其实道理很简单&#xff0c;这些投资者不相信技术分析工具的效率!在这些投资者看来技术分析工具通常比较滞后、需要…

微信小程序开发-数据事件绑定

&#x1f433;简介 数据绑定 数据绑定是一种将小程序中的数据与页面元素关联起来的技术&#xff0c;使得当数据变化时&#xff0c;页面元素能够自动更新。这通常使用特定的语法&#xff08;如双大括号 {{ }}&#xff09;来实现&#xff0c;以便在页面上展示动态数据。 事件绑…

C++类和对象(基础篇)

前言&#xff1a; 其实任何东西&#xff0c;只要你想学&#xff0c;没人能挡得住你&#xff0c;而且其实学的也很快。那么本篇开始学习类和对象&#xff08;C的&#xff0c;由于作者有Java基础&#xff0c;可能有些东西过得很快&#xff09;。 struct在C中的含义&#xff1a; …

【Keil程序大小】Keil编译结果Code-RO-RW-ZI分析

【Keil程序大小】Keil编译结果Code-RO-RW-ZI分析 下图为keil编译后的结果&#xff1a; 单位为Byte。Code是程序大小。RO是常量大小。RW是读写变量占用大小&#xff0c;如已初始化的静态变量和全局变量。ZI是全零变量占用大小&#xff0c;如未初始化的static修饰的静态变量、全局…

ENG-2 AM,129423-53-6主要用于检测生物体系中的Na+浓度

引言&#xff1a;在化学研究的海洋中&#xff0c;优质的化学试剂是实验成功的关键。今天&#xff0c;我要为大家分享一款备受好评的化学试剂——ENG-2。这款试剂以其独特的性能和广泛的应用领域&#xff0c;赢得了众多科研人员的青睐。 中文名称&#xff1a;钠离子荧光探针ENG-…

Linux进程——Linux进程间切换与命令行参数

前言&#xff1a;在上一篇了解完进程状态后&#xff0c;我们简单了解了进程优先级&#xff0c;然后遗留了一点内容&#xff0c;本篇我们就来研究进程间的切换&#xff0c;来理解上篇提到的并发。如果对进程优先级还有没理解的地方可以先阅读&#xff1a; Linux进程优先级 本篇…

无意的一次学习,竟让我摆脱了Android控制?

由于鸿蒙的爆火&#xff0c;为了赶上时代先锋。到目前为止也研究过很长一段时间。作为一名Android的研发人员&#xff0c;免不了对其评头论足&#xff0c;指导文档如何写才算专业&#xff1f;页面如何绘制&#xff1f;页面如何跳转&#xff1f;有没有四大组件等等。 而Harmony…

【数字经济】上市公司供应链数字化数据(2000-2022)

数据来源&#xff1a; 时间跨度&#xff1a;2000-2022年 数据范围&#xff1a;各上市企业 数据指标&#xff1a; 样例数据&#xff1a; 参考文献&#xff1a;[1]刘海建,胡化广,张树山,等.供应链数字化的绿色创新效应[J].财经研究,2023,49(03):4-18. 下载链接&#xff1a;https:…

neo4j-5.11.0安装APOC插件or配置允许使用过程的权限

在已经安装好neo4j和jdk的情况下安装apoc组件&#xff0c;之前使用neo4j-community-4.4.30&#xff0c;可以找到配置apoc-4.4.0.22-all.jar&#xff0c;但是高版本neo4j对应没有apoc-X.X.X-all.jar。解决如下所示&#xff1a; 1.安装好JDK与neo4j 已经安装对应版本的JDK 17.0…

Java快速入门系列-11(项目实战与最佳实践)

第十一章&#xff1a;项目实战与最佳实践 11.1 项目规划与需求分析项目规划需求分析实例代码 11.2 系统设计考虑实例代码 11.3 代码实现与重构实例代码 11.4 性能优化与监控实例代码 11.5 部署与持续集成/持续部署(CI/CD)实例代码 11.1 项目规划与需求分析 在进行任何软件开发…

Google Earth Engine谷歌地球引擎计算遥感影像在每个8天间隔内的多年平均值

本文介绍在谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;中&#xff0c;求取多年时间中&#xff0c;遥感影像在每1个8天时间间隔内的多年平均值的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;系列教学文章…

全双工音频对讲模块-支持空中升级、多级无线中继

SA618F30是一款高集成的大功率全双工无线音频模块&#xff0c;发射功率高达32dBm。该音频模块简化接口&#xff0c;只需外接音频功放或麦克风即可作为一个小型对讲机&#xff0c;方便快捷嵌入到各类手持设备中。支持多级无线中继&#xff0c;支持OTA空中升级。 SA618F30配备1W…

【前端】实现表格简单操作

简言 表格合并基础篇 本篇是在上一章的基础上实现&#xff0c;实现了的功能有添加行、删除行、逆向选区、取消合并功能。 功能实现 添加行 添加行分为在上面添加和在下面追加行。 利用 insertAdjacentElement 方法实现&#xff0c;该方法可以实现从前插入元素和从后插入元…

嘉楠堪智 CanMV K230 进行 Linux、RT-smart 系统开发

本文记录学习、使用 K230 SDK 进行 Linux、RT-smart 系统的开发的一些关键步骤&#xff0c;如何获取系统源代码&#xff0c;如何配置环境&#xff0c;如何使用 Docker 进行编译&#xff0c;获得系统文件。 具体详细的教程&#xff0c;可以学习 CanMV K230 教程。 目录 一、S…

引入 Redis

简介 Jedis Jedis 是早期的 Redis 的 Java 实现客户端&#xff0c;提供了比较全面的 Redis 命令的支持&#xff0c;其官方网址是&#xff1a;http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html 优点&#xff1a;支持全面的 Redis 操作特性&#xff0…

搜狗输入法 PC端 v14.4.0.9307 去广告绿化版.

软件介绍 搜狗拼音输入法作为众多用户计算机配置的必备工具&#xff0c;其功能的全面性已为众所周知&#xff0c;并且以其高效便捷的输入体验受到广大使用者的青睐。然而&#xff0c;该软件在提供便利的同时&#xff0c;其内置的广告元素常常为用户带来一定的干扰。为此&#…

代码随想录算法训练营DAY48|C++动态规划Part9|121.买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

文章目录 121.买卖股票的最佳时机思路CPP代码 122.买卖股票的最佳时机II思路CPP代码 123.买卖股票的最佳时机III思路CPP代码 121.买卖股票的最佳时机 力扣题目链接 文章讲解&#xff1a;121.买卖股票的最佳时机 视频讲解&#xff1a;动态规划之 LeetCode&#xff1a;121.买卖股…

由树形解空间入手,深入分析回溯、动态规划、分治算法的共同点和不同点

一、回溯、动态规划、分治 其实这三个算法就可以直接认为是一类算法&#xff0c;它们的共同点都是会涉及到递归。 更需要清楚明白的一点是&#xff0c;它们的解&#xff0c;都是基于一颗递归形式的树上得来的&#xff01;也就是——回溯、动态规划、分治的解空间是 一棵树&am…

ssm+vue的私人健身和教练预约管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的私人健身和教练预约管理系统(有报告)。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通…