mirror of https://github.com/jumpserver/jumpserver
[Feature] 优化Ops ansible api
parent
ec8106e43d
commit
e57121a780
|
@ -1,9 +1,10 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
#
|
#
|
||||||
from ops.utils import run_AdHoc
|
from .models import Asset
|
||||||
|
|
||||||
|
|
||||||
def test_admin_user_connective_manual(asset):
|
def test_admin_user_connective_manual(asset):
|
||||||
|
from ops.utils import run_AdHoc
|
||||||
if not isinstance(asset, list):
|
if not isinstance(asset, list):
|
||||||
asset = [asset]
|
asset = [asset]
|
||||||
task_tuple = (
|
task_tuple = (
|
||||||
|
@ -15,3 +16,6 @@ def test_admin_user_connective_manual(asset):
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_assets_by_id_list(id_list):
|
||||||
|
return Asset.objects.filter(id__in=id_list)
|
||||||
|
|
|
@ -1418,23 +1418,23 @@ msgid "Assets id"
|
||||||
msgstr "资产id"
|
msgstr "资产id"
|
||||||
|
|
||||||
#: ops/models.py:27
|
#: ops/models.py:27
|
||||||
msgid "Task module and args json format"
|
msgid "Playbook module and args json format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ops/models.py:28
|
#: ops/models.py:28
|
||||||
msgid "Task run pattern"
|
msgid "Playbook run pattern"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ops/models.py:29
|
#: ops/models.py:29
|
||||||
msgid "Task raw result"
|
msgid "Playbook raw result"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ops/models.py:30
|
#: ops/models.py:30
|
||||||
msgid "Task summary"
|
msgid "Playbook summary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ops/templates/ops/task_detail.html:19
|
#: ops/templates/ops/task_detail.html:19
|
||||||
msgid "Task replay detail"
|
msgid "Playbook replay detail"
|
||||||
msgstr "任务记录详情"
|
msgstr "任务记录详情"
|
||||||
|
|
||||||
#: ops/templates/ops/task_detail.html:62
|
#: ops/templates/ops/task_detail.html:62
|
||||||
|
@ -1669,7 +1669,7 @@ msgid "Job Center"
|
||||||
msgstr "作业中心"
|
msgstr "作业中心"
|
||||||
|
|
||||||
#: templates/_nav.html:51
|
#: templates/_nav.html:51
|
||||||
msgid "Task"
|
msgid "Playbook"
|
||||||
msgstr "任务"
|
msgstr "任务"
|
||||||
|
|
||||||
#: templates/_nav.html:62
|
#: templates/_nav.html:62
|
||||||
|
|
|
@ -1,65 +1,76 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
from ansible.plugins.callback import CallbackBase
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
|
||||||
class CommandResultCallback(CallbackBase):
|
|
||||||
def __init__(self, display=None):
|
|
||||||
self.result_q = dict(contacted={}, dark={})
|
|
||||||
super(CommandResultCallback, self).__init__(display)
|
|
||||||
|
|
||||||
def gather_result(self, n, res):
|
|
||||||
self.result_q[n][res._host.name] = {}
|
|
||||||
self.result_q[n][res._host.name]['cmd'] = res._result.get('cmd')
|
|
||||||
self.result_q[n][res._host.name]['stderr'] = res._result.get('stderr')
|
|
||||||
self.result_q[n][res._host.name]['stdout'] = res._result.get('stdout')
|
|
||||||
self.result_q[n][res._host.name]['rc'] = res._result.get('rc')
|
|
||||||
|
|
||||||
def v2_runner_on_ok(self, result):
|
|
||||||
self.gather_result("contacted", result)
|
|
||||||
|
|
||||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
|
||||||
self.gather_result("dark", result)
|
|
||||||
|
|
||||||
def v2_runner_on_unreachable(self, result):
|
|
||||||
self.gather_result("dark", result)
|
|
||||||
|
|
||||||
def v2_runner_on_skipped(self, result):
|
|
||||||
self.gather_result("dark", result)
|
|
||||||
|
|
||||||
|
|
||||||
class AdHocResultCallback(CallbackBase):
|
class AdHocResultCallback(CallbackBase):
|
||||||
"""
|
"""
|
||||||
AdHoc result Callback
|
AdHoc result Callback
|
||||||
"""
|
"""
|
||||||
def __init__(self, display=None):
|
def __init__(self, display=None):
|
||||||
self.result_q = dict(contacted={}, dark={})
|
# result_raw example: {
|
||||||
super(AdHocResultCallback, self).__init__(display)
|
# "ok": {"hostname": []},
|
||||||
|
# "failed": {"hostname": []},
|
||||||
|
# "unreachable: {"hostname": []},
|
||||||
|
# "skipped": {"hostname": []},
|
||||||
|
# }
|
||||||
|
# results_summary example: {
|
||||||
|
# "contacted": {"hostname",...},
|
||||||
|
# "dark": {"hostname": ["error",...],},
|
||||||
|
# }
|
||||||
|
self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={})
|
||||||
|
self.results_summary = dict(contacted=set(), dark={})
|
||||||
|
super().__init__(display)
|
||||||
|
|
||||||
def gather_result(self, n, res):
|
def gather_result(self, t, host, res):
|
||||||
if res._host.name in self.result_q[n]:
|
if self.results_raw[t].get(host):
|
||||||
self.result_q[n][res._host.name].append(res._result)
|
self.results_raw[t][host].append(res)
|
||||||
else:
|
else:
|
||||||
self.result_q[n][res._host.name] = [res._result]
|
self.results_raw[t][host] = [res]
|
||||||
|
self.clean_result(t, host, res)
|
||||||
|
|
||||||
def v2_runner_on_ok(self, result):
|
def clean_result(self, t, host, res):
|
||||||
self.gather_result("contacted", result)
|
contacted = self.results_summary["contacted"]
|
||||||
|
dark = self.results_summary["dark"]
|
||||||
|
if t in ("ok", "skipped") and host not in dark:
|
||||||
|
contacted.add(host)
|
||||||
|
else:
|
||||||
|
dark[host].append(res)
|
||||||
|
if host in contacted:
|
||||||
|
contacted.remove(dark)
|
||||||
|
|
||||||
def v2_runner_on_failed(self, result, ignore_errors=False):
|
def runner_on_ok(self, host, res):
|
||||||
self.gather_result("dark", result)
|
self.gather_result("ok", host, res)
|
||||||
|
|
||||||
def v2_runner_on_unreachable(self, result):
|
def runner_on_failed(self, host, res, ignore_errors=False):
|
||||||
self.gather_result("dark", result)
|
self.gather_result("failed", host, res)
|
||||||
|
|
||||||
def v2_runner_on_skipped(self, result):
|
def runner_on_unreachable(self, host, res):
|
||||||
self.gather_result("dark", result)
|
self.gather_result("unreachable", host, res)
|
||||||
|
|
||||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
def runner_on_skipped(self, host, item=None):
|
||||||
pass
|
self.gather_result("skipped", host, item)
|
||||||
|
|
||||||
def v2_playbook_on_play_start(self, play):
|
|
||||||
pass
|
class CommandResultCallback(AdHocResultCallback):
|
||||||
|
def __init__(self, display=None):
|
||||||
|
self.results_command = dict()
|
||||||
|
super().__init__(display)
|
||||||
|
|
||||||
|
def gather_result(self, t, host, res):
|
||||||
|
super().gather_result(t, host, res)
|
||||||
|
self.gather_cmd(t, host, res)
|
||||||
|
|
||||||
|
def gather_cmd(self, t, host, res):
|
||||||
|
cmd = {}
|
||||||
|
if t == "ok":
|
||||||
|
cmd['cmd'] = res.get('cmd')
|
||||||
|
cmd['stderr'] = res.get('stderr')
|
||||||
|
cmd['stdout'] = res.get('stdout')
|
||||||
|
cmd['rc'] = res.get('rc')
|
||||||
|
else:
|
||||||
|
cmd['err'] = "Error: {}".format(res)
|
||||||
|
self.results_command[host] = cmd
|
||||||
|
|
||||||
|
|
||||||
class PlaybookResultCallBack(CallbackBase):
|
class PlaybookResultCallBack(CallbackBase):
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleError(Exception):
|
||||||
|
pass
|
|
@ -1,31 +1,52 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from ansible.inventory import Inventory, Host, Group
|
from ansible.inventory.group import Group
|
||||||
from ansible.vars import VariableManager
|
from ansible.inventory.host import Host
|
||||||
|
from ansible.vars.manager import VariableManager
|
||||||
|
from ansible.inventory.manager import InventoryManager
|
||||||
from ansible.parsing.dataloader import DataLoader
|
from ansible.parsing.dataloader import DataLoader
|
||||||
|
|
||||||
|
|
||||||
class JMSHost(Host):
|
class JMSHost(Host):
|
||||||
def __init__(self, asset):
|
def __init__(self, host_data):
|
||||||
self.asset = asset
|
"""
|
||||||
self.name = name = asset.get('hostname') or asset.get('ip')
|
初始化
|
||||||
self.port = port = asset.get('port') or 22
|
:param host_data: {
|
||||||
super(JMSHost, self).__init__(name, port)
|
"hostname": "",
|
||||||
self.set_all_variable()
|
"ip": "",
|
||||||
|
"port": "",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"private_key": "",
|
||||||
|
"become": {
|
||||||
|
"method": "",
|
||||||
|
"user": "",
|
||||||
|
"pass": "",
|
||||||
|
}
|
||||||
|
"groups": [],
|
||||||
|
"vars": {},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
self.host_data = host_data
|
||||||
|
hostname = host_data.get('hostname') or host_data.get('ip')
|
||||||
|
port = host_data.get('port') or 22
|
||||||
|
super(JMSHost, self).__init__(hostname, port)
|
||||||
|
self.__set_required_variables()
|
||||||
|
self.__set_extra_variables()
|
||||||
|
|
||||||
def set_all_variable(self):
|
def __set_required_variables(self):
|
||||||
asset = self.asset
|
host_data = self.host_data
|
||||||
self.set_variable('ansible_host', asset['ip'])
|
self.set_variable('ansible_host', host_data['ip'])
|
||||||
self.set_variable('ansible_port', asset['port'])
|
self.set_variable('ansible_port', host_data['port'])
|
||||||
self.set_variable('ansible_user', asset['username'])
|
self.set_variable('ansible_user', host_data['username'])
|
||||||
|
|
||||||
# 添加密码和秘钥
|
# 添加密码和秘钥
|
||||||
if asset.get('password'):
|
if host_data.get('password'):
|
||||||
self.set_variable('ansible_ssh_pass', asset['password'])
|
self.set_variable('ansible_ssh_pass', host_data['password'])
|
||||||
if asset.get('private_key'):
|
if host_data.get('private_key'):
|
||||||
self.set_variable('ansible_ssh_private_key_file', asset['private_key'])
|
self.set_variable('ansible_ssh_private_key_file', host_data['private_key'])
|
||||||
|
|
||||||
# 添加become支持
|
# 添加become支持
|
||||||
become = asset.get("become", False)
|
become = host_data.get("become", False)
|
||||||
if become:
|
if become:
|
||||||
self.set_variable("ansible_become", True)
|
self.set_variable("ansible_become", True)
|
||||||
self.set_variable("ansible_become_method", become.get('method', 'sudo'))
|
self.set_variable("ansible_become_method", become.get('method', 'sudo'))
|
||||||
|
@ -34,58 +55,73 @@ class JMSHost(Host):
|
||||||
else:
|
else:
|
||||||
self.set_variable("ansible_become", False)
|
self.set_variable("ansible_become", False)
|
||||||
|
|
||||||
|
def __set_extra_variables(self):
|
||||||
|
for k, v in self.host_data.get('vars', {}).items():
|
||||||
|
self.set_variable(k, v)
|
||||||
|
|
||||||
class JMSInventory(Inventory):
|
def __repr__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class JMSInventory(InventoryManager):
|
||||||
"""
|
"""
|
||||||
提供生成Ansible inventory对象的方法
|
提供生成Ansible inventory对象的方法
|
||||||
"""
|
"""
|
||||||
|
loader_class = DataLoader
|
||||||
|
variable_manager_class = VariableManager
|
||||||
|
host_manager_class = JMSHost
|
||||||
|
|
||||||
def __init__(self, host_list=None):
|
def __init__(self, host_list=None):
|
||||||
if host_list is None:
|
if host_list is None:
|
||||||
host_list = []
|
host_list = []
|
||||||
assert isinstance(host_list, list)
|
|
||||||
self.host_list = host_list
|
self.host_list = host_list
|
||||||
self.loader = DataLoader()
|
assert isinstance(host_list, list)
|
||||||
self.variable_manager = VariableManager()
|
self.loader = self.loader_class()
|
||||||
super(JMSInventory, self).__init__(self.loader, self.variable_manager,
|
self.variable_manager = self.variable_manager_class()
|
||||||
host_list=host_list)
|
super().__init__(self.loader)
|
||||||
|
|
||||||
def parse_inventory(self, host_list):
|
def get_groups(self):
|
||||||
"""用于生成动态构建Ansible Inventory.
|
return self._inventory.groups
|
||||||
self.host_list: [
|
|
||||||
{"name": "asset_name",
|
def get_group(self, name):
|
||||||
"ip": <ip>,
|
return self._inventory.groups.get(name, None)
|
||||||
"port": <port>,
|
|
||||||
"user": <user>,
|
def parse_sources(self, cache=False):
|
||||||
"pass": <pass>,
|
"""
|
||||||
"key": <sshKey>,
|
用于生成动态构建Ansible Inventory. super().__init__ 会自动调用
|
||||||
"groups": ['group1', 'group2'],
|
host_list: [{
|
||||||
"other_host_var": <other>},
|
"hostname": "",
|
||||||
{...},
|
"ip": "",
|
||||||
|
"port": "",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"private_key": "",
|
||||||
|
"become": {
|
||||||
|
"method": "",
|
||||||
|
"user": "",
|
||||||
|
"pass": "",
|
||||||
|
},
|
||||||
|
"groups": [],
|
||||||
|
"vars": {},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
:return: 返回一个Ansible的inventory对象
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
group_all = self.get_group('all')
|
||||||
|
ungrouped = self.get_group('ungrouped')
|
||||||
|
|
||||||
# TODO: 验证输入
|
for host_data in self.host_list:
|
||||||
# 创建Ansible Group,如果没有则创建default组
|
host = self.host_manager_class(host_data=host_data)
|
||||||
ungrouped = Group('ungrouped')
|
self.hosts[host_data['hostname']] = host
|
||||||
all = Group('all')
|
groups_data = host_data.get('groups')
|
||||||
all.add_child_group(ungrouped)
|
if groups_data:
|
||||||
self.groups = dict(all=all, ungrouped=ungrouped)
|
for group_name in groups_data:
|
||||||
|
group = self.get_group(group_name)
|
||||||
for asset in host_list:
|
if group is None:
|
||||||
host = JMSHost(asset=asset)
|
|
||||||
asset_groups = asset.get('groups')
|
|
||||||
if asset_groups:
|
|
||||||
for group_name in asset_groups:
|
|
||||||
if group_name not in self.groups:
|
|
||||||
group = Group(group_name)
|
group = Group(group_name)
|
||||||
self.groups[group_name] = group
|
self.add_group(group)
|
||||||
else:
|
|
||||||
group = self.groups[group_name]
|
|
||||||
group.add_host(host)
|
group.add_host(host)
|
||||||
else:
|
else:
|
||||||
ungrouped.add_host(host)
|
ungrouped.add_host(host)
|
||||||
all.add_host(host)
|
group_all.add_host(host)
|
||||||
|
|
||||||
|
|
|
@ -2,303 +2,233 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from collections import namedtuple, defaultdict
|
from collections import namedtuple
|
||||||
|
|
||||||
from ansible.executor.task_queue_manager import TaskQueueManager
|
from ansible.executor.task_queue_manager import TaskQueueManager
|
||||||
from ansible.vars import VariableManager
|
from ansible.vars.manager import VariableManager
|
||||||
from ansible.parsing.dataloader import DataLoader
|
from ansible.parsing.dataloader import DataLoader
|
||||||
from ansible.executor.playbook_executor import PlaybookExecutor
|
from ansible.executor.playbook_executor import PlaybookExecutor
|
||||||
from ansible.playbook.play import Play
|
from ansible.playbook.play import Play
|
||||||
import ansible.constants as C
|
import ansible.constants as C
|
||||||
from ansible.utils.vars import load_extra_vars
|
|
||||||
from ansible.utils.vars import load_options_vars
|
|
||||||
|
|
||||||
from .inventory import JMSInventory
|
from .inventory import JMSInventory
|
||||||
from .callback import AdHocResultCallback, PlaybookResultCallBack, \
|
from .callback import AdHocResultCallback, PlaybookResultCallBack, \
|
||||||
CommandResultCallback
|
CommandResultCallback
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from .exceptions import AnsibleError
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AdHocRunner", "PlayBookRunner"]
|
__all__ = ["AdHocRunner", "PlayBookRunner"]
|
||||||
|
|
||||||
C.HOST_KEY_CHECKING = False
|
C.HOST_KEY_CHECKING = False
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
Options = namedtuple('Options', [
|
||||||
|
'listtags', 'listtasks', 'listhosts', 'syntax', 'connection',
|
||||||
|
'module_path', 'forks', 'remote_user', 'private_key_file', 'timeout',
|
||||||
|
'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args',
|
||||||
|
'scp_extra_args', 'become', 'become_method', 'become_user',
|
||||||
|
'verbosity', 'check', 'extra_vars', 'playbook_path', 'passwords',
|
||||||
|
'diff',
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_options():
|
||||||
|
options = Options(
|
||||||
|
listtags=False,
|
||||||
|
listtasks=False,
|
||||||
|
listhosts=False,
|
||||||
|
syntax=False,
|
||||||
|
timeout=60,
|
||||||
|
connection='ssh',
|
||||||
|
module_path='',
|
||||||
|
forks=10,
|
||||||
|
remote_user='root',
|
||||||
|
private_key_file=None,
|
||||||
|
ssh_common_args="",
|
||||||
|
ssh_extra_args="",
|
||||||
|
sftp_extra_args="",
|
||||||
|
scp_extra_args="",
|
||||||
|
become=None,
|
||||||
|
become_method=None,
|
||||||
|
become_user=None,
|
||||||
|
verbosity=None,
|
||||||
|
extra_vars=[],
|
||||||
|
check=False,
|
||||||
|
playbook_path='/etc/ansible/',
|
||||||
|
passwords=None,
|
||||||
|
diff=False,
|
||||||
|
)
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
# Jumpserver not use playbook
|
# Jumpserver not use playbook
|
||||||
class PlayBookRunner(object):
|
class PlayBookRunner:
|
||||||
"""
|
"""
|
||||||
用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
|
用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
|
||||||
"""
|
"""
|
||||||
Options = namedtuple('Options', [
|
|
||||||
'listtags', 'listtasks', 'listhosts', 'syntax', 'connection',
|
|
||||||
'module_path', 'forks', 'remote_user', 'private_key_file', 'timeout',
|
|
||||||
'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args',
|
|
||||||
'scp_extra_args', 'become', 'become_method', 'become_user',
|
|
||||||
'verbosity', 'check', 'extra_vars'])
|
|
||||||
|
|
||||||
def __init__(self,
|
# Default results callback
|
||||||
hosts=None,
|
results_callback_class = PlaybookResultCallBack
|
||||||
playbook_path=None,
|
inventory_class = JMSInventory
|
||||||
forks=C.DEFAULT_FORKS,
|
loader_class = DataLoader
|
||||||
listtags=False,
|
variable_manager_class = VariableManager
|
||||||
listtasks=False,
|
options = get_default_options()
|
||||||
listhosts=False,
|
|
||||||
syntax=False,
|
|
||||||
module_path=None,
|
|
||||||
remote_user='root',
|
|
||||||
timeout=C.DEFAULT_TIMEOUT,
|
|
||||||
ssh_common_args=None,
|
|
||||||
ssh_extra_args=None,
|
|
||||||
sftp_extra_args=None,
|
|
||||||
scp_extra_args=None,
|
|
||||||
become=True,
|
|
||||||
become_method=None,
|
|
||||||
become_user="root",
|
|
||||||
verbosity=None,
|
|
||||||
extra_vars=None,
|
|
||||||
connection_type="ssh",
|
|
||||||
passwords=None,
|
|
||||||
private_key_file=None,
|
|
||||||
check=False):
|
|
||||||
|
|
||||||
|
def __init__(self, hosts=None, options=None):
|
||||||
|
"""
|
||||||
|
:param options: Ansible options like ansible.cfg
|
||||||
|
:param hosts: [
|
||||||
|
{
|
||||||
|
"hostname": "",
|
||||||
|
"ip": "",
|
||||||
|
"port": "",
|
||||||
|
"username": "",
|
||||||
|
"password": "",
|
||||||
|
"private_key": "",
|
||||||
|
"become": {
|
||||||
|
"method": "",
|
||||||
|
"user": "",
|
||||||
|
"pass": "",
|
||||||
|
},
|
||||||
|
"groups": [],
|
||||||
|
"vars": {},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
if options:
|
||||||
|
self.options = options
|
||||||
C.RETRY_FILES_ENABLED = False
|
C.RETRY_FILES_ENABLED = False
|
||||||
self.callbackmodule = PlaybookResultCallBack()
|
self.inventory = self.inventory_class(hosts)
|
||||||
if playbook_path is None or not os.path.exists(playbook_path):
|
self.loader = self.loader_class()
|
||||||
raise AnsibleError(
|
self.results_callback = self.results_callback_class()
|
||||||
"Not Found the playbook file: %s." % playbook_path)
|
self.playbook_path = options.playbook_path
|
||||||
self.playbook_path = playbook_path
|
self.variable_manager = self.variable_manager_class(
|
||||||
self.loader = DataLoader()
|
loader=self.loader, inventory=self.inventory
|
||||||
self.variable_manager = VariableManager()
|
|
||||||
self.passwords = passwords or {}
|
|
||||||
self.inventory = JMSInventory(hosts)
|
|
||||||
|
|
||||||
self.options = self.Options(
|
|
||||||
listtags=listtags,
|
|
||||||
listtasks=listtasks,
|
|
||||||
listhosts=listhosts,
|
|
||||||
syntax=syntax,
|
|
||||||
timeout=timeout,
|
|
||||||
connection=connection_type,
|
|
||||||
module_path=module_path,
|
|
||||||
forks=forks,
|
|
||||||
remote_user=remote_user,
|
|
||||||
private_key_file=private_key_file,
|
|
||||||
ssh_common_args=ssh_common_args or "",
|
|
||||||
ssh_extra_args=ssh_extra_args or "",
|
|
||||||
sftp_extra_args=sftp_extra_args,
|
|
||||||
scp_extra_args=scp_extra_args,
|
|
||||||
become=become,
|
|
||||||
become_method=become_method,
|
|
||||||
become_user=become_user,
|
|
||||||
verbosity=verbosity,
|
|
||||||
extra_vars=extra_vars or [],
|
|
||||||
check=check
|
|
||||||
)
|
)
|
||||||
|
self.passwords = options.passwords
|
||||||
|
self.__check()
|
||||||
|
|
||||||
self.variable_manager.extra_vars = load_extra_vars(loader=self.loader,
|
def __check(self):
|
||||||
options=self.options)
|
if self.options.playbook_path is None or \
|
||||||
self.variable_manager.options_vars = load_options_vars(self.options)
|
not os.path.exists(self.options.playbook_path):
|
||||||
|
raise AnsibleError(
|
||||||
|
"Not Found the playbook file: {}.".format(self.options.playbook_path)
|
||||||
|
)
|
||||||
|
if not self.inventory.list_hosts('all'):
|
||||||
|
raise AnsibleError('Inventory is empty')
|
||||||
|
|
||||||
self.variable_manager.set_inventory(self.inventory)
|
def run(self):
|
||||||
|
executor = PlaybookExecutor(
|
||||||
# 初始化playbook的executor
|
|
||||||
self.runner = PlaybookExecutor(
|
|
||||||
playbooks=[self.playbook_path],
|
playbooks=[self.playbook_path],
|
||||||
inventory=self.inventory,
|
inventory=self.inventory,
|
||||||
variable_manager=self.variable_manager,
|
variable_manager=self.variable_manager,
|
||||||
loader=self.loader,
|
loader=self.loader,
|
||||||
options=self.options,
|
options=self.options,
|
||||||
passwords=self.passwords)
|
passwords=self.passwords
|
||||||
|
|
||||||
if self.runner._tqm:
|
|
||||||
self.runner._tqm._stdout_callback = self.callbackmodule
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
if not self.inventory.list_hosts('all'):
|
|
||||||
raise AnsibleError('Inventory is empty')
|
|
||||||
self.runner.run()
|
|
||||||
self.runner._tqm.cleanup()
|
|
||||||
return self.callbackmodule.output
|
|
||||||
|
|
||||||
|
|
||||||
class AdHocRunner(object):
|
|
||||||
"""
|
|
||||||
ADHoc接口
|
|
||||||
"""
|
|
||||||
Options = namedtuple("Options", [
|
|
||||||
'connection', 'module_path', 'private_key_file', "remote_user",
|
|
||||||
'timeout', 'forks', 'become', 'become_method', 'become_user',
|
|
||||||
'check', 'extra_vars',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
results_callback_class = AdHocResultCallback
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
hosts=C.DEFAULT_HOST_LIST,
|
|
||||||
forks=C.DEFAULT_FORKS, # 5
|
|
||||||
timeout=C.DEFAULT_TIMEOUT, # SSH timeout = 10s
|
|
||||||
remote_user=C.DEFAULT_REMOTE_USER, # root
|
|
||||||
module_path=None, # dirs of custome modules
|
|
||||||
connection_type="smart",
|
|
||||||
become=None,
|
|
||||||
become_method=None,
|
|
||||||
become_user=None,
|
|
||||||
check=False,
|
|
||||||
passwords=None,
|
|
||||||
extra_vars=None,
|
|
||||||
private_key_file=None,
|
|
||||||
gather_facts='no'):
|
|
||||||
|
|
||||||
self.pattern = ''
|
|
||||||
self.variable_manager = VariableManager()
|
|
||||||
self.loader = DataLoader()
|
|
||||||
self.gather_facts = gather_facts
|
|
||||||
self.results_callback = AdHocRunner.results_callback_class()
|
|
||||||
self.options = self.Options(
|
|
||||||
connection=connection_type,
|
|
||||||
timeout=timeout,
|
|
||||||
module_path=module_path,
|
|
||||||
forks=forks,
|
|
||||||
become=become,
|
|
||||||
become_method=become_method,
|
|
||||||
become_user=become_user,
|
|
||||||
check=check,
|
|
||||||
remote_user=remote_user,
|
|
||||||
extra_vars=extra_vars or [],
|
|
||||||
private_key_file=private_key_file,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.variable_manager.extra_vars = load_extra_vars(self.loader,
|
if executor._tqm:
|
||||||
options=self.options)
|
executor._tqm._stdout_callback = self.results_callback
|
||||||
self.variable_manager.options_vars = load_options_vars(self.options)
|
executor.run()
|
||||||
self.passwords = passwords or {}
|
executor._tqm.cleanup()
|
||||||
self.inventory = JMSInventory(hosts)
|
return self.results_callback.output
|
||||||
self.variable_manager.set_inventory(self.inventory)
|
|
||||||
self.tasks = []
|
|
||||||
self.play_source = None
|
class AdHocRunner:
|
||||||
self.play = None
|
"""
|
||||||
self.runner = None
|
ADHoc Runner接口
|
||||||
|
"""
|
||||||
|
results_callback_class = AdHocResultCallback
|
||||||
|
inventory_class = JMSInventory
|
||||||
|
loader_class = DataLoader
|
||||||
|
variable_manager_class = VariableManager
|
||||||
|
options = get_default_options()
|
||||||
|
default_options = get_default_options()
|
||||||
|
|
||||||
|
def __init__(self, hosts, options=None):
|
||||||
|
if options:
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
self.pattern = ''
|
||||||
|
self.loader = DataLoader()
|
||||||
|
self.inventory = self.inventory_class(hosts)
|
||||||
|
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_module_args(module_name, module_args=''):
|
def check_module_args(module_name, module_args=''):
|
||||||
if module_name in C.MODULE_REQUIRE_ARGS and not module_args:
|
if module_name in C.MODULE_REQUIRE_ARGS and not module_args:
|
||||||
err = "No argument passed to '%s' module." % module_name
|
err = "No argument passed to '%s' module." % module_name
|
||||||
print(err)
|
raise AnsibleError(err)
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def run(self, task_tuple, pattern='all', task_name='Ansible Ad-hoc'):
|
def check_pattern(self, pattern):
|
||||||
"""
|
if not self.inventory.list_hosts("all"):
|
||||||
:param task_tuple: (('shell', 'ls'), ('ping', ''))
|
raise AnsibleError("Inventory is empty.")
|
||||||
:param pattern:
|
|
||||||
:param task_name:
|
if not self.inventory.list_hosts(pattern):
|
||||||
:return:
|
raise AnsibleError(
|
||||||
"""
|
"pattern: %s dose not match any hosts." % pattern
|
||||||
for module, args in task_tuple:
|
|
||||||
if not self.check_module_args(module, args):
|
|
||||||
return
|
|
||||||
self.tasks.append(
|
|
||||||
dict(action=dict(
|
|
||||||
module=module,
|
|
||||||
args=args,
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.play_source = dict(
|
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
|
||||||
name=task_name,
|
"""
|
||||||
|
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
|
||||||
|
:param pattern: all, *, or others
|
||||||
|
:param play_name: The play name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
results_callback = self.results_callback_class()
|
||||||
|
clean_tasks = []
|
||||||
|
for task in tasks:
|
||||||
|
self.check_module_args(task['action']['module'], task['action'].get('args'))
|
||||||
|
clean_tasks.append(task)
|
||||||
|
|
||||||
|
play_source = dict(
|
||||||
|
name=play_name,
|
||||||
hosts=pattern,
|
hosts=pattern,
|
||||||
gather_facts=self.gather_facts,
|
gather_facts=gather_facts,
|
||||||
tasks=self.tasks
|
tasks=clean_tasks
|
||||||
)
|
)
|
||||||
|
|
||||||
self.play = Play().load(
|
play = Play().load(
|
||||||
self.play_source,
|
play_source,
|
||||||
variable_manager=self.variable_manager,
|
variable_manager=self.variable_manager,
|
||||||
loader=self.loader,
|
loader=self.loader,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.runner = TaskQueueManager(
|
tqm = TaskQueueManager(
|
||||||
inventory=self.inventory,
|
inventory=self.inventory,
|
||||||
variable_manager=self.variable_manager,
|
variable_manager=self.variable_manager,
|
||||||
loader=self.loader,
|
loader=self.loader,
|
||||||
options=self.options,
|
options=self.options,
|
||||||
passwords=self.passwords,
|
stdout_callback=results_callback,
|
||||||
stdout_callback=self.results_callback,
|
passwords=self.options.passwords,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.inventory.list_hosts("all"):
|
|
||||||
raise AnsibleError("Inventory is empty.")
|
|
||||||
|
|
||||||
if not self.inventory.list_hosts(self.pattern):
|
|
||||||
raise AnsibleError(
|
|
||||||
"pattern: %s dose not match any hosts." % self.pattern)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.runner.run(self.play)
|
tqm.run(play)
|
||||||
|
return results_callback
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(e)
|
raise AnsibleError(e)
|
||||||
else:
|
|
||||||
logger.debug(self.results_callback.result_q)
|
|
||||||
return self.results_callback.result_q
|
|
||||||
finally:
|
finally:
|
||||||
if self.runner:
|
tqm.cleanup()
|
||||||
self.runner.cleanup()
|
self.loader.cleanup_all_tmp_files()
|
||||||
if self.loader:
|
|
||||||
self.loader.cleanup_all_tmp_files()
|
|
||||||
|
|
||||||
def clean_result(self):
|
|
||||||
"""
|
|
||||||
:return: {
|
|
||||||
"success": ['hostname',],
|
|
||||||
"failed": [('hostname', 'msg'), {}],
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
result = {'success': [], 'failed': []}
|
|
||||||
for host in self.results_callback.result_q['contacted']:
|
|
||||||
result['success'].append(host)
|
|
||||||
|
|
||||||
for host, msgs in self.results_callback.result_q['dark'].items():
|
|
||||||
msg = '\n'.join(['{} {}: {}'.format(
|
|
||||||
msg.get('module_stdout', ''),
|
|
||||||
msg.get('invocation', {}).get('module_name'),
|
|
||||||
msg.get('msg', '')) for msg in msgs])
|
|
||||||
result['failed'].append((host, msg))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def test_run():
|
class CommandRunner(AdHocRunner):
|
||||||
assets = [
|
results_callback_class = CommandResultCallback
|
||||||
{
|
modules_choices = ('shell', 'raw', 'command', 'script')
|
||||||
"hostname": "192.168.244.129",
|
|
||||||
"ip": "192.168.244.129",
|
|
||||||
"port": 22,
|
|
||||||
"username": "root",
|
|
||||||
"password": "redhat",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
task_tuple = (('shell', 'ls'),)
|
|
||||||
hoc = AdHocRunner(hosts=assets)
|
|
||||||
hoc.results_callback = CommandResultCallback()
|
|
||||||
ret = hoc.run(task_tuple)
|
|
||||||
print(ret)
|
|
||||||
|
|
||||||
#play = PlayBookRunner(assets, playbook_path='/tmp/some.yml')
|
def execute(self, cmd, pattern, module=None):
|
||||||
"""
|
if module and module not in self.modules_choices:
|
||||||
# /tmp/some.yml
|
raise AnsibleError("Module should in {}".format(self.modules_choices))
|
||||||
---
|
else:
|
||||||
- name: Test the plabybook API.
|
module = "shell"
|
||||||
hosts: all
|
|
||||||
remote_user: root
|
|
||||||
gather_facts: yes
|
|
||||||
tasks:
|
|
||||||
- name: exec uptime
|
|
||||||
shell: uptime
|
|
||||||
"""
|
|
||||||
#play.run()
|
|
||||||
|
|
||||||
|
tasks = [
|
||||||
|
{"action": {"module": module, "args": cmd}}
|
||||||
|
]
|
||||||
|
hosts = self.inventory.get_hosts(pattern=pattern)
|
||||||
|
name = "Run command {} on {}".format(cmd, ", ".join([host.name for host in hosts]))
|
||||||
|
return self.run(tasks, pattern, play_name=name)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
test_run()
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.insert(0, '../..')
|
||||||
|
from ops.ansible.inventory import JMSInventory
|
||||||
|
|
||||||
|
|
||||||
|
class TestJMSInventory(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
host_list = [{
|
||||||
|
"hostname": "testserver1",
|
||||||
|
"ip": "102.1.1.1",
|
||||||
|
"port": 22,
|
||||||
|
"username": "root",
|
||||||
|
"password": "password",
|
||||||
|
"private_key": "/tmp/private_key",
|
||||||
|
"become": {
|
||||||
|
"method": "sudo",
|
||||||
|
"user": "root",
|
||||||
|
"pass": None,
|
||||||
|
},
|
||||||
|
"groups": ["group1", "group2"],
|
||||||
|
"vars": {"sexy": "yes"},
|
||||||
|
}, {
|
||||||
|
"hostname": "testserver2",
|
||||||
|
"ip": "8.8.8.8",
|
||||||
|
"port": 2222,
|
||||||
|
"username": "root",
|
||||||
|
"password": "password",
|
||||||
|
"private_key": "/tmp/private_key",
|
||||||
|
"become": {
|
||||||
|
"method": "su",
|
||||||
|
"user": "root",
|
||||||
|
"pass": "123",
|
||||||
|
},
|
||||||
|
"groups": ["group3", "group4"],
|
||||||
|
"vars": {"love": "yes"},
|
||||||
|
}]
|
||||||
|
|
||||||
|
self.inventory = JMSInventory(host_list=host_list)
|
||||||
|
|
||||||
|
def test_hosts(self):
|
||||||
|
print("#"*10 + "Hosts" + "#"*10)
|
||||||
|
for host in self.inventory.hosts:
|
||||||
|
print(host)
|
||||||
|
|
||||||
|
def test_groups(self):
|
||||||
|
print("#" * 10 + "Groups" + "#" * 10)
|
||||||
|
for group in self.inventory.groups:
|
||||||
|
print(group)
|
||||||
|
|
||||||
|
def test_group_all(self):
|
||||||
|
print("#" * 10 + "all group hosts" + "#" * 10)
|
||||||
|
group = self.inventory.get_group('all')
|
||||||
|
print(group.hosts)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -0,0 +1,54 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, "../..")
|
||||||
|
|
||||||
|
from ops.ansible.runner import AdHocRunner, CommandRunner
|
||||||
|
|
||||||
|
|
||||||
|
class TestAdHocRunner(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
host_data = [
|
||||||
|
{
|
||||||
|
"hostname": "testserver",
|
||||||
|
"ip": "192.168.244.168",
|
||||||
|
"port": 22,
|
||||||
|
"username": "root",
|
||||||
|
"password": "redhat",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.runner = AdHocRunner(hosts=host_data)
|
||||||
|
|
||||||
|
def test_run(self):
|
||||||
|
tasks = [
|
||||||
|
{"action": {"module": "shell", "args": "ls"}},
|
||||||
|
{"action": {"module": "shell", "args": "whoami"}},
|
||||||
|
]
|
||||||
|
ret = self.runner.run(tasks, "all")
|
||||||
|
print(ret.results_summary)
|
||||||
|
print(ret.results_raw)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandRunner(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
host_data = [
|
||||||
|
{
|
||||||
|
"hostname": "testserver",
|
||||||
|
"ip": "192.168.244.168",
|
||||||
|
"port": 22,
|
||||||
|
"username": "root",
|
||||||
|
"password": "redhat",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
self.runner = CommandRunner(hosts=host_data)
|
||||||
|
|
||||||
|
def test_execute(self):
|
||||||
|
res = self.runner.execute('ls', 'all')
|
||||||
|
print(res.results_command)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -4,12 +4,12 @@
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from .hands import IsSuperUser
|
from .hands import IsSuperUser
|
||||||
from .models import Task
|
from .models import Playbook
|
||||||
from .serializers import TaskSerializer
|
from .serializers import TaskSerializer
|
||||||
|
|
||||||
|
|
||||||
class TaskViewSet(viewsets.ModelViewSet):
|
class TaskViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Task.objects.all()
|
queryset = Playbook.objects.all()
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsSuperUser,)
|
||||||
|
|
||||||
|
|
|
@ -7,40 +7,32 @@ import uuid
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from assets.models import Asset
|
|
||||||
|
|
||||||
__all__ = ["Task"]
|
__all__ = ["Playbook"]
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Task(models.Model):
|
class AdHoc(models.Model):
|
||||||
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
|
||||||
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time'))
|
tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'module': '', 'args': ''}, ]
|
||||||
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time'))
|
hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts')) # Asset inventory may be change
|
||||||
timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True)
|
pattern = models.CharField(max_length=64, default='all', verbose_name=_('Playbook run pattern'))
|
||||||
is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
|
|
||||||
is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
|
|
||||||
assets = models.TextField(blank=True, null=True, verbose_name=_('Assets id')) # Asset inventory may be change
|
|
||||||
_modules_args = models.TextField(blank=True, null=True, verbose_name=_('Task module and args json format'))
|
|
||||||
pattern = models.CharField(max_length=64, default='all', verbose_name=_('Task run pattern'))
|
|
||||||
result = models.TextField(blank=True, null=True, verbose_name=_('Task raw result'))
|
|
||||||
summary = models.TextField(blank=True, null=True, verbose_name=_('Task summary'))
|
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return "%s" % self.uuid
|
return "%s" % self.name
|
||||||
|
|
||||||
@property
|
def get_hosts_mapped_assets(self):
|
||||||
def total_assets(self):
|
from assets.utils import get_assets_by_id_list
|
||||||
assets_id = [i for i in self.assets.split(',') if i.isdigit()]
|
assets_id = [i for i in self.hosts.split(',')]
|
||||||
assets = Asset.objects.filter(id__in=assets_id)
|
assets = get_assets_by_id_list(assets_id)
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_json(self):
|
def inventory(self):
|
||||||
return [asset._to_secret_json() for asset in self.total_assets]
|
return [asset._to_secret_json() for asset in self.get_hosts_mapped_assets()]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def module_args(self):
|
def module_args(self):
|
||||||
|
@ -57,3 +49,12 @@ class Task(models.Model):
|
||||||
self._modules_args = json.dumps(module_args_)
|
self._modules_args = json.dumps(module_args_)
|
||||||
|
|
||||||
|
|
||||||
|
class History(models.Model):
|
||||||
|
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time'))
|
||||||
|
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time'))
|
||||||
|
timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True)
|
||||||
|
is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
|
||||||
|
is_success = models.BooleanField(default=False, verbose_name=_('Is success'))
|
||||||
|
result = models.TextField(blank=True, null=True, verbose_name=_('Playbook raw result'))
|
||||||
|
summary = models.TextField(blank=True, null=True, verbose_name=_('Playbook summary'))
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Task
|
from .models import Playbook
|
||||||
|
|
||||||
|
|
||||||
class TaskSerializer(serializers.ModelSerializer):
|
class TaskSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Task
|
model = Playbook
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ logger = get_logger(__file__)
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def rerun_task(task_id):
|
def rerun_task(task_id):
|
||||||
from .models import Task
|
from .models import Playbook
|
||||||
record = Task.objects.get(uuid=task_id)
|
record = Playbook.objects.get(uuid=task_id)
|
||||||
assets = record.assets_json
|
assets = record.assets_json
|
||||||
task_tuple = record.module_args
|
task_tuple = record.module_args
|
||||||
pattern = record.pattern
|
pattern = record.pattern
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
@ -41,16 +42,16 @@ def run_AdHoc(task_tuple, assets,
|
||||||
|
|
||||||
runner = AdHocRunner(assets)
|
runner = AdHocRunner(assets)
|
||||||
if record:
|
if record:
|
||||||
from .models import Task
|
from .models import Playbook
|
||||||
if not Task.objects.filter(uuid=task_id):
|
if not Playbook.objects.filter(uuid=task_id):
|
||||||
record = Task(uuid=task_id,
|
record = Playbook(uuid=task_id,
|
||||||
name=task_name,
|
name=task_name,
|
||||||
assets=','.join(str(asset['id']) for asset in assets),
|
assets=','.join(str(asset['id']) for asset in assets),
|
||||||
module_args=task_tuple,
|
module_args=task_tuple,
|
||||||
pattern=pattern)
|
pattern=pattern)
|
||||||
record.save()
|
record.save()
|
||||||
else:
|
else:
|
||||||
record = Task.objects.get(uuid=task_id)
|
record = Playbook.objects.get(uuid=task_id)
|
||||||
record.date_start = timezone.now()
|
record.date_start = timezone.now()
|
||||||
record.date_finished = None
|
record.date_finished = None
|
||||||
record.timedelta = None
|
record.timedelta = None
|
||||||
|
@ -76,3 +77,14 @@ def run_AdHoc(task_tuple, assets,
|
||||||
record.is_success = False
|
record.is_success = False
|
||||||
record.save()
|
record.save()
|
||||||
return summary, result
|
return summary, result
|
||||||
|
|
||||||
|
|
||||||
|
UUID_PATTERN = re.compile(r'[0-9a-zA-Z\-]{36}')
|
||||||
|
|
||||||
|
|
||||||
|
def is_uuid(s):
|
||||||
|
if UUID_PATTERN.match(s):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ from django.views.generic import ListView, DetailView, View
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.shortcuts import redirect, reverse
|
from django.shortcuts import redirect, reverse
|
||||||
|
|
||||||
from .models import Task
|
from .models import Playbook
|
||||||
from ops.tasks import rerun_task
|
from ops.tasks import rerun_task
|
||||||
|
|
||||||
|
|
||||||
class TaskListView(ListView):
|
class TaskListView(ListView):
|
||||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
model = Task
|
model = Playbook
|
||||||
ordering = ('-date_start',)
|
ordering = ('-date_start',)
|
||||||
context_object_name = 'task_list'
|
context_object_name = 'task_list'
|
||||||
template_name = 'ops/task_list.html'
|
template_name = 'ops/task_list.html'
|
||||||
|
@ -53,7 +53,7 @@ class TaskListView(ListView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': 'Ops',
|
'app': 'Ops',
|
||||||
'action': 'Task record list',
|
'action': 'Playbook record list',
|
||||||
'date_from': self.date_from_s,
|
'date_from': self.date_from_s,
|
||||||
'date_to': self.date_to_s,
|
'date_to': self.date_to_s,
|
||||||
'keyword': self.keyword,
|
'keyword': self.keyword,
|
||||||
|
@ -63,13 +63,13 @@ class TaskListView(ListView):
|
||||||
|
|
||||||
|
|
||||||
class TaskDetailView(DetailView):
|
class TaskDetailView(DetailView):
|
||||||
model = Task
|
model = Playbook
|
||||||
template_name = 'ops/task_detail.html'
|
template_name = 'ops/task_detail.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
'app': 'Ops',
|
'app': 'Ops',
|
||||||
'action': 'Task record detail',
|
'action': 'Playbook record detail',
|
||||||
'results': json.loads(self.object.summary or '{}'),
|
'results': json.loads(self.object.summary or '{}'),
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
|
<i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="nav nav-second-level">
|
<ul class="nav nav-second-level">
|
||||||
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task' %}</a></li>
|
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Playbook' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ class Task(models.Model):
|
||||||
|
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, choices=NAME_CHOICES, verbose_name=_("Name"))
|
name = models.CharField(max_length=128, choices=NAME_CHOICES, verbose_name=_("Name"))
|
||||||
args = models.CharField(max_length=1024, verbose_name=_("Task Args"))
|
args = models.CharField(max_length=1024, verbose_name=_("Playbook Args"))
|
||||||
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.CASCADE)
|
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.CASCADE)
|
||||||
is_finished = models.BooleanField(default=False)
|
is_finished = models.BooleanField(default=False)
|
||||||
date_created = models.DateTimeField(auto_now_add=True)
|
date_created = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
Loading…
Reference in New Issue