mirror of https://github.com/jumpserver/jumpserver
[future] 在主机层面支持多种sudo, 以及回调结果的分类
parent
13f34a8bdd
commit
eb5f0fcf68
|
@ -22,11 +22,11 @@ class Config(object):
|
||||||
"""Ansible运行时配置类, 用于初始化Ansible.
|
"""Ansible运行时配置类, 用于初始化Ansible.
|
||||||
"""
|
"""
|
||||||
def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
|
def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
|
||||||
forks=None, ask_vault_pass=None, vault_password_files=None, new_vault_password_file=None,
|
forks=None, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
|
||||||
output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=None, ask_su_pass=None,
|
output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
|
||||||
sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=None,
|
sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
|
||||||
ask_pass=None, private_key_file=None, remote_user=None, connection=None, timeout=None, ssh_common_args=None,
|
ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=None, ssh_common_args=None,
|
||||||
sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=None,
|
sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
|
||||||
syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
|
syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
|
||||||
self.verbosity = verbosity
|
self.verbosity = verbosity
|
||||||
self.inventory = inventory
|
self.inventory = inventory
|
||||||
|
@ -86,46 +86,75 @@ class InventoryMixin(object):
|
||||||
|
|
||||||
def gen_inventory(self):
|
def gen_inventory(self):
|
||||||
"""用于生成动态构建Ansible Inventory.
|
"""用于生成动态构建Ansible Inventory.
|
||||||
|
self.hosts: [
|
||||||
|
{"host": <ip>,
|
||||||
|
"port": <port>,
|
||||||
|
"user": <user>,
|
||||||
|
"pass": <pass>,
|
||||||
|
"key": <sshKey>,
|
||||||
|
"group": <default>
|
||||||
|
"other_host_var": <other>},
|
||||||
|
{...},
|
||||||
|
]
|
||||||
|
self.group_vars: {
|
||||||
|
"groupName1": {"var1": <value>, "var2": <value>, ...},
|
||||||
|
"groupName2": {"var1": <value>, "var2": <value>, ...},
|
||||||
|
}
|
||||||
|
|
||||||
:return: 返回一个Ansible的inventory对象
|
:return: 返回一个Ansible的inventory对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: 验证输入
|
# TODO: 验证输入
|
||||||
|
|
||||||
# 创建Ansible Group.
|
# 创建Ansible Group,如果没有则创建default组
|
||||||
for asset in self.hosts:
|
for asset in self.hosts:
|
||||||
g_name = asset.get('group', 'default')
|
g_name = asset.get('group', 'default')
|
||||||
if g_name not in [g.name for g in self.groups]:
|
if g_name not in [g.name for g in self.groups]:
|
||||||
group = Group(name=asset.get('group', 'default'))
|
group = Group(name=g_name)
|
||||||
self.groups.append(group)
|
self.groups.append(group)
|
||||||
|
|
||||||
# 初始化组变量
|
# 添加组变量到相应的组上
|
||||||
for group_name, variables in self.group_vars.iteritems():
|
for group_name, variables in self.group_vars.iteritems():
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
if g.name == group_name:
|
if g.name == group_name:
|
||||||
for v_name, v_value in variables:
|
for v_name, v_value in variables.iteritems():
|
||||||
g.set_variable(v_name, v_value)
|
g.set_variable(v_name, v_value)
|
||||||
|
|
||||||
# 往组里面添加Host
|
# 往组里面添加Host
|
||||||
for asset in self.hosts:
|
for asset in self.hosts:
|
||||||
|
# 添加Host链接的常用变量(host,port,user,pass,key)
|
||||||
host = Host(name=asset['name'], port=asset['port'])
|
host = Host(name=asset['name'], port=asset['port'])
|
||||||
host.set_variable('ansible_ssh_host', asset['ip'])
|
host.set_variable('ansible_host', asset['ip'])
|
||||||
host.set_variable('ansible_ssh_port', asset['port'])
|
host.set_variable('ansible_port', asset['port'])
|
||||||
host.set_variable('ansible_ssh_user', asset['username'])
|
host.set_variable('ansible_user', asset['username'])
|
||||||
|
|
||||||
|
# 添加密码和秘钥
|
||||||
if asset.get('password'):
|
if asset.get('password'):
|
||||||
host.set_variable('ansible_ssh_pass', asset['password'])
|
host.set_variable('ansible_ssh_pass', asset['password'])
|
||||||
if asset.get('key'):
|
if asset.get('key'):
|
||||||
host.set_variable('ansible_ssh_private_key_file', asset['key'])
|
host.set_variable('ansible_ssh_private_key_file', asset['key'])
|
||||||
|
|
||||||
|
# 添加become支持
|
||||||
|
become = asset.get("become", None)
|
||||||
|
if become is not None:
|
||||||
|
host.set_variable("ansible_become", True)
|
||||||
|
host.set_variable("ansible_become_method", become.get('method'))
|
||||||
|
host.set_variable("ansible_become_user", become.get('user'))
|
||||||
|
host.set_variable("ansible_become_pass", become.get('pass'))
|
||||||
|
else:
|
||||||
|
host.set_variable("ansible_become", False)
|
||||||
|
|
||||||
|
# 添加其他Host的额外变量
|
||||||
for key, value in asset.iteritems():
|
for key, value in asset.iteritems():
|
||||||
if key not in ["name", "port", "ip", "username", "password", "key"]:
|
if key not in ["name", "port", "ip", "username", "password", "key"]:
|
||||||
host.set_variable(key, value)
|
host.set_variable(key, value)
|
||||||
|
|
||||||
|
# 将host添加到组里面
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
if g.name == asset.get('group', 'default'):
|
if g.name == asset.get('group', 'default'):
|
||||||
g.add_host(host)
|
g.add_host(host)
|
||||||
|
|
||||||
# 生成Ansible inventory对象
|
# 将组添加到Inventory里面,生成真正的inventory对象
|
||||||
inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
|
inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
|
||||||
for g in self.groups:
|
for g in self.groups:
|
||||||
inventory.add_group(g)
|
inventory.add_group(g)
|
||||||
|
@ -143,6 +172,7 @@ class CallbackModule(CallbackBase):
|
||||||
def __init__(self, display=None):
|
def __init__(self, display=None):
|
||||||
super(CallbackModule, self).__init__(display)
|
super(CallbackModule, self).__init__(display)
|
||||||
self.results = []
|
self.results = []
|
||||||
|
self.output = {}
|
||||||
|
|
||||||
def _new_play(self, play):
|
def _new_play(self, play):
|
||||||
return {
|
return {
|
||||||
|
@ -151,6 +181,7 @@ class CallbackModule(CallbackBase):
|
||||||
'id': str(play._uuid)
|
'id': str(play._uuid)
|
||||||
},
|
},
|
||||||
'tasks': []
|
'tasks': []
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _new_task(self, task):
|
def _new_task(self, task):
|
||||||
|
@ -159,22 +190,41 @@ class CallbackModule(CallbackBase):
|
||||||
'name': task.name,
|
'name': task.name,
|
||||||
'id': str(task._uuid)
|
'id': str(task._uuid)
|
||||||
},
|
},
|
||||||
'hosts': {}
|
'failed': {},
|
||||||
|
'unreachable': {},
|
||||||
|
'skipped': {},
|
||||||
|
'no_hosts': {},
|
||||||
|
'success': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def v2_runner_on_failed(self, result, ignore_errors=False):
|
||||||
|
host = result._host
|
||||||
|
self.results[-1]['tasks'][-1]['failed'][host.name] = result._result
|
||||||
|
|
||||||
|
def v2_runner_on_unreachable(self, result):
|
||||||
|
host = result._host
|
||||||
|
self.results[-1]['tasks'][-1]['unreachable'][host.name] = result._result
|
||||||
|
|
||||||
|
def v2_runner_on_skipped(self, result):
|
||||||
|
host = result._host
|
||||||
|
self.results[-1]['tasks'][-1]['skipped'][host.name] = result._result
|
||||||
|
|
||||||
|
def v2_runner_on_no_hosts(self, task):
|
||||||
|
self.results[-1]['tasks'][-1]['no_hosts']['name'] = task.name
|
||||||
|
self.results[-1]['tasks'][-1]['no_hosts']['uuid'] = task.uuid
|
||||||
|
|
||||||
|
def v2_runner_on_ok(self, result):
|
||||||
|
host = result._host
|
||||||
|
self.results[-1]['tasks'][-1]['success'][host.name] = result._result
|
||||||
|
|
||||||
def v2_playbook_on_play_start(self, play):
|
def v2_playbook_on_play_start(self, play):
|
||||||
self.results.append(self._new_play(play))
|
self.results.append(self._new_play(play))
|
||||||
|
|
||||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||||
self.results[-1]['tasks'].append(self._new_task(task))
|
self.results[-1]['tasks'].append(self._new_task(task))
|
||||||
|
|
||||||
def v2_runner_on_ok(self, result, **kwargs):
|
|
||||||
host = result._host
|
|
||||||
self.results[-1]['tasks'][-1]['hosts'][host.name] = result._result
|
|
||||||
|
|
||||||
def v2_playbook_on_stats(self, stats):
|
def v2_playbook_on_stats(self, stats):
|
||||||
"""Display info about playbook statistics"""
|
"""Display info about playbook statistics"""
|
||||||
|
|
||||||
hosts = sorted(stats.processed.keys())
|
hosts = sorted(stats.processed.keys())
|
||||||
|
|
||||||
summary = {}
|
summary = {}
|
||||||
|
@ -182,16 +232,8 @@ class CallbackModule(CallbackBase):
|
||||||
s = stats.summarize(h)
|
s = stats.summarize(h)
|
||||||
summary[h] = s
|
summary[h] = s
|
||||||
|
|
||||||
output = {
|
self.output['plays'] = self.results
|
||||||
'plays': self.results,
|
self.output['stats'] = summary
|
||||||
'stats': summary
|
|
||||||
}
|
|
||||||
|
|
||||||
print(json.dumps(output, indent=4, sort_keys=True))
|
|
||||||
|
|
||||||
v2_runner_on_failed = v2_runner_on_ok
|
|
||||||
v2_runner_on_unreachable = v2_runner_on_ok
|
|
||||||
v2_runner_on_skipped = v2_runner_on_ok
|
|
||||||
|
|
||||||
|
|
||||||
class PlayBookRunner(InventoryMixin):
|
class PlayBookRunner(InventoryMixin):
|
||||||
|
@ -279,7 +321,7 @@ class PlayBookRunner(InventoryMixin):
|
||||||
class ADHocRunner(InventoryMixin):
|
class ADHocRunner(InventoryMixin):
|
||||||
"""ADHoc接口
|
"""ADHoc接口
|
||||||
"""
|
"""
|
||||||
def __init__(self, config, play_data, become_pass=None, *hosts, **group_vars):
|
def __init__(self, config, play_data, *hosts, **group_vars):
|
||||||
"""
|
"""
|
||||||
:param hosts: 见PlaybookRunner参数
|
:param hosts: 见PlaybookRunner参数
|
||||||
:param group_vars: 见PlaybookRunner参数
|
:param group_vars: 见PlaybookRunner参数
|
||||||
|
@ -299,13 +341,9 @@ class ADHocRunner(InventoryMixin):
|
||||||
# 设置verbosity级别, 及命令行的--verbose选项
|
# 设置verbosity级别, 及命令行的--verbose选项
|
||||||
self.display = Display()
|
self.display = Display()
|
||||||
self.display.verbosity = self.options.verbosity
|
self.display.verbosity = self.options.verbosity
|
||||||
playbook_executor.verbosity = self.options.verbosity
|
|
||||||
|
|
||||||
# sudo成其他用户的配置
|
# sudo的配置移到了Host级别去了,因此这里不再需要处理
|
||||||
self.options.become = True
|
self.passwords = None
|
||||||
self.options.become_method = 'sudo'
|
|
||||||
self.options.become_user = 'root'
|
|
||||||
self.passwords = {'become_pass': become_pass}
|
|
||||||
|
|
||||||
# 生成Ansible inventory, 这些变量Mixin都会用到
|
# 生成Ansible inventory, 这些变量Mixin都会用到
|
||||||
self.hosts = hosts
|
self.hosts = hosts
|
||||||
|
@ -344,24 +382,36 @@ class ADHocRunner(InventoryMixin):
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
conf = Config()
|
conf = Config()
|
||||||
assets = [{
|
assets = [
|
||||||
"name": "localhost",
|
{
|
||||||
"ip": "localhost",
|
"name": "192.168.1.119",
|
||||||
|
"ip": "192.168.1.119",
|
||||||
|
"port": "22",
|
||||||
|
"username": "root",
|
||||||
|
"password": "xxx",
|
||||||
|
"key": "asset_private_key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "192.168.232.135",
|
||||||
|
"ip": "192.168.232.135",
|
||||||
"port": "22",
|
"port": "22",
|
||||||
"username": "yumaojun",
|
"username": "yumaojun",
|
||||||
"password": "yusky0902",
|
"password": "xxx",
|
||||||
"key": "asset_private_key",
|
"key": "asset_private_key",
|
||||||
}]
|
"become": {"method": "sudo", "user": "root", "pass": "yusky0902"}
|
||||||
|
},
|
||||||
|
]
|
||||||
# 初始化Play
|
# 初始化Play
|
||||||
play_source = {
|
play_source = {
|
||||||
"name": "Ansible Play",
|
"name": "Ansible Play",
|
||||||
"hosts": "*",
|
"hosts": "default",
|
||||||
"gather_facts": "no",
|
"gather_facts": "no",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
dict(action=dict(module='setup')),
|
dict(action=dict(module='setup')),
|
||||||
|
dict(action=dict(module='command', args='lsdfd'))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
hoc = ADHocRunner(conf, play_source,'yusky0902', *assets)
|
hoc = ADHocRunner(conf, play_source, *assets)
|
||||||
ext_code, result = hoc.run()
|
ext_code, result = hoc.run()
|
||||||
print ext_code
|
print ext_code
|
||||||
print result
|
print result
|
||||||
|
|
|
@ -1,5 +1,34 @@
|
||||||
from __future__ import unicode_literals
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Play(models.Model):
|
||||||
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
|
uuid = models.CharField(max_length=128, verbose_name=_('UUID'))
|
||||||
|
completed = models.BooleanField(default=False, verbose_name=_('IsCompleted'))
|
||||||
|
status_code = models.IntegerField(default=0, verbose_name=_('StatusCode'))
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Task(models.Model):
|
||||||
|
play = models.ForeignKey(Play, related_name='tasks', blank=True)
|
||||||
|
name = models.CharField(max_length=128, blank=True, blverbose_name=_('Name'))
|
||||||
|
uuid = models.CharField(max_length=128, verbose_name=_('UUID'))
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.clean()
|
||||||
|
|
||||||
|
|
||||||
|
class HostResult(models.Model):
|
||||||
|
task = models.ForeignKey(Task, related_name='host_results', blank=True)
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ ForgeryPy==0.1
|
||||||
openpyxl==2.4.0
|
openpyxl==2.4.0
|
||||||
paramiko==2.0.2
|
paramiko==2.0.2
|
||||||
celery==3.1.23
|
celery==3.1.23
|
||||||
ansible==2.1.1.0
|
ansible==2.1.2.0
|
||||||
django-simple-captcha==0.5.2
|
django-simple-captcha==0.5.2
|
||||||
django-formtools==1.0
|
django-formtools==1.0
|
||||||
sshpubkeys==2.2.0
|
sshpubkeys==2.2.0
|
||||||
|
|
Loading…
Reference in New Issue