From eb5f0fcf684b23f30fd422a9ab393a7fa40be54a Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 27 Oct 2016 15:20:16 +0800 Subject: [PATCH] =?UTF-8?q?[future]=20=E5=9C=A8=E4=B8=BB=E6=9C=BA=E5=B1=82?= =?UTF-8?q?=E9=9D=A2=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8Dsudo,=20=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E5=9B=9E=E8=B0=83=E7=BB=93=E6=9E=9C=E7=9A=84=E5=88=86?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/ansible_api.py | 138 +++++++++++++++++++++++++++------------- apps/ops/models.py | 33 +++++++++- requirements.txt | 2 +- 3 files changed, 126 insertions(+), 47 deletions(-) diff --git a/apps/ops/ansible_api.py b/apps/ops/ansible_api.py index 115a06dbe..7b45b1acd 100644 --- a/apps/ops/ansible_api.py +++ b/apps/ops/ansible_api.py @@ -22,11 +22,11 @@ class Config(object): """Ansible运行时配置类, 用于初始化Ansible. """ def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None, - forks=None, ask_vault_pass=None, vault_password_files=None, new_vault_password_file=None, - output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=None, ask_su_pass=None, - sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=None, - ask_pass=None, private_key_file=None, remote_user=None, connection=None, timeout=None, ssh_common_args=None, - sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=None, + forks=None, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None, + output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False, + sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False, + ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=None, ssh_common_args=None, + sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False, syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None): self.verbosity = verbosity self.inventory = inventory @@ -86,46 +86,75 @@ class InventoryMixin(object): def gen_inventory(self): """用于生成动态构建Ansible Inventory. + self.hosts: [ + {"host": , + "port": , + "user": , + "pass": , + "key": , + "group": + "other_host_var": }, + {...}, + ] + self.group_vars: { + "groupName1": {"var1": , "var2": , ...}, + "groupName2": {"var1": , "var2": , ...}, + } :return: 返回一个Ansible的inventory对象 """ # TODO: 验证输入 - # 创建Ansible Group. + # 创建Ansible Group,如果没有则创建default组 for asset in self.hosts: g_name = asset.get('group', 'default') if g_name not in [g.name for g in self.groups]: - group = Group(name=asset.get('group', 'default')) + group = Group(name=g_name) self.groups.append(group) - # 初始化组变量 + # 添加组变量到相应的组上 for group_name, variables in self.group_vars.iteritems(): for g in self.groups: if g.name == group_name: - for v_name, v_value in variables: + for v_name, v_value in variables.iteritems(): g.set_variable(v_name, v_value) # 往组里面添加Host for asset in self.hosts: + # 添加Host链接的常用变量(host,port,user,pass,key) host = Host(name=asset['name'], port=asset['port']) - host.set_variable('ansible_ssh_host', asset['ip']) - host.set_variable('ansible_ssh_port', asset['port']) - host.set_variable('ansible_ssh_user', asset['username']) + host.set_variable('ansible_host', asset['ip']) + host.set_variable('ansible_port', asset['port']) + host.set_variable('ansible_user', asset['username']) + # 添加密码和秘钥 if asset.get('password'): host.set_variable('ansible_ssh_pass', asset['password']) if asset.get('key'): host.set_variable('ansible_ssh_private_key_file', asset['key']) + # 添加become支持 + become = asset.get("become", None) + if become is not None: + host.set_variable("ansible_become", True) + host.set_variable("ansible_become_method", become.get('method')) + host.set_variable("ansible_become_user", become.get('user')) + host.set_variable("ansible_become_pass", become.get('pass')) + else: + host.set_variable("ansible_become", False) + + # 添加其他Host的额外变量 for key, value in asset.iteritems(): if key not in ["name", "port", "ip", "username", "password", "key"]: host.set_variable(key, value) + + # 将host添加到组里面 for g in self.groups: if g.name == asset.get('group', 'default'): g.add_host(host) - # 生成Ansible inventory对象 + # 将组添加到Inventory里面,生成真正的inventory对象 inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[]) for g in self.groups: inventory.add_group(g) @@ -143,6 +172,7 @@ class CallbackModule(CallbackBase): def __init__(self, display=None): super(CallbackModule, self).__init__(display) self.results = [] + self.output = {} def _new_play(self, play): return { @@ -151,6 +181,7 @@ class CallbackModule(CallbackBase): 'id': str(play._uuid) }, 'tasks': [] + } def _new_task(self, task): @@ -159,22 +190,41 @@ class CallbackModule(CallbackBase): 'name': task.name, 'id': str(task._uuid) }, - 'hosts': {} + 'failed': {}, + 'unreachable': {}, + 'skipped': {}, + 'no_hosts': {}, + 'success': {} } + def v2_runner_on_failed(self, result, ignore_errors=False): + host = result._host + self.results[-1]['tasks'][-1]['failed'][host.name] = result._result + + def v2_runner_on_unreachable(self, result): + host = result._host + self.results[-1]['tasks'][-1]['unreachable'][host.name] = result._result + + def v2_runner_on_skipped(self, result): + host = result._host + self.results[-1]['tasks'][-1]['skipped'][host.name] = result._result + + def v2_runner_on_no_hosts(self, task): + self.results[-1]['tasks'][-1]['no_hosts']['name'] = task.name + self.results[-1]['tasks'][-1]['no_hosts']['uuid'] = task.uuid + + def v2_runner_on_ok(self, result): + host = result._host + self.results[-1]['tasks'][-1]['success'][host.name] = result._result + def v2_playbook_on_play_start(self, play): self.results.append(self._new_play(play)) def v2_playbook_on_task_start(self, task, is_conditional): self.results[-1]['tasks'].append(self._new_task(task)) - def v2_runner_on_ok(self, result, **kwargs): - host = result._host - self.results[-1]['tasks'][-1]['hosts'][host.name] = result._result - def v2_playbook_on_stats(self, stats): """Display info about playbook statistics""" - hosts = sorted(stats.processed.keys()) summary = {} @@ -182,16 +232,8 @@ class CallbackModule(CallbackBase): s = stats.summarize(h) summary[h] = s - output = { - 'plays': self.results, - 'stats': summary - } - - print(json.dumps(output, indent=4, sort_keys=True)) - - v2_runner_on_failed = v2_runner_on_ok - v2_runner_on_unreachable = v2_runner_on_ok - v2_runner_on_skipped = v2_runner_on_ok + self.output['plays'] = self.results + self.output['stats'] = summary class PlayBookRunner(InventoryMixin): @@ -279,7 +321,7 @@ class PlayBookRunner(InventoryMixin): class ADHocRunner(InventoryMixin): """ADHoc接口 """ - def __init__(self, config, play_data, become_pass=None, *hosts, **group_vars): + def __init__(self, config, play_data, *hosts, **group_vars): """ :param hosts: 见PlaybookRunner参数 :param group_vars: 见PlaybookRunner参数 @@ -299,13 +341,9 @@ class ADHocRunner(InventoryMixin): # 设置verbosity级别, 及命令行的--verbose选项 self.display = Display() self.display.verbosity = self.options.verbosity - playbook_executor.verbosity = self.options.verbosity - # sudo成其他用户的配置 - self.options.become = True - self.options.become_method = 'sudo' - self.options.become_user = 'root' - self.passwords = {'become_pass': become_pass} + # sudo的配置移到了Host级别去了,因此这里不再需要处理 + self.passwords = None # 生成Ansible inventory, 这些变量Mixin都会用到 self.hosts = hosts @@ -344,24 +382,36 @@ class ADHocRunner(InventoryMixin): if __name__ == "__main__": conf = Config() - assets = [{ - "name": "localhost", - "ip": "localhost", + assets = [ + { + "name": "192.168.1.119", + "ip": "192.168.1.119", + "port": "22", + "username": "root", + "password": "xxx", + "key": "asset_private_key", + }, + { + "name": "192.168.232.135", + "ip": "192.168.232.135", "port": "22", "username": "yumaojun", - "password": "yusky0902", + "password": "xxx", "key": "asset_private_key", - }] + "become": {"method": "sudo", "user": "root", "pass": "yusky0902"} + }, + ] # 初始化Play play_source = { "name": "Ansible Play", - "hosts": "*", + "hosts": "default", "gather_facts": "no", "tasks": [ dict(action=dict(module='setup')), + dict(action=dict(module='command', args='lsdfd')) ] } - hoc = ADHocRunner(conf, play_source,'yusky0902', *assets) + hoc = ADHocRunner(conf, play_source, *assets) ext_code, result = hoc.run() print ext_code - print result \ No newline at end of file + print result diff --git a/apps/ops/models.py b/apps/ops/models.py index bd4b2abe9..13563de9b 100644 --- a/apps/ops/models.py +++ b/apps/ops/models.py @@ -1,5 +1,34 @@ -from __future__ import unicode_literals +# ~*~ coding: utf-8 ~*~ +from __future__ import unicode_literals, absolute_import + +import logging from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +logger = logging.getLogger(__name__) + + +class Play(models.Model): + name = models.CharField(max_length=128, verbose_name=_('Name')) + uuid = models.CharField(max_length=128, verbose_name=_('UUID')) + completed = models.BooleanField(default=False, verbose_name=_('IsCompleted')) + status_code = models.IntegerField(default=0, verbose_name=_('StatusCode')) + + def __unicode__(self): + return self.name + + +class Task(models.Model): + play = models.ForeignKey(Play, related_name='tasks', blank=True) + name = models.CharField(max_length=128, blank=True, blverbose_name=_('Name')) + uuid = models.CharField(max_length=128, verbose_name=_('UUID')) + + def __unicode__(self): + return self.clean() + + +class HostResult(models.Model): + task = models.ForeignKey(Task, related_name='host_results', blank=True) -# Create your models here. diff --git a/requirements.txt b/requirements.txt index ddf45a598..017720c87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ ForgeryPy==0.1 openpyxl==2.4.0 paramiko==2.0.2 celery==3.1.23 -ansible==2.1.1.0 +ansible==2.1.2.0 django-simple-captcha==0.5.2 django-formtools==1.0 sshpubkeys==2.2.0