diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 068f41aa2..1e6540586 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -112,7 +112,12 @@ class Asset(models.Model): 'groups': [group.name for group in self.groups.all()], '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, + 'private_key': self.admin_user.private_key_file if self.admin_user else None, + 'become': { + 'method': self.admin_user.become_method, + 'user': self.admin_user.become_user, + 'pass': self.admin_user.become_pass, + } if self.admin_user.become else {}, } class Meta: diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index ee2a156ab..48d0f4029 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -3,12 +3,14 @@ # from __future__ import unicode_literals - +import os import logging +from hashlib import md5 from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.conf import settings from common.utils import signer, validate_ssh_private_key, ssh_key_string_to_obj @@ -38,7 +40,7 @@ class AdminUser(models.Model): become = models.BooleanField(default=True) become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4) become_user = models.CharField(default='root', max_length=64) - become_password = models.CharField(default='', max_length=128) + become_pass = models.CharField(default='', max_length=128) _public_key = models.CharField( max_length=4096, blank=True, verbose_name=_('SSH public key')) comment = models.TextField(blank=True, verbose_name=_('Comment')) @@ -74,6 +76,18 @@ class AdminUser(models.Model): def private_key(self, private_key_raw): self._private_key = signer.sign(private_key_raw) + @property + def private_key_file(self): + if not self.private_key: + return None + project_dir = settings.PROJECT_DIR + tmp_dir = os.path.join(project_dir, 'tmp') + key_name = md5(self._private_key).hexdigest() + key_path = os.path.join(tmp_dir, key_name) + if not os.path.exists(key_path): + self.private_key.write_private_key_file(key_path) + return key_path + @property def public_key(self): return signer.unsign(self._public_key) diff --git a/apps/audits/templates/audits/proxy_log_list.html b/apps/audits/templates/audits/proxy_log_offline_list.html similarity index 100% rename from apps/audits/templates/audits/proxy_log_list.html rename to apps/audits/templates/audits/proxy_log_offline_list.html diff --git a/apps/audits/templates/audits/proxy_log_online_list.html b/apps/audits/templates/audits/proxy_log_online_list.html new file mode 100644 index 000000000..6a3c815b0 --- /dev/null +++ b/apps/audits/templates/audits/proxy_log_online_list.html @@ -0,0 +1,173 @@ +{% extends '_base_list.html' %} +{% load i18n %} +{% load static %} +{% block content_left_head %} + + +{% endblock %} + + +{% block table_search %} +
+
+
+ + + to + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+{% endblock %} + +{% block table_head %} + + {% trans 'ID' %} + {% trans 'User' %} + {% trans 'Asset' %} + {% trans 'System user' %} + {% trans 'Terminal' %} + {% trans 'Command' %} + {% trans 'Success' %} + {% trans 'Finished' %} + {% trans 'R/M' %} + {% trans 'Date start' %} + {% trans 'Time' %} +{% endblock %} + +{% block table_body %} + {% for proxy_log in proxy_log_list %} + + + + {{ proxy_log.id }} + + {{ proxy_log.user }} + {{ proxy_log.asset }} + {{ proxy_log.system_user }} + {{ proxy_log.terminal }} + {{ proxy_log.commands.all|length}} + + {% if proxy_log.is_failed %} + + {% else %} + + {% endif %} + + {% if proxy_log.is_finished %} + + + + + + + {% else %} + + + + + + + {% endif %} + {{ proxy_log.date_start }} + {{ proxy_log.date_finished|timeuntil:proxy_log.date_start }} + + {% endfor %} +{% endblock %} + +{% block content_bottom_left %} +
+
+ +
+ +
+
+
+{% endblock %} + +{% block custom_foot_js %} + + +{% endblock %} + diff --git a/apps/audits/urls/views_urls.py b/apps/audits/urls/views_urls.py index d28882704..1ad6439b3 100644 --- a/apps/audits/urls/views_urls.py +++ b/apps/audits/urls/views_urls.py @@ -4,14 +4,16 @@ from .. import views app_name = 'audits' urlpatterns = [ - url(r'^proxy-log$', views.ProxyLogListView.as_view(), - name='proxy-log-list'), - url(r'^proxy-log/(?P\d+)$', views.ProxyLogDetailView.as_view(), + url(r'^proxy-log-offline/$', views.ProxyLogOfflineListView.as_view(), + name='proxy-log-offline-list'), + url(r'^proxy-log-online/$', views.ProxyLogOnlineListView.as_view(), + name='proxy-log-online-list'), + url(r'^proxy-log/(?P\d+)/$', views.ProxyLogDetailView.as_view(), name='proxy-log-detail'), # url(r'^proxy-log/(?P\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'), - url(r'^command-log$', views.CommandLogListView.as_view(), + url(r'^command-log/$', views.CommandLogListView.as_view(), name='command-log-list'), - url(r'^login-log$', views.LoginLogListView.as_view(), + url(r'^login-log/$', views.LoginLogListView.as_view(), name='login-log-list'), ] diff --git a/apps/audits/views.py b/apps/audits/views.py index 20bc3f8aa..33762c25b 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -22,7 +22,7 @@ from audits.backends import CommandLogSerializer class ProxyLogListView(AdminUserRequiredMixin, ListView): model = ProxyLog - template_name = 'audits/proxy_log_list.html' + template_name = 'audits/proxy_log_online_list.html' context_object_name = 'proxy_log_list' paginate_by = settings.CONFIG.DISPLAY_PER_PAGE keyword = user = asset = system_user = date_from_s = date_to_s = '' @@ -89,6 +89,38 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView): return super(ProxyLogListView, self).get_context_data(**kwargs) +class ProxyLogOfflineListView(ProxyLogListView): + template_name = 'audits/proxy_log_online_list.html' + + def get_queryset(self): + queryset = super(ProxyLogOfflineListView, self).get_queryset() + queryset = queryset.filter(is_finished=True) + return queryset + + def get_context_data(self, **kwargs): + context = { + 'action': _('Proxy log offline list'), + } + kwargs.update(context) + return super(ProxyLogOfflineListView, self).get_context_data(**kwargs) + + +class ProxyLogOnlineListView(ProxyLogListView): + template_name = 'audits/proxy_log_online_list.html' + + def get_queryset(self): + queryset = super(ProxyLogOnlineListView, self).get_queryset() + queryset = queryset.filter(is_finished=False) + return queryset + + def get_context_data(self, **kwargs): + context = { + 'action': _('Proxy log online list'), + } + kwargs.update(context) + return super(ProxyLogOnlineListView, self).get_context_data(**kwargs) + + class ProxyLogDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView): diff --git a/apps/common/utils.py b/apps/common/utils.py index 3e543a107..1fa8e3915 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -2,6 +2,7 @@ # from __future__ import unicode_literals +from collections import OrderedDict from six import string_types import base64 import os @@ -53,6 +54,7 @@ def get_object_or_none(model, **kwargs): class Signer(object): + """用来加密,解密,和基于时间戳的方式验证token""" def __init__(self, secret_key=SECRET_KEY): self.secret_key = secret_key @@ -330,13 +332,13 @@ def encrypt_password(password): return None -from collections import OrderedDict def capacity_convert(size, expect='auto', rate=1000): """ - :param cap: '100MB', '1G' + :param size: '100MB', '1G' :param expect: 'K, M, G, T + :param rate: Default 1000, may be 1024 :return: """ rate_mapping = ( diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index e9b2496d8..5c421e916 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -21,16 +21,16 @@ class JMSHost(Host): # 添加密码和秘钥 if asset.get('password'): self.set_variable('ansible_ssh_pass', asset['password']) - if asset.get('key'): + if asset.get('private_key'): self.set_variable('ansible_ssh_private_key_file', asset['private_key']) # 添加become支持 - become = asset.get("become", None) - if become is not None: + become = asset.get("become", False) + if become: self.set_variable("ansible_become", True) - self.set_variable("ansible_become_method", become.get('method')) - self.set_variable("ansible_become_user", become.get('user')) - self.set_variable("ansible_become_pass", become.get('pass')) + self.set_variable("ansible_become_method", become.get('method', 'sudo')) + self.set_variable("ansible_become_user", become.get('user', 'root')) + self.set_variable("ansible_become_pass", become.get('pass', '')) else: self.set_variable("ansible_become", False) diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 1af3d7a0d..6496c4202 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -265,8 +265,10 @@ class AdHocRunner(object): result['success'].append(host) 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]) + 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 diff --git a/apps/ops/templates/ops/task_detail.html b/apps/ops/templates/ops/task_detail.html index a0f38e86c..bc8fbf682 100644 --- a/apps/ops/templates/ops/task_detail.html +++ b/apps/ops/templates/ops/task_detail.html @@ -68,7 +68,17 @@ {% trans 'Is success ' %}: + {% if object.is_finished %} {{ object.is_success|yesno:"Yes,No,Unkown" }} + {% else %} + +
+
+ 40% Complete (success) +
+
+ + {% endif %} {% trans 'Assets ' %}: @@ -84,6 +94,31 @@ + +
+
+ Result +
+ + + + + + + + + + +
+
+
+
+{{ object.result }}
+                                    
+
+
+
diff --git a/apps/ops/utils.py b/apps/ops/utils.py index d1b5190dd..0c3cbbc99 100644 --- a/apps/ops/utils.py +++ b/apps/ops/utils.py @@ -18,7 +18,7 @@ logger = get_logger(__file__) def run_AdHoc(task_tuple, assets, task_name='Ansible AdHoc runner', task_id=None, pattern='all', - record=True, verbose=False): + record=True, verbose=True): """ :param task_tuple: (('module_name', 'module_args'), ('module_name', 'module_args')) :param assets: [asset1, asset2] @@ -51,6 +51,11 @@ def run_AdHoc(task_tuple, assets, else: record = Task.objects.get(uuid=task_id) record.date_start = timezone.now() + record.date_finished = None + record.timedelta = None + record.is_finished = False + record.is_success = False + record.save() ts_start = time.time() if verbose: logger.debug('Start runner {}'.format(task_name)) @@ -61,7 +66,7 @@ def run_AdHoc(task_tuple, assets, record.date_finished = timezone.now() record.is_finished = True if verbose: - record.result = json.dumps(result) + record.result = json.dumps(result, indent=4, sort_keys=True) record.summary = json.dumps(summary) record.timedelta = timedelta if len(summary['failed']) == 0: diff --git a/apps/ops/views.py b/apps/ops/views.py index dadf71faf..145f72534 100644 --- a/apps/ops/views.py +++ b/apps/ops/views.py @@ -1,5 +1,6 @@ # ~*~ coding: utf-8 ~*~ from __future__ import unicode_literals +import time import json from datetime import datetime @@ -81,4 +82,5 @@ class TaskRunView(View): def get(self, request, *args, **kwargs): pk = kwargs.get(self.pk_url_kwarg) rerun_task.delay(pk) + time.sleep(0.5) return redirect(reverse('ops:task-detail', kwargs={'pk': pk})) diff --git a/apps/perms/tasks.py b/apps/perms/tasks.py index 50ced1ee3..6bef200cd 100644 --- a/apps/perms/tasks.py +++ b/apps/perms/tasks.py @@ -35,10 +35,10 @@ def push_users(self, assets, users): ('authorized_key', "user={} state=present key='{}'".format( user['username'], user['public_key'])), ('lineinfile', - "name=/etc/sudoers state=present regexp='^{0} ALL=(ALL)' " + "dest=/etc/sudoers state=present regexp='^{0} ALL=' " "line='{0} ALL=(ALL) NOPASSWD: {1}' " "validate='visudo -cf %s'".format( - user['username'], user.get('sudo', '/bin/whoami') + user['username'], user.get('sudo', '/sbin/ifconfig') )) ]) task_name = 'Push user {}'.format(','.join([user['name'] for user in users])) diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 6792a22b2..6b89cd330 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -56,8 +56,11 @@
  • {% trans 'Audits' %}