mirror of https://github.com/jumpserver/jumpserver
				
				
				
			[Feature] 优化Ops ansible api
							parent
							
								
									ec8106e43d
								
							
						
					
					
						commit
						e57121a780
					
				| 
						 | 
				
			
			@ -1,9 +1,10 @@
 | 
			
		|||
# ~*~ 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,65 +1,76 @@
 | 
			
		|||
# ~*~ 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)
 | 
			
		||||
        # 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, n, res):
 | 
			
		||||
        if res._host.name in self.result_q[n]:
 | 
			
		||||
            self.result_q[n][res._host.name].append(res._result)
 | 
			
		||||
    def gather_result(self, t, host, res):
 | 
			
		||||
        if self.results_raw[t].get(host):
 | 
			
		||||
            self.results_raw[t][host].append(res)
 | 
			
		||||
        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):
 | 
			
		||||
        self.gather_result("contacted", result)
 | 
			
		||||
    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:
 | 
			
		||||
            dark[host].append(res)
 | 
			
		||||
            if host in contacted:
 | 
			
		||||
                contacted.remove(dark)
 | 
			
		||||
 | 
			
		||||
    def v2_runner_on_failed(self, result, ignore_errors=False):
 | 
			
		||||
        self.gather_result("dark", result)
 | 
			
		||||
    def runner_on_ok(self, host, res):
 | 
			
		||||
        self.gather_result("ok", host, res)
 | 
			
		||||
 | 
			
		||||
    def v2_runner_on_unreachable(self, result):
 | 
			
		||||
        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_skipped(self, result):
 | 
			
		||||
        self.gather_result("dark", result)
 | 
			
		||||
    def runner_on_unreachable(self, host, res):
 | 
			
		||||
        self.gather_result("unreachable", host, res)
 | 
			
		||||
 | 
			
		||||
    def v2_playbook_on_task_start(self, task, is_conditional):
 | 
			
		||||
        pass
 | 
			
		||||
    def runner_on_skipped(self, host, item=None):
 | 
			
		||||
        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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnsibleError(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
| 
						 | 
				
			
			@ -1,31 +1,52 @@
 | 
			
		|||
# ~*~ 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)
 | 
			
		||||
 | 
			
		||||
class JMSInventory(Inventory):
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return self.name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
    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": {},
 | 
			
		||||
          },
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        :return: 返回一个Ansible的inventory对象
 | 
			
		||||
        :return: None
 | 
			
		||||
        """
 | 
			
		||||
        group_all = self.get_group('all')
 | 
			
		||||
        ungrouped = self.get_group('ungrouped')
 | 
			
		||||
 | 
			
		||||
        # TODO: 验证输入
 | 
			
		||||
        # 创建Ansible Group,如果没有则创建default组
 | 
			
		||||
        ungrouped = Group('ungrouped')
 | 
			
		||||
        all = Group('all')
 | 
			
		||||
        all.add_child_group(ungrouped)
 | 
			
		||||
        self.groups = dict(all=all, ungrouped=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)
 | 
			
		||||
    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')
 | 
			
		||||
 | 
			
		||||
        self.variable_manager.set_inventory(self.inventory)
 | 
			
		||||
 | 
			
		||||
        # 初始化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
 | 
			
		||||
 | 
			
		||||
    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,
 | 
			
		||||
            passwords=self.passwords
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        if executor._tqm:
 | 
			
		||||
            executor._tqm._stdout_callback = self.results_callback
 | 
			
		||||
        executor.run()
 | 
			
		||||
        executor._tqm.cleanup()
 | 
			
		||||
        return self.results_callback.output
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AdHocRunner:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    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 run(self, task_tuple, pattern='all', task_name='Ansible Ad-hoc'):
 | 
			
		||||
        """
 | 
			
		||||
        :param task_tuple:  (('shell', 'ls'), ('ping', ''))
 | 
			
		||||
        :param pattern:
 | 
			
		||||
        :param task_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,
 | 
			
		||||
                ))
 | 
			
		||||
    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
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        self.play_source = dict(
 | 
			
		||||
            name=task_name,
 | 
			
		||||
    def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
 | 
			
		||||
        """
 | 
			
		||||
        :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,
 | 
			
		||||
            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()
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
            tqm.cleanup()
 | 
			
		||||
            self.loader.cleanup_all_tmp_files()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
class CommandRunner(AdHocRunner):
 | 
			
		||||
    results_callback_class = CommandResultCallback
 | 
			
		||||
    modules_choices = ('shell', 'raw', 'command', 'script')
 | 
			
		||||
 | 
			
		||||
    #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()
 | 
			
		||||
    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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 .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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue