From 915873135eb8bf341707215b17d3351ab7d6efb0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 14 Mar 2017 00:58:25 +0800 Subject: [PATCH] =?UTF-8?q?[Fixture]=20=E6=B7=BB=E5=8A=A0runner=20run=20re?= =?UTF-8?q?cord?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms.py | 15 +- apps/assets/models/asset.py | 6 +- .../assets/templates/assets/asset_create.html | 1 - .../assets/templates/assets/asset_update.html | 1 - apps/ops/models.py | 32 +- apps/ops/tasks.py | 57 +++- apps/ops/templates/cron/_cron.html | 97 ------ apps/ops/templates/cron/create.html | 16 - apps/ops/templates/cron/detail.html | 280 ------------------ apps/ops/templates/cron/list.html | 231 --------------- apps/ops/templates/cron/update.html | 20 -- .../ops/templates/ops/task_record_detail.html | 147 +++++++++ apps/ops/templates/ops/task_record_list.html | 136 +++++++++ apps/ops/templates/sudo/_sudo.html | 97 ------ apps/ops/templates/sudo/create.html | 16 - apps/ops/templates/sudo/detail.html | 280 ------------------ apps/ops/templates/sudo/list.html | 226 -------------- apps/ops/templates/sudo/update.html | 20 -- apps/ops/templates/task/_task.html | 97 ------ apps/ops/templates/task/create.html | 16 - apps/ops/templates/task/detail.html | 280 ------------------ apps/ops/templates/task/list.html | 225 -------------- apps/ops/templates/task/update.html | 20 -- apps/ops/urls/view_urls.py | 5 +- apps/ops/utils/callback.py | 28 +- apps/ops/utils/runner.py | 29 +- apps/ops/views.py | 33 ++- apps/templates/_nav.html | 2 +- 28 files changed, 444 insertions(+), 1969 deletions(-) delete mode 100644 apps/ops/templates/cron/_cron.html delete mode 100644 apps/ops/templates/cron/create.html delete mode 100644 apps/ops/templates/cron/detail.html delete mode 100644 apps/ops/templates/cron/list.html delete mode 100644 apps/ops/templates/cron/update.html create mode 100644 apps/ops/templates/ops/task_record_detail.html create mode 100644 apps/ops/templates/ops/task_record_list.html delete mode 100644 apps/ops/templates/sudo/_sudo.html delete mode 100644 apps/ops/templates/sudo/create.html delete mode 100644 apps/ops/templates/sudo/detail.html delete mode 100644 apps/ops/templates/sudo/list.html delete mode 100644 apps/ops/templates/sudo/update.html delete mode 100644 apps/ops/templates/task/_task.html delete mode 100644 apps/ops/templates/task/create.html delete mode 100644 apps/ops/templates/task/detail.html delete mode 100644 apps/ops/templates/task/list.html delete mode 100644 apps/ops/templates/task/update.html diff --git a/apps/assets/forms.py b/apps/assets/forms.py index e6beb3e6e..0001eeea0 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -23,19 +23,16 @@ class AssetCreateForm(forms.ModelForm): self.instance.tags.clear() self.instance.tags.add(*tuple(tags)) - # def clean(self): - # clean_data = super(AssetCreateForm, self).clean() - # ip = clean_data.get('ip') - # port = clean_data.get('port') - # query = Asset.objects.filter(ip=ip, port=port) - # if query: - # raise forms.ValidationError('this asset has exists.') + def clean_admin_user(self): + if not self.cleaned_data['admin_user']: + raise forms.ValidationError(_('Select admin user')) + return self.cleaned_data['admin_user'] class Meta: model = Asset tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all()) fields = [ - 'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'system_users', 'idc', 'groups', + 'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'idc', 'groups', 'other_ip', 'remote_card_ip', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no', 'cabinet_pos', 'number', 'status', 'env', 'sn', 'tags', ] @@ -44,8 +41,6 @@ class AssetCreateForm(forms.ModelForm): 'data-placeholder': _('Select asset groups')}), 'tags': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset tags')}), - 'system_users': forms.SelectMultiple(attrs={'class': 'select2', - 'data-placeholder': _('Select asset system users')}), 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), } help_texts = { diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 4fa70d25e..7386b23bc 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -94,9 +94,9 @@ class Asset(models.Model): 'ip': self.ip, 'port': self.port, 'groups': [group.name for group in self.groups.all()], - 'username': self.admin_user.username, - 'password': self.admin_user.password, - 'private_key': self.admin_user.private_key, + 'username': self.admin_user.username if self.admin_user else '', + 'password': self.admin_user.password if self.admin_user else '', + 'private_key': self.admin_user.private_key if self.admin_user else None, } class Meta: diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html index 1a4aa38d5..5fbe1b772 100644 --- a/apps/assets/templates/assets/asset_create.html +++ b/apps/assets/templates/assets/asset_create.html @@ -20,7 +20,6 @@

{% trans 'Asset user' %}

{{ form.admin_user|bootstrap_horizontal }} - {{ form.system_users|bootstrap_horizontal }}

{% trans 'Other' %}

diff --git a/apps/assets/templates/assets/asset_update.html b/apps/assets/templates/assets/asset_update.html index fb46c79d0..bd9f7bde8 100644 --- a/apps/assets/templates/assets/asset_update.html +++ b/apps/assets/templates/assets/asset_update.html @@ -25,7 +25,6 @@

{% trans 'Asset user' %}

{{ form.admin_user|bootstrap_horizontal }} - {{ form.system_users|bootstrap_horizontal }}

{% trans 'Hardware' %}

diff --git a/apps/ops/models.py b/apps/ops/models.py index 0bdc65ae6..b8c8054c7 100644 --- a/apps/ops/models.py +++ b/apps/ops/models.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals, absolute_import import logging +from collections import OrderedDict +import json from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -17,10 +19,14 @@ class TaskRecord(models.Model): 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')) - result = models.TextField(blank=True, null=True, verbose_name=_('Task result')) + assets = models.TextField(blank=True, null=True, verbose_name=_('Assets for hostname')) # 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): return "%s" % self.uuid @@ -29,3 +35,25 @@ class TaskRecord(models.Model): def total_assets(self): return self.assets.split(',') + @property + def assets_json(self): + from assets.models import Asset + return [Asset.objects.get(hostname=hostname)._to_secret_json() + for hostname in self.total_assets + if Asset.objects.exists(hostname=hostname)] + + @property + def module_args(self): + task_tuple = [] + for module, args in json.loads(self._modules_args, object_pairs_hook=OrderedDict).items(): + task_tuple.append((module, args)) + return task_tuple + + @module_args.setter + def module_args(self, task_tuple): + module_args_ = OrderedDict({}) + for module, args in task_tuple: + module_args_[module] = args + self._modules_args = json.dumps(module_args_) + + diff --git a/apps/ops/tasks.py b/apps/ops/tasks.py index 45de3e727..5e333195e 100644 --- a/apps/ops/tasks.py +++ b/apps/ops/tasks.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import absolute_import, unicode_literals +import json import time @@ -33,13 +34,50 @@ def asset_test_ping_check(assets): return result['contacted'].keys(), result['dark'].keys() +@shared_task(bind=True) +def run_AdHoc(self, task_tuple, assets, + task_name='Ansible AdHoc runner', pattern='all', record=True): + + runner = AdHocRunner(assets) + if record: + from .models import TaskRecord + if not TaskRecord.objects.filter(uuid=self.request.id): + record = TaskRecord(uuid=self.request.id, + name=task_name, + assets=','.join(asset['hostname'] for asset in assets), + module_args=task_tuple, + pattern=pattern) + record.save() + else: + record = TaskRecord.objects.get(uuid=self.request.id) + record.date_start = timezone.now() + ts_start = time.time() + logger.warn('Start runner {}'.format(task_name)) + result = runner.run(task_tuple, pattern=pattern, task_name=task_name) + timedelta = round(time.time() - ts_start, 2) + summary = runner.clean_result() + if record: + record.date_finished = timezone.now() + record.is_finished = True + record.result = json.dumps(result) + record.summary = json.dumps(summary) + record.timedelta = timedelta + if len(summary['failed']) == 0: + record.is_success = True + else: + record.is_success = False + record.save() + return summary + + @shared_task(bind=True) def push_users(self, assets, users): """ user: { - username: xxx, - shell: /bin/bash, - password: 'staf', + name: 'web', + username: 'web', + shell: '/bin/bash', + password: '123123123', public_key: 'string', sudo: '/bin/whoami,/sbin/ifconfig' } @@ -49,8 +87,8 @@ def push_users(self, assets, users): if isinstance(assets, dict): assets = [assets] task_tuple = [] + for user in users: - logger.debug('Push user: {}'.format(user)) # 添加用户, 设置公钥, 设置sudo task_tuple.extend([ ('user', 'name={} shell={} state=present password={}'.format( @@ -65,16 +103,19 @@ def push_users(self, assets, users): user['username'], user.get('sudo', '/bin/whoami') )) ]) - record = TaskRecord(name='Push user', + task_name = 'Push user {}'.format(','.join([user['name'] for user in users])) + record = TaskRecord(name=task_name, uuid=self.request.id, date_start=timezone.now(), assets=','.join(asset['hostname'] for asset in assets)) record.save() - logger.info('Runner start {0}'.format(timezone.now())) + logger.info('Runner {0} start {1}'.format(task_name, timezone.now())) hoc = AdHocRunner(assets) + ts_start = time.time() _ = hoc.run(task_tuple) - logger.info('Runner complete {0}'.format(timezone.now())) + logger.info('Runner {0} complete {1}'.format(task_name, timezone.now())) result_clean = hoc.clean_result() + record.time = int(time.time() - ts_start) record.date_finished = timezone.now() record.is_finished = True @@ -82,6 +123,6 @@ def push_users(self, assets, users): record.is_success = True else: record.is_success = False - record.result = result_clean + record.result = json.dumps(result_clean) record.save() return result_clean diff --git a/apps/ops/templates/cron/_cron.html b/apps/ops/templates/cron/_cron.html deleted file mode 100644 index 59d03e5ee..000000000 --- a/apps/ops/templates/cron/_cron.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% load static %} -{% load bootstrap %} -{% block custom_head_css_js %} - - - -{% endblock %} - -{% block content %} -
-
-
-
-
-
{% block user_template_title %}{% trans 'Create user' %}{% endblock %}
- -
-
-
- {% csrf_token %} -

{% trans 'Account' %}

- {% block username %} {% endblock %} - {{ form.email|bootstrap_horizontal }} - {{ form.name|bootstrap_horizontal }} - {{ form.groups|bootstrap_horizontal }} - -
- {% block password %} {% endblock %} - -
-

{% trans 'Security and Role' %}

- {{ form.role|bootstrap_horizontal }} -
- -
-
- - -
- {{ form.date_expired.errors }} -
-
-{# {{ form.date_expired|bootstrap_horizontal }}#} -
- -
- {{ form.enable_otp }} -
-
-
-

{% trans 'Profile' %}

- {{ form.phone|bootstrap_horizontal }} - {{ form.wechat|bootstrap_horizontal }} - {{ form.comment|bootstrap_horizontal }} -
-
-
- - -
-
-
-
-
-
-
-
-{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} diff --git a/apps/ops/templates/cron/create.html b/apps/ops/templates/cron/create.html deleted file mode 100644 index 291a5656a..000000000 --- a/apps/ops/templates/cron/create.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'cron/_cron.html' %} -{% load i18n %} -{% load bootstrap %} -{% block user_template_title %}{% trans "Create user" %}{% endblock %} -{% block username %} - {{ form.username|bootstrap_horizontal }} -{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- {% trans 'Reset link will be generated and sent to the user. ' %} -
-
-{% endblock %} \ No newline at end of file diff --git a/apps/ops/templates/cron/detail.html b/apps/ops/templates/cron/detail.html deleted file mode 100644 index 6188962ff..000000000 --- a/apps/ops/templates/cron/detail.html +++ /dev/null @@ -1,280 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - - - -{% endblock %} -{% block content %} -
-
-
-
- -
-
-
-
- {{ cron.name }} -
- - - - - - - - - - -
-
-
- - - - - - - -
{% trans 'Name' %}:{{ cron.name }}
-
-
-
-
-
-
- {% trans 'Quick modify' %} -
-
- - - - - - - - - - - - - - - - - - - - - - - -
{% trans 'Active' %}: -
-
- - -
-
-
{% trans 'Enable OTP' %}: -
-
- - -
-
-
{% trans 'Reset password' %}: - - - -
{% trans 'Reset ssh key' %}: - - - -
{% trans 'Update ssh key' %}: - - - -
-
-
- -
-
- {% trans 'User group' %} -
-
- - - - - - - - - - - - {% for group in user_object.groups.all %} - - - - - {% endfor %} - -
- -
- -
{{ group.name }} - -
-
-
-
-
-
-
-
-
- {% include 'users/_user_update_pk_modal.html' %} -{% endblock %} -{% block custom_foot_js %} - -{% endblock %} diff --git a/apps/ops/templates/cron/list.html b/apps/ops/templates/cron/list.html deleted file mode 100644 index d1a2c78bc..000000000 --- a/apps/ops/templates/cron/list.html +++ /dev/null @@ -1,231 +0,0 @@ -{% extends '_base_list.html' %} -{% load i18n static %} -{% block table_search %} -{% endblock %} -{% block table_container %} -
{% trans "Create cron" %}
-{#
{% trans "Import user" %}
#} - - - - - - - - - - - - - -
-{#
#} - -
{% trans 'Name' %}{% trans 'Time(minute-hour-day-month-weekday)' %}{% trans 'Job' %}{% trans 'User' %}{% trans 'Action' %}
-
-
- -
- -
-
-
-{% include "users/_user_bulk_update_modal.html" %} -{#{% include "users/_user_import_modal.html" %}#} -{% endblock %} -{% block content_bottom_left %}{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} - diff --git a/apps/ops/templates/cron/update.html b/apps/ops/templates/cron/update.html deleted file mode 100644 index f033e1ed8..000000000 --- a/apps/ops/templates/cron/update.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'cron/_cron.html' %} -{% load i18n %} -{% block user_template_title %}{% trans "Update user" %}{% endblock %} -{% block username %} -
- -
- -
-
-{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- -
-
-{% endblock %} diff --git a/apps/ops/templates/ops/task_record_detail.html b/apps/ops/templates/ops/task_record_detail.html new file mode 100644 index 000000000..bd71a5a90 --- /dev/null +++ b/apps/ops/templates/ops/task_record_detail.html @@ -0,0 +1,147 @@ +{% extends 'base.html' %} +{% load static %} +{% load i18n %} + +{% block custom_head_css_js %} + + + + +{% endblock %} +{% block content %} +
+
+
+
+ +
+
+
+
+ {{ object.name }} +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans 'UUID' %}:{{ object.uuid }}
{% trans 'Name' %}:{{ object.name }}
{% trans 'Date start' %}:{{ object.date_start}}
{% trans 'Date finished' %}:{{ object.date_finished }}
{% trans 'Time delta' %}:{{ object.timedelta}} s
{% trans 'Is finished' %}:{{ object.is_finished }}
{% trans 'Is success ' %}:{{ object.is_success }}
{% trans 'Assets ' %}: + + {% for asset in object.total_assets %} + {{ asset }}
+ {% endfor %} +
+
+
+
+
+
+
+
+ {% trans 'Failed assets' %} +
+
+ + + {% for host, msg in results.failed %} + {% if forloop.first %} + + {% else %} + + {% endif %} + + + + {% empty %} + + + + {% endfor %} + +
{{ host }}: {{ msg }}
{% trans 'No assets' %}
+
+
+ +
+
+ {% trans 'Success assets' %} +
+
+ + + {% for host in results.success %} + {% if forloop.first %} + + {% else %} + + {% endif %} + + + {% empty %} + + + + {% endfor %} + +
{{ host }}
{% trans 'No assets' %}
+
+
+
+
+
+
+
+
+ {% include 'users/_user_update_pk_modal.html' %} +{% endblock %} + diff --git a/apps/ops/templates/ops/task_record_list.html b/apps/ops/templates/ops/task_record_list.html new file mode 100644 index 000000000..fe4a2412e --- /dev/null +++ b/apps/ops/templates/ops/task_record_list.html @@ -0,0 +1,136 @@ +{% extends '_base_list.html' %} +{% load i18n %} +{% load static %} +{% block content_left_head %} + + +{% endblock %} + + +{% block table_search %} +
+
+
+ + + to + +
+
+
+ +
+
+
+ +
+
+
+{% endblock %} + +{% block table_head %} + + {% trans 'Name' %} + {% trans 'Asset' %} + {% trans 'Success' %} + {% trans 'Finished' %} + {% trans 'Date start' %} + {% trans 'Time' %} + {% trans 'Action' %} +{% endblock %} + +{% block table_body %} + {% for object in task_record_list %} + + + {{ object.name }} + {{ object.total_assets|length }} + + {% if object.is_success %} + + {% else %} + + {% endif %} + + + {% if object.is_finished %} + + {% else %} + + {% endif %} + + {{ object.date_start }} + {{ object.timedelta }} s + + {% trans "Repush" %} + + + {% endfor %} +{% endblock %} + +{# comment #} +{% block custom_foot_js %} + + +{# function terminateConnection(data) {#} +{# function success() {#} +{# window.setTimeout(function () {#} +{# window.location.reload()#} +{# }, 300)#} +{# }#} +{# var the_url = "{% url 'api-applications:terminate-connection' %}";#} +{# APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});#} +{# }#} +{# $(document).ready(function() {#} +{# $('table').DataTable({#} +{# "searching": false,#} +{# "paging": false,#} +{# "bInfo" : false,#} +{# "order": []#} +{# });#} +{# $('.select2').select2();#} +{# $('#date .input-daterange').datepicker({#} +{# dateFormat: 'mm/dd/yy',#} +{# keyboardNavigation: false,#} +{# forceParse: false,#} +{# autoclose: true#} +{# });#} +{# }).on('click', '.btn-term', function () {#} +{# var $this = $(this);#} +{# var proxy_log_id = $this.attr('value');#} +{# var data = {#} +{# proxy_log_id: proxy_log_id#} +{# };#} +{# terminateConnection(data)#} +{# }).on('click', '#btn_bulk_update', function () {#} +{# var data = [];#} +{# $('.cbx-term:checked').each(function () {#} +{# data.push({proxy_log_id: $(this).attr('value')})#} +{# });#} +{# terminateConnection(data)#} +{# })#} +{# #} +{% endblock %} + diff --git a/apps/ops/templates/sudo/_sudo.html b/apps/ops/templates/sudo/_sudo.html deleted file mode 100644 index 59d03e5ee..000000000 --- a/apps/ops/templates/sudo/_sudo.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% load static %} -{% load bootstrap %} -{% block custom_head_css_js %} - - - -{% endblock %} - -{% block content %} -
-
-
-
-
-
{% block user_template_title %}{% trans 'Create user' %}{% endblock %}
- -
-
-
- {% csrf_token %} -

{% trans 'Account' %}

- {% block username %} {% endblock %} - {{ form.email|bootstrap_horizontal }} - {{ form.name|bootstrap_horizontal }} - {{ form.groups|bootstrap_horizontal }} - -
- {% block password %} {% endblock %} - -
-

{% trans 'Security and Role' %}

- {{ form.role|bootstrap_horizontal }} -
- -
-
- - -
- {{ form.date_expired.errors }} -
-
-{# {{ form.date_expired|bootstrap_horizontal }}#} -
- -
- {{ form.enable_otp }} -
-
-
-

{% trans 'Profile' %}

- {{ form.phone|bootstrap_horizontal }} - {{ form.wechat|bootstrap_horizontal }} - {{ form.comment|bootstrap_horizontal }} -
-
-
- - -
-
-
-
-
-
-
-
-{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} diff --git a/apps/ops/templates/sudo/create.html b/apps/ops/templates/sudo/create.html deleted file mode 100644 index 19727e102..000000000 --- a/apps/ops/templates/sudo/create.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'sudo/_sudo.html' %} -{% load i18n %} -{% load bootstrap %} -{% block user_template_title %}{% trans "Create user" %}{% endblock %} -{% block username %} - {{ form.username|bootstrap_horizontal }} -{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- {% trans 'Reset link will be generated and sent to the user. ' %} -
-
-{% endblock %} \ No newline at end of file diff --git a/apps/ops/templates/sudo/detail.html b/apps/ops/templates/sudo/detail.html deleted file mode 100644 index cf6b8e879..000000000 --- a/apps/ops/templates/sudo/detail.html +++ /dev/null @@ -1,280 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - - - -{% endblock %} -{% block content %} -
-
-
-
- -
-
-
-
- {{ sudo.name }} -
- - - - - - - - - - -
-
-
- - - - - - - -
{% trans 'Name' %}:{{ sudo.name }}
-
-
-
-
-
-
- {% trans 'Quick modify' %} -
-
- - - - - - - - - - - - - - - - - - - - - - - -
{% trans 'Active' %}: -
-
- - -
-
-
{% trans 'Enable OTP' %}: -
-
- - -
-
-
{% trans 'Reset password' %}: - - - -
{% trans 'Reset ssh key' %}: - - - -
{% trans 'Update ssh key' %}: - - - -
-
-
- -
-
- {% trans 'User group' %} -
-
- - - - - - - - - - - - {% for group in user_object.groups.all %} - - - - - {% endfor %} - -
- -
- -
{{ group.name }} - -
-
-
-
-
-
-
-
-
- {% include 'users/_user_update_pk_modal.html' %} -{% endblock %} -{% block custom_foot_js %} - -{% endblock %} diff --git a/apps/ops/templates/sudo/list.html b/apps/ops/templates/sudo/list.html deleted file mode 100644 index 8da993251..000000000 --- a/apps/ops/templates/sudo/list.html +++ /dev/null @@ -1,226 +0,0 @@ -{% extends '_base_list.html' %} -{% load i18n static %} -{% block table_search %} -{% endblock %} -{% block table_container %} -
{% trans "Create sudo" %}
-{#
{% trans "Import user" %}
#} - - - - - - - - - - - - - -
- - {% trans 'Name' %}{% trans 'Privileges' %}{% trans 'Extra Lines' %}{% trans 'Action' %}
-
-
- -
- -
-
-
-{#{% include "users/_user_bulk_update_modal.html" %}#} -{#{% include "users/_user_import_modal.html" %}#} -{% endblock %} -{% block content_bottom_left %}{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} - diff --git a/apps/ops/templates/sudo/update.html b/apps/ops/templates/sudo/update.html deleted file mode 100644 index 3172d23f4..000000000 --- a/apps/ops/templates/sudo/update.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'sudo/_sudo.html' %} -{% load i18n %} -{% block user_template_title %}{% trans "Update user" %}{% endblock %} -{% block username %} -
- -
- -
-
-{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- -
-
-{% endblock %} diff --git a/apps/ops/templates/task/_task.html b/apps/ops/templates/task/_task.html deleted file mode 100644 index 59d03e5ee..000000000 --- a/apps/ops/templates/task/_task.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% load static %} -{% load bootstrap %} -{% block custom_head_css_js %} - - - -{% endblock %} - -{% block content %} -
-
-
-
-
-
{% block user_template_title %}{% trans 'Create user' %}{% endblock %}
- -
-
-
- {% csrf_token %} -

{% trans 'Account' %}

- {% block username %} {% endblock %} - {{ form.email|bootstrap_horizontal }} - {{ form.name|bootstrap_horizontal }} - {{ form.groups|bootstrap_horizontal }} - -
- {% block password %} {% endblock %} - -
-

{% trans 'Security and Role' %}

- {{ form.role|bootstrap_horizontal }} -
- -
-
- - -
- {{ form.date_expired.errors }} -
-
-{# {{ form.date_expired|bootstrap_horizontal }}#} -
- -
- {{ form.enable_otp }} -
-
-
-

{% trans 'Profile' %}

- {{ form.phone|bootstrap_horizontal }} - {{ form.wechat|bootstrap_horizontal }} - {{ form.comment|bootstrap_horizontal }} -
-
-
- - -
-
-
-
-
-
-
-
-{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} diff --git a/apps/ops/templates/task/create.html b/apps/ops/templates/task/create.html deleted file mode 100644 index 19727e102..000000000 --- a/apps/ops/templates/task/create.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends 'sudo/_sudo.html' %} -{% load i18n %} -{% load bootstrap %} -{% block user_template_title %}{% trans "Create user" %}{% endblock %} -{% block username %} - {{ form.username|bootstrap_horizontal }} -{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- {% trans 'Reset link will be generated and sent to the user. ' %} -
-
-{% endblock %} \ No newline at end of file diff --git a/apps/ops/templates/task/detail.html b/apps/ops/templates/task/detail.html deleted file mode 100644 index cf6b8e879..000000000 --- a/apps/ops/templates/task/detail.html +++ /dev/null @@ -1,280 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load i18n %} - -{% block custom_head_css_js %} - - - - -{% endblock %} -{% block content %} -
-
-
-
- -
-
-
-
- {{ sudo.name }} -
- - - - - - - - - - -
-
-
- - - - - - - -
{% trans 'Name' %}:{{ sudo.name }}
-
-
-
-
-
-
- {% trans 'Quick modify' %} -
-
- - - - - - - - - - - - - - - - - - - - - - - -
{% trans 'Active' %}: -
-
- - -
-
-
{% trans 'Enable OTP' %}: -
-
- - -
-
-
{% trans 'Reset password' %}: - - - -
{% trans 'Reset ssh key' %}: - - - -
{% trans 'Update ssh key' %}: - - - -
-
-
- -
-
- {% trans 'User group' %} -
-
- - - - - - - - - - - - {% for group in user_object.groups.all %} - - - - - {% endfor %} - -
- -
- -
{{ group.name }} - -
-
-
-
-
-
-
-
-
- {% include 'users/_user_update_pk_modal.html' %} -{% endblock %} -{% block custom_foot_js %} - -{% endblock %} diff --git a/apps/ops/templates/task/list.html b/apps/ops/templates/task/list.html deleted file mode 100644 index 7109e4fe8..000000000 --- a/apps/ops/templates/task/list.html +++ /dev/null @@ -1,225 +0,0 @@ -{% extends '_base_list.html' %} -{% load i18n static %} -{% block table_search %} -{% endblock %} -{% block table_container %} -
{% trans "Create task" %}
-{#
{% trans "Import user" %}
#} - - - - - - - - - - - - - - -
- - {% trans 'Name' %}{% trans 'UUID' %}{% trans 'Start' %}{% trans 'Completed' %}{% trans 'Action' %}
-
-
- -
- -
-
-
-{% endblock %} -{% block content_bottom_left %}{% endblock %} -{% block custom_foot_js %} - - -{% endblock %} - diff --git a/apps/ops/templates/task/update.html b/apps/ops/templates/task/update.html deleted file mode 100644 index 3172d23f4..000000000 --- a/apps/ops/templates/task/update.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'sudo/_sudo.html' %} -{% load i18n %} -{% block user_template_title %}{% trans "Update user" %}{% endblock %} -{% block username %} -
- -
- -
-
-{% endblock %} -{% block password %} -

{% trans 'Password' %}

-
- -
- -
-
-{% endblock %} diff --git a/apps/ops/urls/view_urls.py b/apps/ops/urls/view_urls.py index 8a0fba4f7..c8ef31529 100644 --- a/apps/ops/urls/view_urls.py +++ b/apps/ops/urls/view_urls.py @@ -3,11 +3,12 @@ from __future__ import unicode_literals from django.conf.urls import url -from ops import views as page_view +from .. import views __all__ = ["urlpatterns"] urlpatterns = [ # TResource Task url - url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'), + url(r'^task-record/$', views.TaskRecordListView.as_view(), name='task-record-list'), + url(r'^task-record/(?P[0-9a-zA-Z-]+)/$', views.TaskRecordDetailView.as_view(), name='task-record-detail'), ] \ No newline at end of file diff --git a/apps/ops/utils/callback.py b/apps/ops/utils/callback.py index 6e0f29cb6..148117991 100644 --- a/apps/ops/utils/callback.py +++ b/apps/ops/utils/callback.py @@ -1,11 +1,37 @@ # ~*~ 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): """ - Custom Callback + AdHoc result Callback """ def __init__(self, display=None): self.result_q = dict(contacted={}, dark={}) diff --git a/apps/ops/utils/runner.py b/apps/ops/utils/runner.py index 14ccf0afe..1a1612d91 100644 --- a/apps/ops/utils/runner.py +++ b/apps/ops/utils/runner.py @@ -14,7 +14,8 @@ 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 +from .callback import AdHocResultCallback, PlaybookResultCallBack, \ + CommandResultCallback from common.utils import get_logger @@ -29,6 +30,7 @@ class AnsibleError(StandardError): pass +# Jumpserver not use playbook class PlayBookRunner(object): """ 用于执行AnsiblePlaybook的接口.简化Playbook对象的使用. @@ -136,6 +138,8 @@ class AdHocRunner(object): ] ) + results_callback_class = AdHocResultCallback + def __init__(self, hosts=C.DEFAULT_HOST_LIST, forks=C.DEFAULT_FORKS, # 5 @@ -156,7 +160,7 @@ class AdHocRunner(object): self.variable_manager = VariableManager() self.loader = DataLoader() self.gather_facts = gather_facts - self.results_callback = AdHocResultCallback() + self.results_callback = AdHocRunner.results_callback_class() self.options = self.Options( connection=connection_type, timeout=timeout, @@ -171,7 +175,8 @@ class AdHocRunner(object): private_key_file=private_key_file, ) - self.variable_manager.extra_vars = load_extra_vars(self.loader, options=self.options) + 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) @@ -252,7 +257,7 @@ class AdHocRunner(object): """ :return: { "success": ['hostname',], - "failed": [{'hostname': 'msg'}, {}], + "failed": [('hostname', 'msg'), {}], } """ result = {'success': [], 'failed': []} @@ -262,26 +267,30 @@ class AdHocRunner(object): for host, msgs in self.results_callback.result_q['dark'].items(): msg = '\n'.join(['{}: {}'.format(msg.get('invocation', {}).get('module_name'), msg.get('msg', '')) for msg in msgs]) - result['failed'].append({host: msg}) + result['failed'].append((host, msg)) return result + + + def test_run(): assets = [ { - "hostname": "192.168.152.129", - "ip": "192.168.152.129", + "hostname": "192.168.244.129", + "ip": "192.168.244.129", "port": 22, "username": "root", "password": "redhat", }, ] - task_tuple = (('shell', 'ls'), ('ping', '')) + task_tuple = (('shell', 'ls'),) hoc = AdHocRunner(hosts=assets) + hoc.results_callback = CommandResultCallback() ret = hoc.run(task_tuple) print(ret) - play = PlayBookRunner(assets, playbook_path='/tmp/some.yml') + #play = PlayBookRunner(assets, playbook_path='/tmp/some.yml') """ # /tmp/some.yml --- @@ -293,7 +302,7 @@ def test_run(): - name: exec uptime shell: uptime """ - play.run() + #play.run() if __name__ == "__main__": diff --git a/apps/ops/views.py b/apps/ops/views.py index ebaa2f033..33720dbc8 100644 --- a/apps/ops/views.py +++ b/apps/ops/views.py @@ -1,24 +1,39 @@ # ~*~ coding: utf-8 ~*~ from __future__ import unicode_literals -from django.conf import settings -from django.views.generic.list import ListView +import json + +from django.conf import settings +from django.views.generic import ListView, DetailView -from users.utils import AdminUserRequiredMixin from .models import TaskRecord -class TaskListView(AdminUserRequiredMixin, ListView): +class TaskRecordListView(ListView): paginate_by = settings.CONFIG.DISPLAY_PER_PAGE model = TaskRecord - context_object_name = 'tasks' - template_name = 'task/list.html' + ordering = ('-date_start',) + context_object_name = 'task_record_list' + template_name = 'ops/task_record_list.html' def get_context_data(self, **kwargs): context = { - 'task': 'Assets', - 'action': 'Create asset', + 'app': 'Ops', + 'action': 'Task record list', } kwargs.update(context) - return super(TaskListView, self).get_context_data(**kwargs) + return super(TaskRecordListView, self).get_context_data(**kwargs) + +class TaskRecordDetailView(DetailView): + model = TaskRecord + template_name = 'ops/task_record_detail.html' + + def get_context_data(self, **kwargs): + context = { + 'app': 'Ops', + 'action': 'Task record detail', + 'results': json.loads(self.object.summary), + } + kwargs.update(context) + return super(TaskRecordDetailView, self).get_context_data(**kwargs) diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 25ecf136d..e9b55c48f 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -49,7 +49,7 @@ {% trans 'Job Center' %}