Commit e57121a7 authored by ibuler's avatar ibuler

[Feature] 优化Ops ansible api

parent ec8106e4
# ~*~ coding: utf-8 ~*~
#
from ops.utils import run_AdHoc
from .models import Asset
def test_admin_user_connective_manual(asset):
from ops.utils import run_AdHoc
if not isinstance(asset, list):
asset = [asset]
task_tuple = (
......@@ -15,3 +16,6 @@ def test_admin_user_connective_manual(asset):
else:
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"
#: ops/models.py:27
msgid "Task module and args json format"
msgid "Playbook module and args json format"
msgstr ""
#: ops/models.py:28
msgid "Task run pattern"
msgid "Playbook run pattern"
msgstr ""
#: ops/models.py:29
msgid "Task raw result"
msgid "Playbook raw result"
msgstr ""
#: ops/models.py:30
msgid "Task summary"
msgid "Playbook summary"
msgstr ""
#: ops/templates/ops/task_detail.html:19
msgid "Task replay detail"
msgid "Playbook replay detail"
msgstr "任务记录详情"
#: ops/templates/ops/task_detail.html:62
......@@ -1669,7 +1669,7 @@ msgid "Job Center"
msgstr "作业中心"
#: templates/_nav.html:51
msgid "Task"
msgid "Playbook"
msgstr "任务"
#: templates/_nav.html:62
......
# ~*~ coding: utf-8 ~*~
from collections import defaultdict
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):
"""
AdHoc result Callback
"""
def __init__(self, display=None):
self.result_q = dict(contacted={}, dark={})
super(AdHocResultCallback, self).__init__(display)
def gather_result(self, n, res):
if res._host.name in self.result_q[n]:
self.result_q[n][res._host.name].append(res._result)
# result_raw example: {
# "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, t, host, res):
if self.results_raw[t].get(host):
self.results_raw[t][host].append(res)
else:
self.results_raw[t][host] = [res]
self.clean_result(t, host, res)
def clean_result(self, t, host, res):
contacted = self.results_summary["contacted"]
dark = self.results_summary["dark"]
if t in ("ok", "skipped") and host not in dark:
contacted.add(host)
else:
self.result_q[n][res._host.name] = [res._result]
dark[host].append(res)
if host in contacted:
contacted.remove(dark)
def v2_runner_on_ok(self, result):
self.gather_result("contacted", result)
def runner_on_ok(self, host, res):
self.gather_result("ok", host, res)
def v2_runner_on_failed(self, result, ignore_errors=False):
self.gather_result("dark", result)
def runner_on_failed(self, host, res, ignore_errors=False):
self.gather_result("failed", host, res)
def v2_runner_on_unreachable(self, result):
self.gather_result("dark", result)
def runner_on_unreachable(self, host, res):
self.gather_result("unreachable", host, res)
def v2_runner_on_skipped(self, result):
self.gather_result("dark", result)
def runner_on_skipped(self, host, item=None):
self.gather_result("skipped", host, item)
def v2_playbook_on_task_start(self, task, is_conditional):
pass
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):
......
# -*- coding: utf-8 -*-
#
class AnsibleError(Exception):
pass
# ~*~ coding: utf-8 ~*~
from ansible.inventory import Inventory, Host, Group
from ansible.vars import VariableManager
from ansible.inventory.group import Group
from ansible.inventory.host import Host
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.parsing.dataloader import DataLoader
class JMSHost(Host):
def __init__(self, asset):
self.asset = asset
self.name = name = asset.get('hostname') or asset.get('ip')
self.port = port = asset.get('port') or 22
super(JMSHost, self).__init__(name, port)
self.set_all_variable()
def __init__(self, host_data):
"""
初始化
:param host_data: {
"hostname": "",
"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):
asset = self.asset
self.set_variable('ansible_host', asset['ip'])
self.set_variable('ansible_port', asset['port'])
self.set_variable('ansible_user', asset['username'])
def __set_required_variables(self):
host_data = self.host_data
self.set_variable('ansible_host', host_data['ip'])
self.set_variable('ansible_port', host_data['port'])
self.set_variable('ansible_user', host_data['username'])
# 添加密码和秘钥
if asset.get('password'):
self.set_variable('ansible_ssh_pass', asset['password'])
if asset.get('private_key'):
self.set_variable('ansible_ssh_private_key_file', asset['private_key'])
if host_data.get('password'):
self.set_variable('ansible_ssh_pass', host_data['password'])
if host_data.get('private_key'):
self.set_variable('ansible_ssh_private_key_file', host_data['private_key'])
# 添加become支持
become = asset.get("become", False)
become = host_data.get("become", False)
if become:
self.set_variable("ansible_become", True)
self.set_variable("ansible_become_method", become.get('method', 'sudo'))
......@@ -34,58 +55,73 @@ class JMSHost(Host):
else:
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)
def __repr__(self):
return self.name
class JMSInventory(Inventory):
class JMSInventory(InventoryManager):
"""
提供生成Ansible inventory对象的方法
"""
loader_class = DataLoader
variable_manager_class = VariableManager
host_manager_class = JMSHost
def __init__(self, host_list=None):
if host_list is None:
host_list = []
assert isinstance(host_list, list)
self.host_list = host_list
self.loader = DataLoader()
self.variable_manager = VariableManager()
super(JMSInventory, self).__init__(self.loader, self.variable_manager,
host_list=host_list)
assert isinstance(host_list, list)
self.loader = self.loader_class()
self.variable_manager = self.variable_manager_class()
super().__init__(self.loader)
def parse_inventory(self, host_list):
"""用于生成动态构建Ansible Inventory.
self.host_list: [
{"name": "asset_name",
"ip": <ip>,
"port": <port>,
"user": <user>,
"pass": <pass>,
"key": <sshKey>,
"groups": ['group1', 'group2'],
"other_host_var": <other>},
{...},
]
def get_groups(self):
return self._inventory.groups
def get_group(self, name):
return self._inventory.groups.get(name, None)
:return: 返回一个Ansible的inventory对象
def parse_sources(self, cache=False):
"""
用于生成动态构建Ansible Inventory. super().__init__ 会自动调用
host_list: [{
"hostname": "",
"ip": "",
"port": "",
"username": "",
"password": "",
"private_key": "",
"become": {
"method": "",
"user": "",
"pass": "",
},
"groups": [],
"vars": {},
},
]
# TODO: 验证输入
# 创建Ansible Group,如果没有则创建default组
ungrouped = Group('ungrouped')
all = Group('all')
all.add_child_group(ungrouped)
self.groups = dict(all=all, ungrouped=ungrouped)
:return: None
"""
group_all = self.get_group('all')
ungrouped = self.get_group('ungrouped')
for asset in host_list:
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:
for host_data in self.host_list:
host = self.host_manager_class(host_data=host_data)
self.hosts[host_data['hostname']] = host
groups_data = host_data.get('groups')
if groups_data:
for group_name in groups_data:
group = self.get_group(group_name)
if group is None:
group = Group(group_name)
self.groups[group_name] = group
else:
group = self.groups[group_name]
self.add_group(group)
group.add_host(host)
else:
ungrouped.add_host(host)
all.add_host(host)
group_all.add_host(host)
......@@ -2,303 +2,233 @@
from __future__ import unicode_literals
import os
from collections import namedtuple, defaultdict
from collections import namedtuple
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.executor.playbook_executor import PlaybookExecutor
from ansible.playbook.play import Play
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 .callback import AdHocResultCallback, PlaybookResultCallBack, \
CommandResultCallback
from common.utils import get_logger
from .exceptions import AnsibleError
__all__ = ["AdHocRunner", "PlayBookRunner"]
C.HOST_KEY_CHECKING = False
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
class PlayBookRunner(object):
class PlayBookRunner:
"""
用于执行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,
hosts=None,
playbook_path=None,
forks=C.DEFAULT_FORKS,
listtags=False,
listtasks=False,
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):
# Default results callback
results_callback_class = PlaybookResultCallBack
inventory_class = JMSInventory
loader_class = DataLoader
variable_manager_class = VariableManager
options = get_default_options()
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
self.callbackmodule = PlaybookResultCallBack()
if playbook_path is None or not os.path.exists(playbook_path):
raise AnsibleError(
"Not Found the playbook file: %s." % playbook_path)
self.playbook_path = playbook_path
self.loader = DataLoader()
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.inventory = self.inventory_class(hosts)
self.loader = self.loader_class()
self.results_callback = self.results_callback_class()
self.playbook_path = options.playbook_path
self.variable_manager = self.variable_manager_class(
loader=self.loader, inventory=self.inventory
)
self.passwords = options.passwords
self.__check()
self.variable_manager.extra_vars = load_extra_vars(loader=self.loader,
options=self.options)
self.variable_manager.options_vars = load_options_vars(self.options)
self.variable_manager.set_inventory(self.inventory)
def __check(self):
if self.options.playbook_path is None or \
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')
# 初始化playbook的executor
self.runner = PlaybookExecutor(
def run(self):
executor = PlaybookExecutor(
playbooks=[self.playbook_path],
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords)
if self.runner._tqm:
self.runner._tqm._stdout_callback = self.callbackmodule
passwords=self.passwords
)
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
if executor._tqm:
executor._tqm._stdout_callback = self.results_callback
executor.run()
executor._tqm.cleanup()
return self.results_callback.output
class AdHocRunner(object):
class AdHocRunner:
"""
ADHoc接口
ADHoc Runner接口
"""
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
inventory_class = JMSInventory
loader_class = DataLoader
variable_manager_class = VariableManager
options = get_default_options()
default_options = get_default_options()
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'):
def __init__(self, hosts, options=None):
if options:
self.options = options
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,
options=self.options)
self.variable_manager.options_vars = load_options_vars(self.options)
self.passwords = passwords or {}
self.inventory = JMSInventory(hosts)
self.variable_manager.set_inventory(self.inventory)
self.tasks = []
self.play_source = None
self.play = None
self.runner = None
self.inventory = self.inventory_class(hosts)
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
@staticmethod
def check_module_args(module_name, module_args=''):
if module_name in C.MODULE_REQUIRE_ARGS and not module_args:
err = "No argument passed to '%s' module." % module_name
print(err)
return False
return True
raise AnsibleError(err)
def check_pattern(self, pattern):
if not self.inventory.list_hosts("all"):
raise AnsibleError("Inventory is empty.")
if not self.inventory.list_hosts(pattern):
raise AnsibleError(
"pattern: %s dose not match any hosts." % pattern
)
def run(self, task_tuple, pattern='all', task_name='Ansible Ad-hoc'):
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
"""
:param task_tuple: (('shell', 'ls'), ('ping', ''))
:param pattern:
:param task_name:
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
:param pattern: all, *, or others
:param play_name: The play name
:return:
"""
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(
name=task_name,
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,
gather_facts=self.gather_facts,
tasks=self.tasks
gather_facts=gather_facts,
tasks=clean_tasks
)
self.play = Play().load(
self.play_source,
play = Play().load(
play_source,
variable_manager=self.variable_manager,
loader=self.loader,
)
self.runner = TaskQueueManager(
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
stdout_callback=self.results_callback,
stdout_callback=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:
self.runner.run(self.play)
tqm.run(play)
return results_callback
except Exception as e:
logger.warning(e)
else:
logger.debug(self.results_callback.result_q)
return self.results_callback.result_q
raise AnsibleError(e)
finally:
if self.runner:
self.runner.cleanup()
if self.loader:
self.loader.cleanup_all_tmp_files()
tqm.cleanup()
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():
assets = [
{
"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')
"""
# /tmp/some.yml
---
- name: Test the plabybook API.
hosts: all
remote_user: root
gather_facts: yes
tasks:
- name: exec uptime
shell: uptime
"""
#play.run()
class CommandRunner(AdHocRunner):
results_callback_class = CommandResultCallback
modules_choices = ('shell', 'raw', 'command', 'script')
def execute(self, cmd, pattern, module=None):
if module and module not in self.modules_choices:
raise AnsibleError("Module should in {}".format(self.modules_choices))
else:
module = "shell"
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()
# -*- 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()
# -*- 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 .hands import IsSuperUser
from .models import Task
from .models import Playbook
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
queryset = Playbook.objects.all()
serializer_class = TaskSerializer
permission_classes = (IsSuperUser,)
......@@ -7,40 +7,32 @@ import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from assets.models import Asset
__all__ = ["Task"]
__all__ = ["Playbook"]
logger = logging.getLogger(__name__)
class Task(models.Model):
class AdHoc(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
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'))
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'))
tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'module': '', 'args': ''}, ]
hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts')) # Asset inventory may be change
pattern = models.CharField(max_length=64, default='all', verbose_name=_('Playbook run pattern'))
def __unicode__(self):
return "%s" % self.uuid
def __str__(self):
return "%s" % self.name
@property
def total_assets(self):
assets_id = [i for i in self.assets.split(',') if i.isdigit()]
assets = Asset.objects.filter(id__in=assets_id)
def get_hosts_mapped_assets(self):
from assets.utils import get_assets_by_id_list
assets_id = [i for i in self.hosts.split(',')]
assets = get_assets_by_id_list(assets_id)
return assets
@property
def assets_json(self):
return [asset._to_secret_json() for asset in self.total_assets]
def inventory(self):
return [asset._to_secret_json() for asset in self.get_hosts_mapped_assets()]
@property
def module_args(self):
......@@ -57,3 +49,12 @@ class Task(models.Model):
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 rest_framework import serializers
from .models import Task
from .models import Playbook
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
model = Playbook
fields = '__all__'
......@@ -12,8 +12,8 @@ logger = get_logger(__file__)
@shared_task
def rerun_task(task_id):
from .models import Task
record = Task.objects.get(uuid=task_id)
from .models import Playbook
record = Playbook.objects.get(uuid=task_id)
assets = record.assets_json
task_tuple = record.module_args
pattern = record.pattern
......
......@@ -3,6 +3,7 @@
from __future__ import absolute_import, unicode_literals
import json
import re
import time
import uuid
......@@ -41,16 +42,16 @@ def run_AdHoc(task_tuple, assets,
runner = AdHocRunner(assets)
if record:
from .models import Task
if not Task.objects.filter(uuid=task_id):
record = Task(uuid=task_id,
name=task_name,
assets=','.join(str(asset['id']) for asset in assets),
module_args=task_tuple,
pattern=pattern)
from .models import Playbook
if not Playbook.objects.filter(uuid=task_id):
record = Playbook(uuid=task_id,
name=task_name,
assets=','.join(str(asset['id']) for asset in assets),
module_args=task_tuple,
pattern=pattern)
record.save()
else:
record = Task.objects.get(uuid=task_id)
record = Playbook.objects.get(uuid=task_id)
record.date_start = timezone.now()
record.date_finished = None
record.timedelta = None
......@@ -76,3 +77,14 @@ def run_AdHoc(task_tuple, assets,
record.is_success = False
record.save()
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.shortcuts import redirect, reverse
from .models import Task
from .models import Playbook
from ops.tasks import rerun_task
class TaskListView(ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Task
model = Playbook
ordering = ('-date_start',)
context_object_name = 'task_list'
template_name = 'ops/task_list.html'
......@@ -53,7 +53,7 @@ class TaskListView(ListView):
def get_context_data(self, **kwargs):
context = {
'app': 'Ops',
'action': 'Task record list',
'action': 'Playbook record list',
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'keyword': self.keyword,
......@@ -63,13 +63,13 @@ class TaskListView(ListView):
class TaskDetailView(DetailView):
model = Task
model = Playbook
template_name = 'ops/task_detail.html'
def get_context_data(self, **kwargs):
context = {
'app': 'Ops',
'action': 'Task record detail',
'action': 'Playbook record detail',
'results': json.loads(self.object.summary or '{}'),
}
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>
</a>
<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>
</li>
......
......@@ -113,7 +113,7 @@ class Task(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
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)
is_finished = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment