Commit e57121a7 authored by ibuler's avatar ibuler

[Feature] 优化Ops ansible api

parent ec8106e4
# ~*~ 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): ...@@ -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" ...@@ -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" ...@@ -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
......
# ~*~ 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": []},
def gather_result(self, n, res): # "unreachable: {"hostname": []},
if res._host.name in self.result_q[n]: # "skipped": {"hostname": []},
self.result_q[n][res._host.name].append(res._result) # }
# 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: 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): def runner_on_ok(self, host, res):
self.gather_result("contacted", result) self.gather_result("ok", host, res)
def v2_runner_on_failed(self, result, ignore_errors=False): 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_unreachable(self, result): def runner_on_unreachable(self, host, res):
self.gather_result("dark", result) self.gather_result("unreachable", host, res)
def v2_runner_on_skipped(self, result): def runner_on_skipped(self, host, item=None):
self.gather_result("dark", result) 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): class CommandResultCallback(AdHocResultCallback):
pass 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):
......
# -*- coding: utf-8 -*-
#
class AnsibleError(Exception):
pass
# ~*~ 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): ...@@ -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)
def __repr__(self):
return self.name
class JMSInventory(Inventory):
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>,
"pass": <pass>,
"key": <sshKey>,
"groups": ['group1', 'group2'],
"other_host_var": <other>},
{...},
]
: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: 验证输入 :return: None
# 创建Ansible Group,如果没有则创建default组 """
ungrouped = Group('ungrouped') group_all = self.get_group('all')
all = Group('all') ungrouped = self.get_group('ungrouped')
all.add_child_group(ungrouped)
self.groups = dict(all=all, ungrouped=ungrouped)
for asset in host_list: for host_data in self.host_list:
host = JMSHost(asset=asset) host = self.host_manager_class(host_data=host_data)
asset_groups = asset.get('groups') self.hosts[host_data['hostname']] = host
if asset_groups: groups_data = host_data.get('groups')
for group_name in asset_groups: if groups_data:
if group_name not in self.groups: for group_name in groups_data:
group = self.get_group(group_name)
if group is None:
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 @@ ...@@ -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__)
# Jumpserver not use playbook Options = namedtuple('Options', [
class PlayBookRunner(object):
"""
用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
"""
Options = namedtuple('Options', [
'listtags', 'listtasks', 'listhosts', 'syntax', 'connection', 'listtags', 'listtasks', 'listhosts', 'syntax', 'connection',
'module_path', 'forks', 'remote_user', 'private_key_file', 'timeout', 'module_path', 'forks', 'remote_user', 'private_key_file', 'timeout',
'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args', 'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args',
'scp_extra_args', 'become', 'become_method', 'become_user', 'scp_extra_args', 'become', 'become_method', 'become_user',
'verbosity', 'check', 'extra_vars']) 'verbosity', 'check', 'extra_vars', 'playbook_path', 'passwords',
'diff',
])
def __init__(self, def get_default_options():
hosts=None, options = Options(
playbook_path=None,
forks=C.DEFAULT_FORKS,
listtags=False, listtags=False,
listtasks=False, listtasks=False,
listhosts=False, listhosts=False,
syntax=False, syntax=False,
module_path=None, timeout=60,
connection='ssh',
module_path='',
forks=10,
remote_user='root', remote_user='root',
timeout=C.DEFAULT_TIMEOUT, private_key_file=None,
ssh_common_args=None, ssh_common_args="",
ssh_extra_args=None, ssh_extra_args="",
sftp_extra_args=None, sftp_extra_args="",
scp_extra_args=None, scp_extra_args="",
become=True, become=None,
become_method=None, become_method=None,
become_user="root", become_user=None,
verbosity=None, verbosity=None,
extra_vars=None, extra_vars=[],
connection_type="ssh", check=False,
playbook_path='/etc/ansible/',
passwords=None, passwords=None,
private_key_file=None, diff=False,
check=False): )
return options
# Jumpserver not use playbook
class PlayBookRunner:
"""
用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
"""
# 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 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(
self.variable_manager.set_inventory(self.inventory) "Not Found the playbook file: {}.".format(self.options.playbook_path)
)
if not self.inventory.list_hosts('all'):
raise AnsibleError('Inventory is empty')
# 初始化playbook的executor def run(self):
self.runner = PlaybookExecutor( executor = 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: if executor._tqm:
self.runner._tqm._stdout_callback = self.callbackmodule executor._tqm._stdout_callback = self.results_callback
executor.run()
def run(self): executor._tqm.cleanup()
if not self.inventory.list_hosts('all'): return self.results_callback.output
raise AnsibleError('Inventory is empty')
self.runner.run()
self.runner._tqm.cleanup()
return self.callbackmodule.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 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, def __init__(self, hosts, options=None):
hosts=C.DEFAULT_HOST_LIST, if options:
forks=C.DEFAULT_FORKS, # 5 self.options = options
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.pattern = ''
self.variable_manager = VariableManager()
self.loader = DataLoader() self.loader = DataLoader()
self.gather_facts = gather_facts self.inventory = self.inventory_class(hosts)
self.results_callback = AdHocRunner.results_callback_class() self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
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
@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 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 tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
:param pattern: :param pattern: all, *, or others
:param task_name: :param play_name: The play name
:return: :return:
""" """
for module, args in task_tuple: results_callback = self.results_callback_class()
if not self.check_module_args(module, args): clean_tasks = []
return for task in tasks:
self.tasks.append( self.check_module_args(task['action']['module'], task['action'].get('args'))
dict(action=dict( clean_tasks.append(task)
module=module,
args=args, play_source = dict(
)) name=play_name,
)
self.play_source = dict(
name=task_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()
if self.loader:
self.loader.cleanup_all_tmp_files() 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(): class CommandRunner(AdHocRunner):
msg = '\n'.join(['{} {}: {}'.format( results_callback_class = CommandResultCallback
msg.get('module_stdout', ''), modules_choices = ('shell', 'raw', 'command', 'script')
msg.get('invocation', {}).get('module_name'),
msg.get('msg', '')) for msg in msgs])
result['failed'].append((host, msg))
return result
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"
def test_run(): tasks = [
assets = [ {"action": {"module": module, "args": cmd}}
{
"hostname": "192.168.244.129",
"ip": "192.168.244.129",
"port": 22,
"username": "root",
"password": "redhat",
},
] ]
task_tuple = (('shell', 'ls'),) hosts = self.inventory.get_hosts(pattern=pattern)
hoc = AdHocRunner(hosts=assets) name = "Run command {} on {}".format(cmd, ", ".join([host.name for host in hosts]))
hoc.results_callback = CommandResultCallback() return self.run(tasks, pattern, play_name=name)
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()
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 @@ ...@@ -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 ...@@ -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): ...@@ -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 @@ ...@@ -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__) ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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 ...@@ -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): ...@@ -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): ...@@ -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 @@ ...@@ -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): ...@@ -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)
......
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