ansible(一)

2020-06-18 2 条评论 460 次阅读 0 人点赞

ansible

ansible是一个具有幂等性的“配置管理工具”,通过playbook能够批量对多台主机实现自动化运维

ansible管理清单

同时支持ini和yaml格式的配置形式

  • ansible_host=

  • ansible_port=

  • ansible_user=

  • ansible_ssh_pass=

ansible模块:

1.fetch模块:Fetches a file from remote nodes,从受管主机中拉取文件

  • src

  • dest

  • validate_checksum #文件拉取完成后校验一致性

2.copy模块:将文件或目录从本地复制到远程(受管)主机

  • src

  • dest

  • content #当不使用src指定拷贝的文件时,可以使用content直接指定文件内容,src与content两个参数必有其一,否则会报错

  • force #当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否强制覆盖

  • backup #当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否对远程主机的文件进行备份

  • owner #指定文件拷贝到远程主机后的属主,但是远程主机上必须有对应的用户,否则会报错

  • group #指定文件拷贝到远程主机后的属组,但是远程主机上必须有对应的组,否则会报错。

  • mode #指定文件拷贝到远程主机后的权限,如果你想将权限设置为"rw-r--r--",则可以使用mode=0644表示,如果你想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示

3.file模块:帮助我们完成对文件的一些基本操作,例如创建、删除、修改权限等

  • path #指定文件或目录的路径

  • state #此参数对应的值根据情况而定,标记path指定的是目录而不是文件:directory;标记path指定的是文件而不是文件目录:touch;标记path指定的file文件类型是软硬链接:link、hard;标记对path指定的文件进行删除(删除时不需要区分文件类型):absent

  • src #当state设置为link或者hard时,表示想要创建一个软链或者硬链,所以须指明软链或硬链链接的哪个文件,通过src参数即可指定链接源

  • force #强制执行,即使链接文件的源文件不存在也会强制创建出指定的链接文件,且如果有同名的其他类型文件存在,强制覆盖

  • owner #用于指定被操作文件的属主,属主对应的用户必须在远程主机中存在,否则会报错

  • group #用于指定被操作文件的属组,属组对应的组必须在远程主机中存在,否则会报错

  • mode #用于指定被操作文件的权限,比如,如果想要将文件权限设置为"rw-r-x---",则可以使用mode=650进行设置,或者使用mode=0650,效果也是相同的,如果你想要设置特殊权限,比如为二进制文件设置suid,则可以使用mode=4700

  • recurse #当要操作的文件为目录,将recurse设置为yes,可以递归的修改目录中文件的属性

4.blockinfile模块:帮助我们在指定的文件中插入"一段文本",这段文本是被标记过的,换句话说就是,我们在这段文本上做了记号,以便在以后的操作中可以通过"标记"找到这段文本,然后修改或者删除它

  • path #必须参数,指定要修改的文件

  • block #指定要操作的那段文本,别名content,作用一样

  • mark #在指定文件中插入一段文本,ansible会自动为这段文本添加两个标记,一个开始标记,一个结束标记,默认情况下,开始标记为# BEGIN ANSIBLE MANAGED BLOCK,结束标记为# END ANSIBLE MANAGED BLOCK,使用marker参数自定义"标记"的内容,如:mask=#{mask}info,这样标记就成了:# BEGIN info 和 # END info

  • stat #对block的操作设置,present表示对block段的插入和更新,absent表示将block段从文件中删除

  • insertafter #block默认会在文件的末尾插入文本,如果你想要将文本插入在某一行的后面,可以使用此参数指定对应的行,也可以使用正则表达式(python正则),表示将文本插入在符合正则表达式的行的后面,如果有多行文本都能够匹配对应的正则表达式,则以最后一个满足正则的行为准,此参数的值还可以设置为EOF,表示将文本插入到文档末尾。

  • insertbefore #类似insertafter,表示将block插入到指定位置的前面,正则表达式的匹配使用同inserafter,设置为BOF,表示将block插入到文档开头

  • backup #在修改之前先备份

  • create #当path指定的文件不存在时,是否创建对应文件

5.lineinfile模块:可以借助lineinfile模块,确保"某一行文本"存在于指定的文件中,或者确保从文件中删除指定的"文本"(即确保指定的文本不存在于文件中),还可以根据正则表达式,替换"某一行文本"。

  • path #必须参数,指定要操作的文件

  • line #指定文本内容

  • regexp #使用正则表达式匹配对应的行,当替换文本时,如果有多行文本都能被匹配,则只有最后面被匹配到的那行文本才会被替换,当删除文本时,如果有多行文本都能被匹配,这么这些行都会被删除。

  • state #state参数的值设置为absent,表示删除,默认值为present

  • backrefs #开启正则表达式的向后引用功能,另外一个作用:默认情况下,当使用正则表达式替换对应行时,如果正则没有匹配到任何的行,那么line对应的内容会被插入到文本的末尾,不过,如果使用了backrefs=yes,情况就不一样了,当正则没有匹配到任何的行时,则不会对文件进行任何操作,相当于保持原文件不变

  • insertafter

  • insertbefore

  • backup

  • create

6.find模块:可以帮助我们在远程主机中查找符合条件的文件,就像find命令一样

  • path #必须参数,指定在哪个目录中查找文件,可以指定多个路径,路径间用逗号隔开,此参数有别名,使用别名path或者别名name可以代替paths

  • recurse #默认情况下,只会在指定的目录中查找文件,并不会递归的进入子目录查找对应文件,如果想要递归的查找文件,recurse参数设置为yes

  • hidden #默认情况下,隐藏文件会被忽略,当hidden参数的值设置为yes时,才会查找隐藏文件

  • file_type #默认情况下,ansible只会根据条件查找"文件",并不会查找"目录"或"软链接"等文件类型,如果想要指定查找的文件类型,可以通过file_type指定文件类型,可指定的文件类型有any、directory、file、link 四种

  • patterns #使用此参数指定需要查找的文件名称,支持使用shell(比如通配符)或者正则表达式去匹配文件名称,默认情况下,使用shell匹配对应的文件名,如果想要使用python的正则去匹配文件名,需要将use_regex参数的值设置为yes

  • use_regex #find模块默认使用glob通配符解析patterns参数中的表达式,当use_regex设置为yes时,表示使用python正则解析patterns参数中的表达式

  • contains #使用此参数可以根据文章内容查找文件,此参数的值为一个正则表达式,find模块会根据对应的正则表达式匹配文件内容

  • age #使用此参数可以根据时间范围查找文件,默认以文件的mtime为准与指定的时间进行对比,比如,如果想要查找mtime在3天之前的文件,那么可以设置age=3d,如果想要查找mtime在3天以内的文件,可以设置age=-3d,这里所说的3天是按照当前时间往前推3天,可以使用的单位有秒(s)、分(m)、时(h)、天(d)、星期(w)

  • age_stamp #文件的时间属性中有三个时间种类,atime、ctime、mtime,当我们根据时间范围查找文件时,可以指定以哪个时间种类为准,当根据时间查找文件时,默认以mtime为准

  • size #根据文件大小查找文件,比如,如果想要查找大于3M的文件,设置size=3m,如果想要查找小于50k的文件,可以设置size=-50k,可以使用的单位有t、g、m、k、b。

  • get_checksum #当有符合查找条件的文件被找到时,会同时返回对应文件的sha1校验码,如果要查找的文件比较大,那么生成校验码的时间会比较长。

7.replace模块:可以根据我们指定的正则表达式替换文件中的字符串,文件中所有被正则匹配到的字符串都会被替换

  • path #必须参数,指定要操作的文件,2.3版本之前,只能使用dest, destfile, name指定要操作的文件,2.4版本中,仍然可以使用这些参数名,这些参数名作为path参数的别名使用

  • regexp #必须参数,指定一个python正则表达式,文件中与正则匹配的字符串将会被替换

  • replace #指定最终要替换成的字符串

  • backup #是否在修改文件之前对文件进行备份,最好设置为yes

8.command模块:帮助我们在远程主机上执行命令

注意:使用command模块在远程主机中执行命令时,不会经过远程主机的shell处理,在使用command模块时,如果需要执行的命令中含有重定向、管道符等操作时,这些符号也会失效,比如"<", ">", "|", ";" 和 "&" 这些符号,如果你需要这些功能,可以参考后面介绍的shell模块,还有一点需要注意,如果远程节点是windows操作系统,则需要使用win_command模块

  • free_form #必须参数,指定需要远程执行的命令,free_form参数与其他参数并不相同,在之前的模块示例中,如果想要使用一个参数,那么则需要为这个参数赋值,但是free_form参数则不同,"free_form"并不是一个"实际存在"的参数名,而是所有具体命令的代称,比如,当我们想要在远程主机上执行ls命令时,我们并不需要写成"free_form=ls" ,直接写成ls即可,这就是free_form参数的含义,因为command模块的作用是执行命令,所以,任何一个可以在远程主机上执行的命令都可以被称为free_form

  • chdir #此参数的作用就是指定一个目录,在执行对应的命令之前,会先进入到chdir参数指定的目录中

  • creates #看到creates,你可能会从字面上理解这个参数,但是使用这个参数并不会帮助我们创建文件,它的作用是当指定的文件存在时,就不执行对应命令,比如,如果/testdir/test文件存在,就不执行我们指定的命令。

  • removes #与creates参数的作用正好相反,它的作用是当指定的文件不存在时,就不执行对应命令,比如,如果/testdir/tests文件不存在,就不执行我们指定的命令,此参数并不会帮助我们删除文件

9.shell模块:可以帮助我们在远程主机上执行命令,与command模块不同的是,shell模块在远程主机中执行命令时,会经过远程主机上的/bin/sh程序处理

  • free_form #必须参数,指定需要远程执行的命令,参见command

  • chdir #此参数的作用就是指定一个目录,在执行对应的命令之前,会先进入到chdir参数指定的目录中

  • creates #使用此参数指定一个文件,当指定的文件存在时,就不执行对应命令,参考command模块中的解释

  • removes #使用此参数指定一个文件,当指定的文件不存在时,就不执行对应命令,参考command模块中的解释

  • executable #默认情况下,shell模块会调用远程主机中的/bin/bash去执行对应的命令,如果你想要使用其他类型的shell执行命令,则可以使用此参数指定某种类型的shell去执行对应的命令,指定shell文件时,需要使用绝对路径

10.script模块:可以帮助我们在远程主机上执行ansible主机上的脚本,也就是说,脚本一直存在于ansible主机本地,不需要手动拷贝到远程主机后再执行

  • free_form #必须参数,指定需要执行的脚本,脚本位于ansible主机本地,并没有具体的一个参数名叫free_form,具体解释参考command模块

  • chdir #此参数的作用就是指定一个远程主机中的目录,在执行对应的脚本之前,会先进入到chdir参数指定的目录中

  • creates #使用此参数指定一个远程主机中的文件,当指定的文件存在时,就不执行对应脚本,可参考command模块中的解释

  • removes #使用此参数指定一个远程主机中的文件,当指定的文件不存在时,就不执行对应脚本,可参考command模块中的解释

11.cron模块:可以帮助我们管理远程主机中的计划任务,功能相当于crontab命令

  • minute #此参数用于设置计划任务中分钟设定位的值,不使用此参数时,设定位值默认为"*"

  • hour #此参数用于设置计划任务中小时设定位的值,不使用此参数时,设定位值默认为"*"

  • day #此参数用于设置计划任务中日设定位的值,不使用此参数时,设定位值默认为"*"

  • month #此参数用于设置计划任务中月设定位的值,不使用此参数时,设定位的值默认为"*"

  • weekday #此参数用于设置计划任务中周几设定位的值,当不使用此参数时,周几设定位的值默认为"*"

  • special_time #计划任务的时间可以设定为@reboot或者@hourly,@reboot表示重启时执行,@hourly表示每小时执行一次,相当于设置成"0 * * * *" ,这种@开头的时间设定格式则需要使用special_time参数进行设置,special_time参数的可用值有reboot(重启后)、yearly(每年)、annually(每年,与yearly相同)、monthly(每月)、weekly(每周)、daily(每天)、hourly(每时)。

  • user #此参数用于设置当前计划任务属于哪个用户,当不使用此参数时,默认为管理员用户

  • job #此参数用于指定计划的任务中需要实际执行的命令或者脚本,比如上例中的"echo test"命令

  • name #此参数用于设置计划任务的名称,计划任务的名称会在注释中显示,当不指定计划任务的名称时,ansible会默认为计划任务加入注释,注释的内容为#Ansible: None,假设指定计划任务的名称为test,那么注释的内容为#Ansible: test,在一台机器中,计划任务的名称应该具有唯一性,方便我们以后根据名称修改或删除计划任务

  • state #当计划任务有名称时,我们可以根据名称修改或删除对应的任务,当删除计划任务时,需要将state的值设置为absent

  • disabled #当计划任务有名称时,我们可以根据名称使对应的任务"失效"(注释掉对应的任务),注意,使用此参数时,除了需要指定任务的名称,还需要同时指定任务的job以及任务的时间设定,而且任务的时间设定必须和对应任务完全相同,否则在注释任务的同时,任务的时间设定会被修改

  • backup #如果此参数的值设置为yes,那么当修改或者删除对应的计划任务时,会先对计划任务进行备份,然后再对计划任务进行修改或者删除,cron模块会在远程主机的/tmp目录下创建备份文件,以crontab开头并且随机加入一些字符,具体的备份文件名称会在返回信息的backup_file字段中看到,推荐将此此参数设置为yes

12.service模块:可以帮助我们管理远程主机上的服务,比如,启动或停止远程主机中的nginx服务

  • name #此参数用于指定需要操作的服务名称,比如nginx

  • state #此参数用于指定服务的状态,比如,我们想要启动远程主机中的nginx,则可以将state的值设置为started,如果想要停止远程主机中的服务,则可以将state的值设置为stopped,此参数的可用值有started、stopped、restarted、reloaded

  • enabled #此参数用于指定是否将服务设置为开机 启动项,设置为yes表示将对应服务设置为开机启动,设置为no表示不会开机启动

12.user模块:可以帮助我们管理远程主机上的用户,比如创建用户、修改用户、删除用户、为用户创建密钥对等操作

  • name #必须参数,用于指定要操作的用户名称,可以使用别名user

  • group #此参数用于指定用户所在的基本组

  • gourps #此参数用于指定用户所在的附加组,注意,如果说用户已经存在并且已经拥有多个附加组,那么如果想要继续添加新的附加组,需要结合append参数使用,否则在默认情况下,当再次使用groups参数设置附加组时,用户原来的附加组会被覆盖

  • append #如果用户原本就存在多个附加组,那么当使用groups参数设置附加组时,当前设置会覆盖原来的附加组设置,如果不想覆盖原来的附加组设置,需要结合append参数,将append设置为yes,表示追加附加组到现有的附加组设置,append默认值为no

  • shell #此参数用于指定用户的默认shell

  • expires #此参数用于指定用户的过期时间,相当于设置/etc/shadow文件中的的第8列,比如,你想要设置用户的过期日期为2018年12月31日,那么你首先要获取到2018年12月31日的unix时间戳,使用命令"date -d 2018-12-31 +%s"获取到的时间戳为1546185600,所以,当设置expires=1546185600时,表示用户的过期时间为2018年12月31日0点0分,设置成功后,查看远程主机的/etc/shadow文件,对应用户的第八列的值将变成17895(表示1970年1月1日到2018年12月31日的天数,unix时间戳的值会自动转换为天数,我们不用手动的进行换算),目前此参数只支持在Linux和FreeBSD系统中使用

  • comment #此参数用于指定用户的注释信息

  • state #此参数用于指定用户是否存在于远程主机中,可选值有present、absent,默认值为present,表示用户需要存在,当设置为absent时表示删除用户

  • remove #当state的值设置为absent时,表示要删除远程主机中的用户,但是在删除用户时,不会删除用户的家目录等信息,这是因为remoove参数的默认值为no,如果设置为yes,在删除用户的同时,会删除用户的家目录,当state=absent并且remove=yes时,相当于执行"userdel --remove"命令

  • password #此参数用于指定用户的密码,但是这个密码不能是明文的密码,而是一个对明文密码"加密后"的字符串,相当于/etc/shadow文件中的密码字段,是一个对明文密码进行哈希后的字符串,你可以在python的命令提示符下输入如下命令,生成明文密码对应的加密字符串。

    import crypt; crypt.crypt('666666')

  • update_password #此参数有两个值可选,always和on_create,当此参数的值设置为always时表示,如果password参数设置的值与用户当前的加密过的密码字符串不一致,则直接更新用户的密码,默认值即为always,但是当此参数设置为on_create时,如果password参数设置的值与用户当前的加密过的密码字符串不一致,则不会更新用户的密码字符串,保持之前的密码设定,如果是新创建的用户,即使此参数设置为on_create,也会将用户的密码设置为password参数对应的值

  • generate_ssh_key #此参数默认值为no,如果设置为yes,表示为对应的用户生成ssh密钥对,默认在用户家目录的./ssh目录中生成名为id_rsa的私钥和名为id_rsa.pub的公钥,如果同名的密钥已经存在与对应的目录中,原同名密钥并不会被覆盖(不做任何操作)

  • ssh_key_file #当generate_ssh_key参数的值为yes时,使用此参数自定义生成ssh私钥的路径和名称,对应公钥会在同路径下生成,公钥名以私钥名开头,以".pub"结尾。

  • ssh_key_comment #当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置公钥中的注释信息,但是如果同名的密钥对已经存在,则并不会修改原来的注释信息,即不做任何操作,当不指定此参数时,默认的注释信息为"ansible-generated on 远程主机的主机名"

  • ssh_key_passphrase #当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置私钥的密码,但是如果同名的密钥对已经存在,则并不会修改原来的密码,即不做任何操作

  • ssh_key_type #当generate_ssh_key参数的值为yes时,在创建证书时,使用此参数设置密钥对的类型,默认密钥类型为rsa,但是如果同名的密钥对已经存在,并不会对同名密钥做任何操作

13.group模块:可以帮助我们管理远程主机上的组

  • name #必须参数,用于指定要操作的组名称

  • state #用于指定组的状态,两个值可选,present,absent,默认为present,设置为absent表示删除组

  • gid #用于指定组的gid

14.yum_repository模块:可以帮助我们管理远程主机上的yum仓库

  • name参数 #用于指定要操作的唯一的仓库ID,也就是".repo"配置文件中每个仓库对应的"中括号"内的仓库ID

  • baseurl #此参数用于设置yum仓库的baseurl

  • description #此参数用于设置仓库的注释信息,也就是".repo"配置文件中每个仓库对应的"name字段"对应的内容

  • file #此参数用于设置仓库的配置文件名称,即设置".repo"配置文件的文件名前缀,在不使用此参数的情况下,默认以name参数的仓库ID作为".repo"配置文件的文件名前缀,同一个'.repo'配置文件中可以存在多个yum源

  • enabled #此参数用于设置是否激活对应的yum源,此参数默认值为yes,表示启用对应的yum源,设置为no表示不启用对应的yum源

  • gpgcheck #此参数用于设置是否开启rpm包验证功能,默认值为no,表示不启用包验证,设置为yes表示开启包验证功能

  • gpgcakey #当gpgcheck参数设置为yes时,需要使用此参数指定验证包所需的公钥

  • state #默认值为present,当值设置为absent时,表示删除对应的yum源

16.yum模块:可以帮助我们在远程主机上通过yum源管理软件包

  • name #必须参数,用于指定需要管理的软件包,比如nginx

  • state #用于指定软件包的状态 ,默认值为present,表示确保软件包已经安装,除了present,其他可用值有installed、latest、absent、removed,其中installed与present等效,latest表示安装yum中最新的版本,absent和removed等效,表示删除对应的软件包

  • disable_gpg_check #用于禁用对rpm包的公钥gpg验证,默认值为no,表示不禁用验证,设置为yes表示禁用验证,即不验证包,直接安装,在对应的yum源没有开启gpg验证的情况下,需要将此参数的值设置为yes,否则会报错而无法进行安装。

  • enablerepo #用于指定安装软件包时临时启用的yum源,假如你想要从A源中安装软件,但是你不确定A源是否启用了,你可以在安装软件包时将此参数的值设置为yes,即使A源的设置是未启用,也可以在安装软件包时临时启用A源

  • disablerepo #用于指定安装软件包时临时禁用的yum源,某些场景下需要此参数,比如,当多个yum源中同时存在要安装的软件包时,你可以使用此参数临时禁用某个源,这样设置后,在安装软件包时则不会从对应的源中选择安装包

playbook

1.handlers

handlers是另一种'任务列表',handlers中的任务会被tasks中的任务进行"调用",但是,被"调用"并不意味着一定会执行,只有当tasks中的任务"真正执行"以后(真正的进行实际操作,造成了实际的改变),handlers中被调用的任务才会执行,如果tasks中的任务并没有做出任何实际的操作,那么handlers中的任务即使被'调用',也并不会执行

  • name: #命名被notify的name

  • listen: #填写handler组名,加入要被一并notify的handler组

2.meta: flush_handlers #立即执行notify的handler

3.tags

tags可以帮助我们对任务进行'打标签'的操作,当任务存在标签以后,我们就可以在执行playbook时,借助标签,指定执行哪些任务,或者指定不执行哪些任务

  • always #包含always关键字的tags,在执行playbook时如果没有使用“--skip-tags always”选项,则默认会执行所有带有always关键字的tags

  • never #ansible2.5中引入了never关键字tags,就是默认情况下不执行该task的意思,除非运行playbook时点名要演never这个tags的戏

  • tagged、untagged、all #这三个关键字是在运行playbook调用tags时使用的,分别代表所有打过标签的tesk,所有未打标签的任务,以及所有任务,如“--tags untagged”、“--skip-tags tagged”

4、变量

  1. 先使用“vars”定义变量
  2. 使用“{{VARS}}”双大括号引用前面定义的变量
  3. playbook支持以“属性”的方式定义变量,如:
- hosts: test1
  remote_user: root
  vars:
    nginx:
      port1: 80
      port2: 8080
      port3: 10080
  tasks:
    - name: nginx port set
      lineinfile:
        path: "{{OLD_VARS}}"
        regexp: listen(.*)80(.*)
        backrefs: yes
        line: "listen\1{{nginx.port2}}\2"
        backup: yes
      notify:
        HANDLERS

ps: 上面的引用中,tasks中path那行,变量的引用位于语句的起始位置,且是用“:”在给path赋值,所以必须带双引号,否则会报语法错误,line那行中的引用则不用,因为没有处于语句的起始位置

除了能够在playbook中直接定义变量,还可以在某个文件中专门存放变量,然后再在playbook中引入对应的文件,引入文件后,playbook即可使用文件中定义的变量,以此来做到"变量、文件分离"

  • 文件中定义变量时,不要使用vars关键字,直接定义变量即可
  • 在playbook中引入包含变量的文件时,需要使用"vars_files"关键字,被引入的文件需要以"- "开头,以YAML中块序列的语法引入

5.setup模块

当运行一个playbook时,默认都会运行一个名为"[Gathering Facts]"的任务,ansible通过"[Gathering Facts]"这个默认任务收集远程主机的相关信息(例如远程主机的IP地址,主机名,系统版本,硬件配置等信息),这些被收集到的远程主机信息会保存在对应的变量中,当我们想要使用这些信息时,我们可以获取对应的变量,从而使用这些信息

如果想要查看"[Gathering Facts]"任务收集的信息内容,我们可以借助setup模块

当执行playbook时,playbook其实就是自动调用了setup模块从而执行了"[Gathering Facts]"任务,所以我们可以通过ad-hoc手动执行setup模块查看"[Gathering Facts]"任务收集到的信息

6.debug模块:debug模块可以帮助我们把信息输出到ansible控制台上,以便我们能够定位问题

  • msg: #类似print,打印设定的信息到控制台

  • var: VAR #输出变量的值

7.vars_prompt模块:类似shell里的read,输入信息赋值给变量

  • name: #将要赋值的变量

  • prompt: #提示信息

  • private: #输入信息时是否可见

  • default: #不输入任何值时的默认值

  • encrypt: #对输入内容加密后赋值给变量,依赖passlib库

  • confirm: #实现类似确认密码输入两遍的功能

8.ad-hoc命令行模式常用选项

  • --extra-vars:传递变量给playbook

  • -e:等同于“--extra-vars”,除了传递变量,还可以传递变量文件,如-e "@/PAHT/TO/FILE"

  • --syntax-check:检查playbook语法格式

  • --check:dry-run的意思

  • -c: “--check”的简写形式

once

这个人太懒什么东西都没留下

文章评论(2)

  • erotik izle

    Vale la pena leggere il tuo articolo.

    2020-07-25
  • Doreen Gard Gasperoni

    Yeah. that’s what I was exploring for.. thanks. Doreen Gard Gasperoni

    2020-08-01