* 学习环境 CentOS Linux release 7.8.2003 (Core)
编译安装Python3.8.5
1、下载Python-3.8.5源码包
下载地址1(巨慢):https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tar.xz
下载地址2(推荐):https://pan.baidu.com/s/1OKeCPr3yD5fbpbILscjtGQ 提取码: 7b3w
2、更换阿里源、安装常用依赖(避免关键时刻命令不存在, 最好按顺序执行~)
yum install gcc patch libffi-devel python-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel netstat lrzsz unzip zip sysstat ntpdate net-tools vim wget -y
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo_bak
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum makecache fast
yum install epel-release -y
yum repolist
3、按顺序执行命令
# 切换到目录'/usr/local'
cd /usr/local
# 上传源码包'Python-3.8.5.tar.xz'到目录'/usr/local'下
rz
# 解压'Python-3.8.5.tar.xz'
tar -xvf Python-3.8.5.tar.xz
# 切换到目录'Python-3.8.5'
cd Python-3.8.5
# 配置、编译、安装(有点漫长, 稍等片刻~)
./configure --prefix=/usr/local/python3
# 如果pip3 install出现ssl异常, 那么就换成以下语句重新编译
# ./configure --with-ssl --prefix=/usr/local/python3
make && make install
# 退回上一层, 删除解压后的源码目录(防止误覆盖)
cd ..
rm -rf Python-3.8.5
# 环境变量开机生效(不会用vim保存的建议摔电脑, 或者去百度)
vim /etc/profile.d/python3.sh
-----------------------
export PYTHON_HOME=/usr/local/python3
PATH=$PATH:$PYTHON_HOME/bin
-----------------------
# 配置生效
source /etc/profile
# 检查python3安装情况
[root@localhost local]# python3 -V
Python 3.8.5
安装ansible-2.4.6的软件包、开发依赖包
1、rpm包下载
仓库:https://releases.ansible.com/ansible/rpm/release/epel-7-x86_64/
下载地址:https://releases.ansible.com/ansible/rpm/release/epel-7-x86_64/ansible-2.4.6.0-1.el7.ans.noarch.rpm
2、yum本地安装rpm包
# 切换到目录'/usr/local'
cd /usr/local
# 上传软件包'ansible-2.4.6.0-1.el7.ans.noarch.rpm'到目录'/usr/local'下
rz
# yum本地安装
yum localinstall ansible-2.4.6.0-1.el7.ans.noarch.rpm -y
# 验证安装(ok)
[root@localhost local]# ansible --version
ansible 2.4.6.0
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Nov 16 2020, 22:23:17) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# 验证功能(ok)
[root@localhost local]# ansible 127.0.0.1 -m ping
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit
localhost does not match 'all'
127.0.0.1 | SUCCESS => {
"changed": false,
"failed": false,
"ping": "pong"
}
3、pip3 指定安装ansible开发依赖
# 从阿里镜像源,查询并安装2.4.6.0版本的ansible依赖
pip3 install ansible==2.4.6.0 -i https://mirrors.aliyun.com/pypi/simple
修改官方例子, 使之可用于自己的业务
ResultCallBack.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from ansible.plugins.callback import CallbackBase
class ResultCallBack(CallbackBase):
"""
结果回调
"""
def __init__(self, *args, **kwargs):
super(ResultCallBack, self).__init__(*args, **kwargs)
self.host_ok = {}
self.host_unreachable = {}
self.host_failed = {}
def v2_runner_on_unreachable(self, result):
self.host_unreachable[result._host.get_name()] = result
def v2_runner_on_ok(self, result, *args, **kwargs):
self.host_ok[result._host.get_name()] = result
def v2_runner_on_failed(self, result, *args, **kwargs):
self.host_failed[result._host.get_name()] = result
AdHocRunner.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import sys
from collections import namedtuple
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.inventory.manager import InventoryManager
from ansible.parsing.dataloader import DataLoader
from ansible.playbook.play import Play
from ansible.vars.manager import VariableManager
from ext.ansibles.ResultCallBack import ResultCallBack
from utils.FileUtils import FileUtils
class ConnectionEnum:
"""
连接类型(枚举)
"""
# LOCAL = 'local'
SSH = 'ssh'
PARAMIKO = 'paramiko'
class BecomeMethodEnum:
"""
权限升级方法(枚举)
"""
SUDO = 'sudo'
SU = 'su'
PBRUN = 'pbrun'
PFEXEC = 'pfexec'
DOAS = 'doas'
DZDO = 'dzdo'
KSU = 'ksu'
RUNAS = 'runas'
PMRUN = 'pmrun'
ENABLE = 'enable'
class AdHocRunner(object):
"""
This is a General object for parallel execute modules.
"""
def __init__(self, sources, *args, **kwargs):
self.sources = sources
self.inventory = None
self.variable_manager = None
self.loader = None
self.options = None
self.passwords = None
self.__initialize_data()
def __initialize_data(self):
"""
初始化ansible
"""
Options = namedtuple('Options', [
# 连接类型
'connection',
# 要执行的模块的路径,默认为/usr/share/ansible/
'module_path',
# 在与主机通信时的默认并行进程数
'forks',
# 远程SSH端口
'remote_port',
# 用于指定远程主机上的执行任务的用户(您登录时使用的用户)
'remote_user',
# 是否允许身份切换
'become',
# 使用哪个权限升级方法,默认为sudo
'become_method',
# 使用哪个用户运行(默认为root, 设置为具有所需特权的用户-您想要成为的用户,而不是您登录时使用的用户)
'become_user',
# 只是测试一下会改变什么内容,不会真正去执行;相反,试图预测一些可能发生的变化
'check',
# 当更改文件和模板时,显示这些文件得差异,比check效果好
'diff',
# 检查主机密钥
'host_key_checking'
])
self.loader = DataLoader()
self.options = Options(
connection=ConnectionEnum.SSH,
module_path='/path/to',
forks=100,
remote_port=22,
remote_user='root',
become='yes',
become_method=None,
become_user='root',
check=False,
diff=False,
host_key_checking=False,
)
self.passwords = dict(vault_pass='secret')
self.inventory = InventoryManager(loader=self.loader, sources=self.sources)
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
def run(self, host_list, module_name, module_args, extra_vars):
"""
run module from andible ad-hoc
:param host_list 主机列表
:param module_name ansible模块名称
:param module_args ansible模块参数
:param extra_vars 额外的参数 sudoers.yml以及模板中的参数,它对应ansible-playbook test.yml --extra-vars "host='aa' name='cc' "
"""
# create play with tasks
play_source = dict(
name='AnsiblePlay',
hosts=host_list,
# 除非明确说明不需要在远程主机上执行setup模块,否则默认自动执行。如果确实不需要setup模块传递过来的变量,则可以将该选项设置为False
gather_facts='no',
tasks=[dict(action=dict(module=module_name, args=module_args))]
)
# join vars
if extra_vars:
self.variable_manager.extra_vars = extra_vars
# create play
play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)
# actually run it
tqm = None
callback = ResultCallBack()
try:
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
stdout_callback=callback,
)
tqm.run(play)
return self.__get_result(callback)
finally:
if tqm is not None:
tqm.cleanup()
def runByTaskList(self, host_list, task_list, extra_vars):
"""
run module from andible ad-hoc
:param host_list 主机列表
:param task_list 任务对象列表
{'moduleName': 'ansible模块名称', 'moduleArgs': ''}
:param extra_vars 额外的参数 sudoers.yml以及模板中的参数,它对应ansible-playbook test.yml --extra-vars "host='aa' name='cc' "
"""
# create play with tasks
if not isinstance(task_list, list):
sys.exit()
tasks = list()
for item in task_list:
tasks.append(dict(action=dict(module=item['moduleName'], args=item['moduleArgs'])))
play_source = dict(
name='AnsiblePlay',
hosts=host_list,
# 除非明确说明不需要在远程主机上执行setup模块,否则默认自动执行。如果确实不需要setup模块传递过来的变量,则可以将该选项设置为False
gather_facts='no',
tasks=tasks
)
# join vars
if extra_vars:
self.variable_manager.extra_vars = extra_vars
# create play
play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)
# actually run it
tqm = None
callback = ResultCallBack()
try:
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
stdout_callback=callback,
)
tqm.run(play)
return self.__get_result(callback)
finally:
if tqm is not None:
tqm.cleanup()
def run_playbook(self, host_list, extra_vars, yaml_filename):
"""
run ansible playbook
:param host_list 主机列表
:param extra_vars 额外的参数 sudoers.yml以及模板中的参数,它对应ansible-playbook test.yml --extra-vars "host='aa' name='cc' "
:param yaml_filename yaml文件名称
"""
try:
callback = ResultCallBack()
yaml_path = FileUtils.get_root_path('ansible2player') + 'resources' + os.sep + 'ansible' + yaml_filename + '.yaml'
if not os.path.exists(yaml_path):
print('%s 文件不存在 ' % yaml_path)
sys.exit()
# join vars
if extra_vars:
self.variable_manager.extra_vars = extra_vars
# create host str
host_list_str = ','.join([item for item in host_list])
self.variable_manager.extra_vars['host_list'] = host_list_str
print('playbook 额外参数:%s' % self.variable_manager.extra_vars)
# yaml file list
filenames = [yaml_path]
# actually run it
executor = PlaybookExecutor(
playbooks=filenames,
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
)
executor._tqm._stdout_callback = callback
executor.run()
return self.__get_result(callback)
except Exception as e:
print('run_playbook:%s' % e)
return None
def __get_result(self, callback):
results_raw = {'success': {}, 'failed': {}, 'unreachable': {}}
for host, result in callback.host_ok.items():
results_raw['success'][host] = result._result
for host, result in callback.host_failed.items():
results_raw['failed'][host] = result._result
for host, result in callback.host_unreachable.items():
results_raw['unreachable'][host] = result._result['msg']
# print('Ansible执行结果集:%s' % results_raw)
return results_raw
Test.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
from ext.ansibles.AdHocRunner import AdHocRunner
# 传入inventory路径, 有就传, 没有就None
runner = AdHocRunner('/etc/ansible/hosts')
# 获取服务器磁盘信息
result = runner.run('127.0.0.1', 'setup', "filter='ansible_mounts'", None)
# 执行成功的主机
success = result['success']
# 执行失败的主机
failed = result['failed']
# 不可到达的主机
unreachable = result['unreachable']
print(success)
执行结果(字典)
{
"127.0.0.1": {
"ansible_facts": {
"ansible_mounts": [{
"mount": "/",
"device": "/dev/mapper/cl-root",
"fstype": "xfs",
"options": "rw,relatime,attr2,inode64,noquota",
"uuid": "52851cc2-a9ab-4e54-9a49-0a938c2bb513",
"size_total": 38216605696,
"size_available": 23118983168,
"block_size": 4096,
"block_total": 9330226,
"block_available": 5644283,
"block_used": 3685943,
"inode_total": 18669568,
"inode_available": 18118528,
"inode_used": 551040
}, {
"mount": "/boot",
"device": "/dev/sda1",
"fstype": "xfs",
"options": "rw,relatime,attr2,inode64,noquota",
"uuid": "34c2a884-3c37-40fd-a8ea-3d54ce931a2f",
"size_total": 1063256064,
"size_available": 901054464,
"block_size": 4096,
"block_total": 259584,
"block_available": 219984,
"block_used": 39600,
"inode_total": 524288,
"inode_available": 523959,
"inode_used": 329
}, {
"mount": "/app",
"device": "/dev/mapper/appvg-applv",
"fstype": "xfs",
"options": "rw,relatime,attr2,inode64,noquota",
"uuid": "f9c5fab4-79ad-4e02-97ad-54aab4ca1f72",
"size_total": 106174664704,
"size_available": 89128210432,
"block_size": 4096,
"block_total": 25921549,
"block_available": 21759817,
"block_used": 4161732,
"inode_total": 51853312,
"inode_available": 51710733,
"inode_used": 142579
}, {
"mount": "/home",
"device": "/dev/mapper/cl-home",
"fstype": "xfs",
"options": "rw,relatime,attr2,inode64,noquota",
"uuid": "446edbe9-a3f5-4614-a7bc-57060e39f5d9",
"size_total": 18658361344,
"size_available": 18300760064,
"block_size": 4096,
"block_total": 4555264,
"block_available": 4467959,
"block_used": 87305,
"inode_total": 9115648,
"inode_available": 9106838,
"inode_used": 8810
}]
},
"invocation": {
"module_args": {
"filter": "ansible_mounts",
"gather_subset": ["all"],
"gather_timeout": 10,
"fact_path": "/etc/ansible/facts.d"
}
},
"_ansible_parsed": true,
"_ansible_verbose_override": true,
"_ansible_no_log": false,
"changed": false
}
}
python3 + flask + celery + ansible-api
这个项目雏形后期会在博客给出的gitee地址的, 期待吧
再就是这个sdk访问方式是互信免密的,所以还是得和被控机器做下SSH互信
1、首先, 检查文件夹'/root/.ssh'是否存在;
如果存在进去拷贝出公钥的内容:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAq7iH6+FqHhGQvfUlQugw4/BtfQKJyLzCgDzrmy3ynB0en0EVHUWzBfT4mkPxFdfZE4Vw9fOz+R6H/lFRiHy7tI4U+aWfc/fQIFmESBf6S7RRHPGNbxJ++2hnY7blJNWUPEaGR3khVRbTMP7iKDHD12DsHLCJoFg/WTqvmBSeBCqLtaGIcnh1n4BUSExGbTL4wX54CIVJQuWZNav7L0sa9O+hZ3ZS6lZfj7wSTla8eLBbHkCWdsx81i4/Y+EsQF+9xTziYYBFiWTVOjtEfJV1dI4hFSdiiokn16eEtV0YACbm16zNyJZljabcfNRSHvim6TBpL615QyEJJgm15xcZ root@localhost.localdomain
如果不存在, 则执行 'ssh-keygen -t rsa' 命令(去掉引号, 谢谢), 一路狂按回车键即可, 然后也是拷贝出公钥的内容
2、到被控机上面, 将公钥的内容拷贝到要互信的用户的.ssh目录下的authorized_keys文件中, 这里几乎是每一个初学者都特别懵逼的地方
举几个简单的例子:
'192.168.175.130'免密登陆'192.168.175.131':
a、在'192.168.175.130'执行'ssh root@192.168.175.131': 在'192.168.175.130'上以被控机root的身份登陆'192.168.175.131', 那么就需要在/root/.ssh/authorized_keys文件中加入公钥内容.
b、在'192.168.175.130'执行'ssh odboy@192.168.175.131': 在'192.168.175.130'上以被控机odboy的身份登陆'192.168.175.131', 那么就需要在/home/odboy/.ssh/authorized_keys文件中加入公钥内容, 这里又有一个问题, 如果我有odboy这个用户, 我并没有.ssh这个目录咋搞, Easy我教你; 执行 'ssh-keygen -t rsa' 命令(去掉引号, 谢谢), 一路狂按回车键即可, odboy这个用户的公私钥内容最好请空, 以免混乱.
cat /dev/null > /home/odboy/.ssh/id_rsa
cat /dev/null > /home/odboy/.ssh/id_rsa.pub