diff --git a/apps/assets/views/cluster.py b/apps/assets/views/cluster.py
index 835229fc1..5df58953c 100644
--- a/apps/assets/views/cluster.py
+++ b/apps/assets/views/cluster.py
@@ -60,11 +60,6 @@ class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView)
success_url = reverse_lazy('assets:cluster-list')
success_message = update_success_msg
- def form_valid(self, form):
- cluster = form.save(commit=False)
- cluster.save()
- return super().form_valid(form)
-
def get_context_data(self, **kwargs):
context = {
'app': _('assets'),
diff --git a/apps/common/api.py b/apps/common/api.py
index e8680e85e..e75f6147a 100644
--- a/apps/common/api.py
+++ b/apps/common/api.py
@@ -63,8 +63,6 @@ class LDAPTestingAPI(APIView):
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
- print(serializer.validated_data)
-
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
@@ -77,9 +75,6 @@ class LDAPTestingAPI(APIView):
except Exception as e:
return Response({"error": str(e)}, status=401)
- print(search_ou)
- print(search_filter % ({"user": "*"}))
- print(attr_map.values())
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
attributes=list(attr_map.values()))
if not ok:
diff --git a/apps/common/fields.py b/apps/common/fields.py
index 36a8bdf9a..f19689830 100644
--- a/apps/common/fields.py
+++ b/apps/common/fields.py
@@ -18,7 +18,6 @@ class DictField(forms.Field):
# we don't need to handle that explicitly.
if isinstance(value, six.string_types):
try:
- print(value)
value = json.loads(value)
return value
except json.JSONDecodeError:
diff --git a/apps/common/forms.py b/apps/common/forms.py
index 6b83c54cc..3eb8c4989 100644
--- a/apps/common/forms.py
+++ b/apps/common/forms.py
@@ -4,7 +4,9 @@ import json
from django import forms
from django.utils.translation import ugettext_lazy as _
+from django.utils.html import escape
from django.db import transaction
+from django.conf import settings
from .models import Setting
from .fields import DictField
@@ -30,28 +32,32 @@ def to_form_value(value):
class BaseForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- settings = Setting.objects.all()
+ db_settings = Setting.objects.all()
for name, field in self.fields.items():
- db_value = getattr(settings, name).value
- if db_value:
+ db_value = getattr(db_settings, name).value
+ django_value = getattr(settings, name) if hasattr(settings, name) else None
+ if db_value is False or db_value:
field.initial = to_form_value(db_value)
+ elif django_value is False or django_value:
+ field.initial = django_value
- def save(self):
+ def save(self, category="default"):
if not self.is_bound:
raise ValueError("Form is not bound")
- settings = Setting.objects.all()
+ db_settings = Setting.objects.all()
if self.is_valid():
with transaction.atomic():
for name, value in self.cleaned_data.items():
field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value:
continue
- if value == to_form_value(getattr(settings, name).value):
+ if value == to_form_value(getattr(db_settings, name).value):
continue
defaults = {
'name': name,
+ 'category': category,
'value': to_model_value(value)
}
Setting.objects.update_or_create(defaults=defaults, name=name)
@@ -126,6 +132,28 @@ class LDAPSettingForm(BaseForm):
AUTH_LDAP_START_TLS = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False
)
- AUTH_LDAP = forms.BooleanField(
- label=_("Enable LDAP Auth"), initial=False, required=False
+
+
+class TerminalSettingForm(BaseForm):
+ SORT_BY_CHOICES = (
+ ('hostname', _('Hostname')),
+ ('ip', _('IP')),
+ )
+ TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
+ choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
+ )
+ TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
+ initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
+ )
+ TERMINAL_PASSWORD_AUTH = forms.BooleanField(
+ initial=True, required=False, label=_("Password auth")
+ )
+ TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
+ initial=True, required=False, label=_("Public key auth")
+ )
+ TERMINAL_COMMAND_STORAGE = DictField(
+ label=_("Command storage"), help_text=_(
+ "Set terminal storage setting, `default` is the using as default,"
+ "You can set other storage and some terminal using"
+ )
)
diff --git a/apps/common/models.py b/apps/common/models.py
index 091b8b83a..1f634bce2 100644
--- a/apps/common/models.py
+++ b/apps/common/models.py
@@ -2,6 +2,7 @@ import json
import ldap
from django.db import models
+from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django_auth_ldap.config import LDAPSearch
@@ -24,6 +25,7 @@ class SettingManager(models.Manager):
class Setting(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
value = models.TextField(verbose_name=_("Value"))
+ category = models.CharField(max_length=128, default="default")
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
comment = models.TextField(verbose_name=_("Comment"))
@@ -33,17 +35,28 @@ class Setting(models.Model):
return self.name
@property
- def value_(self):
+ def cleaned_value(self):
try:
return json.loads(self.value)
except json.JSONDecodeError:
return None
+ @cleaned_value.setter
+ def cleaned_value(self, item):
+ try:
+ v = json.dumps(item)
+ self.value = v
+ except json.JSONDecodeError as e:
+ raise ValueError("Json dump error: {}".format(str(e)))
+
@classmethod
def refresh_all_settings(cls):
- settings_list = cls.objects.all()
- for setting in settings_list:
- setting.refresh_setting()
+ try:
+ settings_list = cls.objects.all()
+ for setting in settings_list:
+ setting.refresh_setting()
+ except (ProgrammingError, OperationalError):
+ pass
def refresh_setting(self):
try:
@@ -53,9 +66,9 @@ class Setting(models.Model):
setattr(settings, self.name, value)
if self.name == "AUTH_LDAP":
- if self.value_ and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
+ if self.cleaned_value and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
- elif not self.value_ and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
+ elif not self.cleaned_value and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
if self.name == "AUTH_LDAP_SEARCH_FILTER":
diff --git a/apps/common/templates/common/_add_terminal_command_storage_modal.html b/apps/common/templates/common/_add_terminal_command_storage_modal.html
new file mode 100644
index 000000000..678152981
--- /dev/null
+++ b/apps/common/templates/common/_add_terminal_command_storage_modal.html
@@ -0,0 +1,21 @@
+{% extends '_modal.html' %}
+{% load i18n %}
+{% block modal_id %}add_command_storage_model{% endblock %}
+{% block modal_title%}{% trans "Add command storage" %}{% endblock %}
+{% block modal_body %}
+
+{% endblock %}
+{% block modal_confirm_id %}btn_asset_import{% endblock %}
diff --git a/apps/common/templates/common/basic_setting.html b/apps/common/templates/common/basic_setting.html
index fb5039795..496eca977 100644
--- a/apps/common/templates/common/basic_setting.html
+++ b/apps/common/templates/common/basic_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/email_setting.html b/apps/common/templates/common/email_setting.html
index 7561849bd..1fd772db1 100644
--- a/apps/common/templates/common/email_setting.html
+++ b/apps/common/templates/common/email_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/ldap_setting.html b/apps/common/templates/common/ldap_setting.html
index 26f021569..f0569f873 100644
--- a/apps/common/templates/common/ldap_setting.html
+++ b/apps/common/templates/common/ldap_setting.html
@@ -20,6 +20,9 @@
{% trans 'LDAP setting' %}
+
+ {% trans 'Terminal setting' %}
+
diff --git a/apps/common/templates/common/terminal_setting.html b/apps/common/templates/common/terminal_setting.html
new file mode 100644
index 000000000..3d0b7eb6f
--- /dev/null
+++ b/apps/common/templates/common/terminal_setting.html
@@ -0,0 +1,130 @@
+{% extends 'base.html' %}
+{% load static %}
+{% load bootstrap3 %}
+{% load i18n %}
+{% load common_tags %}
+
+{% block content %}
+
+
+ {% include 'common/_add_terminal_command_storage_modal.html' %}
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
diff --git a/apps/common/urls/view_urls.py b/apps/common/urls/view_urls.py
index ff8086bde..466f7c49c 100644
--- a/apps/common/urls/view_urls.py
+++ b/apps/common/urls/view_urls.py
@@ -10,4 +10,5 @@ urlpatterns = [
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
+ url(r'^terminal/$', views.TerminalSettingView.as_view(), name='terminal-setting'),
]
diff --git a/apps/common/views.py b/apps/common/views.py
index 43b249cee..dc0d74f92 100644
--- a/apps/common/views.py
+++ b/apps/common/views.py
@@ -1,9 +1,12 @@
-from django.views.generic import View, TemplateView
+from django.views.generic import TemplateView
from django.shortcuts import render, redirect
from django.contrib import messages
from django.utils.translation import ugettext as _
+from django.conf import settings
-from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm
+from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
+ TerminalSettingForm
+from .models import Setting
from .mixins import AdminUserRequiredMixin
from .signals import ldap_auth_enable
@@ -86,3 +89,31 @@ class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
context = self.get_context_data()
context.update({"form": form})
return render(request, self.template_name, context)
+
+
+class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
+ form_class = TerminalSettingForm
+ template_name = "common/terminal_setting.html"
+
+ def get_context_data(self, **kwargs):
+ command_storage = settings.TERMINAL_COMMAND_STORAGE
+ context = {
+ 'app': _('Settings'),
+ 'action': _('Terminal setting'),
+ 'form': self.form_class(),
+ 'command_storage': command_storage,
+ }
+ kwargs.update(context)
+ return super().get_context_data(**kwargs)
+
+ def post(self, request):
+ form = self.form_class(request.POST)
+ if form.is_valid():
+ form.save()
+ msg = _("Update setting successfully, please restart program")
+ messages.success(request, msg)
+ return redirect('settings:terminal-setting')
+ else:
+ context = self.get_context_data()
+ context.update({"form": form})
+ return render(request, self.template_name, context)
diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py
index 8b0aa2a64..62ea2d657 100644
--- a/apps/jumpserver/settings.py
+++ b/apps/jumpserver/settings.py
@@ -373,7 +373,20 @@ CAPTCHA_FOREGROUND_COLOR = '#001100'
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)
CAPTCHA_TEST_MODE = CONFIG.CAPTCHA_TEST_MODE
-COMMAND_STORAGE_BACKEND = 'terminal.backends.command.db'
+COMMAND_STORAGE = {
+ 'ENGINE': 'terminal.backends.command.db',
+}
+
+TERMINAL_COMMAND_STORAGE = {
+ 'default': {
+ 'TYPE': 'server',
+ },
+ # 'ali-es': {
+ # 'TYPE': 'elasticsearch',
+ # 'HOSTS': ['http://elastic:changeme@localhost:9200'],
+ # },
+}
+
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = {
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
index 7be8a996a..f64890662 100644
Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index ebcc5ac66..ec16cc439 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/zh/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-01-17 17:26+0800\n"
+"POT-Creation-Date: 2018-01-22 11:04+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler \n"
"Language-Team: Jumpserver team\n"
@@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37
-#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:246
+#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:245
msgid "Select asset groups"
msgstr "选择资产组"
@@ -44,7 +44,7 @@ msgstr "默认使用管理用户"
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
#: assets/templates/assets/asset_group_detail.html:75 perms/forms.py:34
-#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:243
+#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:242
msgid "Select assets"
msgstr "选择资产"
@@ -66,7 +66,7 @@ msgstr "端口"
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22
-#: terminal/backends/command/models.py:11 terminal/models.py:93
+#: terminal/backends/command/models.py:11 terminal/models.py:124
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41
@@ -77,7 +77,7 @@ msgid "Asset"
msgstr "资产"
#: assets/forms.py:161 perms/forms.py:40
-#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:249
+#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:248
msgid "Select system users"
msgstr "选择系统用户"
@@ -99,14 +99,15 @@ msgstr "选择的系统用户将会在该集群资产上创建"
#: assets/templates/assets/cluster_detail.html:57
#: assets/templates/assets/cluster_list.html:19
#: assets/templates/assets/system_user_detail.html:58
-#: assets/templates/assets/system_user_list.html:24 common/models.py:25
-#: ops/models.py:31 ops/templates/ops/task_detail.html:56
-#: ops/templates/ops/task_list.html:34 perms/models.py:14
+#: assets/templates/assets/system_user_list.html:24 common/models.py:26
+#: common/templates/common/terminal_setting.html:62 ops/models.py:31
+#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34
+#: perms/models.py:14
#: perms/templates/perms/asset_permission_create_update.html:33
#: perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:25
-#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14
-#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43
+#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:23
+#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:62
@@ -126,10 +127,10 @@ msgstr "集群级别管理用户"
#: assets/forms.py:200
msgid "Password or private key password"
-msgstr "密码或秘钥不合法"
+msgstr "密码或秘钥密码"
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
-#: common/forms.py:107 users/forms.py:16 users/forms.py:24
+#: common/forms.py:113 users/forms.py:16 users/forms.py:24
#: users/templates/users/login.html:56
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
@@ -239,7 +240,7 @@ msgstr "测试环境"
#: assets/templates/assets/asset_list.html:31
#: assets/templates/assets/cluster_assets.html:52
#: assets/templates/assets/system_user_asset.html:53
-#: assets/templates/assets/user_asset_list.html:20
+#: assets/templates/assets/user_asset_list.html:20 common/forms.py:140
#: perms/templates/perms/asset_permission_asset.html:55
#: users/templates/users/login_log_list.html:52
#: users/templates/users/user_granted_asset.html:49
@@ -253,7 +254,7 @@ msgstr "IP"
#: assets/templates/assets/asset_list.html:30
#: assets/templates/assets/cluster_assets.html:51
#: assets/templates/assets/system_user_asset.html:52
-#: assets/templates/assets/user_asset_list.html:19
+#: assets/templates/assets/user_asset_list.html:19 common/forms.py:139
#: perms/templates/perms/asset_permission_asset.html:54
#: users/templates/users/user_granted_asset.html:48
#: users/templates/users/user_group_granted_asset.html:49
@@ -400,9 +401,9 @@ msgstr "创建日期"
#: assets/templates/assets/asset_group_list.html:17
#: assets/templates/assets/cluster_detail.html:97
#: assets/templates/assets/system_user_detail.html:100
-#: assets/templates/assets/system_user_list.html:30 common/models.py:28
+#: assets/templates/assets/system_user_list.html:30 common/models.py:30
#: ops/models.py:37 perms/models.py:24
-#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:22
+#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:33
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:47 users/templates/users/user_detail.html:110
#: users/templates/users/user_group_detail.html:67
@@ -494,7 +495,7 @@ msgstr "Shell"
#: assets/models/user.py:269 perms/models.py:19
#: perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
-#: terminal/backends/command/models.py:12 terminal/models.py:94
+#: terminal/backends/command/models.py:12 terminal/models.py:125
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
@@ -576,9 +577,9 @@ msgstr "仅修改你需要更新的字段"
#: assets/views/admin_user.py:106 assets/views/asset.py:48
#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:144
#: assets/views/asset.py:161 assets/views/asset.py:185
-#: assets/views/cluster.py:26 assets/views/cluster.py:85
-#: assets/views/cluster.py:102 assets/views/group.py:34
-#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87
+#: assets/views/cluster.py:26 assets/views/cluster.py:80
+#: assets/views/cluster.py:97 assets/views/group.py:34 assets/views/group.py:52
+#: assets/views/group.py:69 assets/views/group.py:87
#: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:75
#: templates/_nav.html:19
@@ -602,20 +603,24 @@ msgid "Import asset"
msgstr "导入资产"
#: assets/templates/assets/_asset_import_modal.html:9
+#: common/templates/common/_add_terminal_command_storage_modal.html:9
#: users/templates/users/_user_import_modal.html:10
msgid "Template"
msgstr "模板"
#: assets/templates/assets/_asset_import_modal.html:10
+#: common/templates/common/_add_terminal_command_storage_modal.html:10
#: users/templates/users/_user_import_modal.html:11
msgid "Download"
msgstr "下载"
#: assets/templates/assets/_asset_import_modal.html:13
+#: common/templates/common/_add_terminal_command_storage_modal.html:13
msgid "Asset csv file"
msgstr "资产csv文件"
#: assets/templates/assets/_asset_import_modal.html:16
+#: common/templates/common/_add_terminal_command_storage_modal.html:16
msgid "If set id, will use this id update asset existed"
msgstr "如果设置了id,则会使用该行信息更新该id的资产"
@@ -650,7 +655,7 @@ msgstr "自动生成秘钥"
#: assets/templates/assets/asset_update.html:47
#: assets/templates/assets/cluster_create_update.html:46
#: perms/templates/perms/asset_permission_create_update.html:45
-#: terminal/templates/terminal/terminal_update.html:40
+#: terminal/templates/terminal/terminal_update.html:41
msgid "Other"
msgstr "其它"
@@ -661,11 +666,12 @@ msgstr "其它"
#: assets/templates/assets/asset_group_create.html:16
#: assets/templates/assets/asset_update.html:55
#: assets/templates/assets/cluster_create_update.html:54
-#: common/templates/common/basic_setting.html:55
-#: common/templates/common/email_setting.html:56
-#: common/templates/common/ldap_setting.html:56
+#: common/templates/common/basic_setting.html:58
+#: common/templates/common/email_setting.html:59
+#: common/templates/common/ldap_setting.html:59
+#: common/templates/common/terminal_setting.html:82
#: perms/templates/perms/asset_permission_create_update.html:67
-#: terminal/templates/terminal/terminal_update.html:45
+#: terminal/templates/terminal/terminal_update.html:46
#: users/templates/users/_user.html:49
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_password_update.html:58
@@ -684,11 +690,12 @@ msgstr "重置"
#: assets/templates/assets/asset_list.html:53
#: assets/templates/assets/asset_update.html:56
#: assets/templates/assets/cluster_create_update.html:55
-#: common/templates/common/basic_setting.html:56
-#: common/templates/common/email_setting.html:57
-#: common/templates/common/ldap_setting.html:57
+#: common/templates/common/basic_setting.html:59
+#: common/templates/common/email_setting.html:60
+#: common/templates/common/ldap_setting.html:60
+#: common/templates/common/terminal_setting.html:83
#: perms/templates/perms/asset_permission_create_update.html:68
-#: terminal/templates/terminal/terminal_update.html:46
+#: terminal/templates/terminal/terminal_update.html:47
#: users/templates/users/_user.html:50
#: users/templates/users/first_login.html:62
#: users/templates/users/forgot_password.html:44
@@ -778,6 +785,7 @@ msgstr "资产列表"
#: assets/templates/assets/asset_group_detail.html:53
#: assets/templates/assets/cluster_assets.html:54
#: assets/templates/assets/user_asset_list.html:22
+#: common/templates/common/terminal_setting.html:63
#: users/templates/users/login_log_list.html:50
msgid "Type"
msgstr "类型"
@@ -878,7 +886,7 @@ msgid "Group"
msgstr "组"
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
-#: assets/views/cluster.py:103
+#: assets/views/cluster.py:98
msgid "Asset detail"
msgstr "资产详情"
@@ -1236,16 +1244,16 @@ msgstr "已经存在"
msgid "Cluster list"
msgstr "集群列表"
-#: assets/views/cluster.py:42 assets/views/cluster.py:70
+#: assets/views/cluster.py:42 assets/views/cluster.py:65
#: assets/views/system_user.py:96
msgid "assets"
msgstr "资产管理"
-#: assets/views/cluster.py:71
+#: assets/views/cluster.py:66
msgid "Update Cluster"
msgstr "更新Cluster"
-#: assets/views/cluster.py:86
+#: assets/views/cluster.py:81
msgid "Cluster detail"
msgstr "集群详情"
@@ -1291,81 +1299,108 @@ msgstr "%(name)s 创建成功"
msgid "%(name)s was updated successfully"
msgstr "%(name)s 更新成功"
-#: common/forms.py:64
+#: common/forms.py:70
msgid "Current SITE URL"
msgstr "当前站点URL"
-#: common/forms.py:68
+#: common/forms.py:74
msgid "User Guide URL"
msgstr "用户向导URL"
-#: common/forms.py:69
+#: common/forms.py:75
msgid "User first login update profile done redirect to it"
msgstr "用户第一次登录,修改profile后重定向到地址"
-#: common/forms.py:72
+#: common/forms.py:78
msgid "Email Subject Prefix"
msgstr "Email主题前缀"
-#: common/forms.py:79
+#: common/forms.py:85
msgid "SMTP host"
msgstr "SMTP主机"
-#: common/forms.py:81
+#: common/forms.py:87
msgid "SMTP port"
msgstr "SMTP端口"
-#: common/forms.py:83
+#: common/forms.py:89
msgid "SMTP user"
msgstr "SMTP账号"
-#: common/forms.py:86
+#: common/forms.py:92
msgid "SMTP password"
msgstr "SMTP密码"
-#: common/forms.py:87
+#: common/forms.py:93
msgid "Some provider use token except password"
msgstr "一些邮件提供商需要输入的是Token"
-#: common/forms.py:90 common/forms.py:127
+#: common/forms.py:96 common/forms.py:133
msgid "Use SSL"
msgstr "使用SSL"
-#: common/forms.py:91
+#: common/forms.py:97
msgid "If SMTP port is 465, may be select"
msgstr "如果SMTP端口是465,通常需要启用SSL"
-#: common/forms.py:94
+#: common/forms.py:100
msgid "Use TLS"
msgstr "使用TLS"
-#: common/forms.py:95
+#: common/forms.py:101
msgid "If SMTP port is 587, may be select"
msgstr "如果SMTP端口是587,通常需要启用TLS"
-#: common/forms.py:101
+#: common/forms.py:107
msgid "LDAP server"
msgstr "LDAP地址"
-#: common/forms.py:104
+#: common/forms.py:110
msgid "Bind DN"
msgstr "绑定DN"
-#: common/forms.py:111
+#: common/forms.py:117
msgid "User OU"
msgstr "用户OU"
-#: common/forms.py:114
+#: common/forms.py:120
msgid "User search filter"
msgstr "用户过滤器"
-#: common/forms.py:117
+#: common/forms.py:123
msgid "User attr map"
msgstr "LDAP属性映射"
-#: common/forms.py:130
-msgid "Enable LDAP Auth"
-msgstr "开启LDAP认证"
+#: common/forms.py:143
+msgid "List sort by"
+msgstr "资产列表排序"
+
+#: common/forms.py:146
+msgid "Heartbeat interval"
+msgstr "心跳间隔"
+
+#: common/forms.py:146 ops/models.py:32
+msgid "Units: seconds"
+msgstr "单位: 秒"
+
+#: common/forms.py:149
+msgid "Password auth"
+msgstr "密码认证"
+
+#: common/forms.py:152
+msgid "Public key auth"
+msgstr "秘钥认证"
+
+#: common/forms.py:155 common/templates/common/terminal_setting.html:58
+#: terminal/models.py:27
+msgid "Command storage"
+msgstr "命令存储"
+
+#: common/forms.py:156
+msgid ""
+"Set terminal storage setting, `default` is the using as default,You can set "
+"other storage and some terminal using"
+msgstr "设置终端命令存储,default是默认用的存储方式"
#: common/mixins.py:29
msgid "is discard"
@@ -1375,43 +1410,62 @@ msgstr ""
msgid "discard time"
msgstr ""
-#: common/models.py:26
+#: common/models.py:27
msgid "Value"
msgstr "值"
-#: common/models.py:27
+#: common/models.py:29
msgid "Enabled"
msgstr "启用"
+#: common/templates/common/_add_terminal_command_storage_modal.html:4
+msgid "Add command storage"
+msgstr "添加命令存储"
+
#: common/templates/common/basic_setting.html:15
#: common/templates/common/email_setting.html:15
-#: common/templates/common/ldap_setting.html:15 common/views.py:18
+#: common/templates/common/ldap_setting.html:15
+#: common/templates/common/terminal_setting.html:15
+#: common/templates/common/terminal_setting.html:38 common/views.py:21
msgid "Basic setting"
msgstr "基本设置"
#: common/templates/common/basic_setting.html:18
#: common/templates/common/email_setting.html:18
-#: common/templates/common/ldap_setting.html:18 common/views.py:44
+#: common/templates/common/ldap_setting.html:18
+#: common/templates/common/terminal_setting.html:18 common/views.py:47
msgid "Email setting"
msgstr "邮件设置"
#: common/templates/common/basic_setting.html:21
#: common/templates/common/email_setting.html:21
-#: common/templates/common/ldap_setting.html:21 common/views.py:70
+#: common/templates/common/ldap_setting.html:21
+#: common/templates/common/terminal_setting.html:21 common/views.py:73
msgid "LDAP setting"
msgstr "LDAP设置"
-#: common/templates/common/email_setting.html:55
-#: common/templates/common/ldap_setting.html:55
+#: common/templates/common/basic_setting.html:24
+#: common/templates/common/email_setting.html:24
+#: common/templates/common/ldap_setting.html:24
+#: common/templates/common/terminal_setting.html:24 common/views.py:102
+msgid "Terminal setting"
+msgstr "终端设置"
+
+#: common/templates/common/email_setting.html:58
+#: common/templates/common/ldap_setting.html:58
msgid "Test connection"
msgstr "测试连接"
-#: common/views.py:17 common/views.py:43 common/views.py:69
+#: common/templates/common/terminal_setting.html:77 terminal/models.py:28
+msgid "Replay storage"
+msgstr "录像存储"
+
+#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:101
#: templates/_nav.html:69
msgid "Settings"
msgstr "系统设置"
-#: common/views.py:28 common/views.py:54 common/views.py:82
+#: common/views.py:31 common/views.py:57 common/views.py:85 common/views.py:113
msgid "Update setting successfully, please restart program"
msgstr "更新设置成功, 请手动重启程序"
@@ -1419,10 +1473,6 @@ msgstr "更新设置成功, 请手动重启程序"
msgid "Interval"
msgstr "间隔"
-#: ops/models.py:32
-msgid "Units: seconds"
-msgstr "单位: 秒"
-
#: ops/models.py:33
msgid "Crontab"
msgstr "Crontab"
@@ -1566,7 +1616,7 @@ msgstr "执行历史"
#: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:58
-#: ops/templates/ops/task_history.html:55 terminal/models.py:101
+#: ops/templates/ops/task_history.html:55 terminal/models.py:132
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
@@ -1683,18 +1733,18 @@ msgid "Task run history"
msgstr "执行历史"
#: perms/forms.py:16 users/forms.py:147 users/forms.py:152 users/forms.py:164
-#: users/forms.py:195
+#: users/forms.py:194
msgid "Select users"
msgstr "选择用户"
#: perms/forms.py:18 perms/models.py:15
#: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
-#: terminal/backends/command/models.py:10 terminal/models.py:92
+#: terminal/backends/command/models.py:10 terminal/models.py:123
#: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
-#: terminal/templates/terminal/session_list.html:71 users/forms.py:191
+#: terminal/templates/terminal/session_list.html:71 users/forms.py:190
#: users/models/user.py:30 users/templates/users/user_group_detail.html:78
#: users/views/user.py:337
msgid "User"
@@ -1949,7 +1999,7 @@ msgstr "在线会话"
msgid "Session offline"
msgstr "离线会话"
-#: templates/_nav.html:53 terminal/models.py:99
+#: templates/_nav.html:53 terminal/models.py:130
#: terminal/templates/terminal/command_list.html:55
#: terminal/templates/terminal/command_list.html:71
#: terminal/templates/terminal/session_detail.html:48
@@ -1998,56 +2048,56 @@ msgstr "SSH 监听端口"
msgid "Coco http/ws listen port"
msgstr "Http/Websocket 监听端口"
-#: terminal/models.py:15
+#: terminal/models.py:24
msgid "Remote Address"
msgstr "远端地址"
-#: terminal/models.py:16
+#: terminal/models.py:25
msgid "SSH Port"
msgstr "SSH端口"
-#: terminal/models.py:17
+#: terminal/models.py:26
msgid "HTTP Port"
msgstr "HTTP端口"
-#: terminal/models.py:68
+#: terminal/models.py:99
msgid "Session Online"
msgstr "在线会话"
-#: terminal/models.py:69
+#: terminal/models.py:100
msgid "CPU Usage"
msgstr "CPU使用"
-#: terminal/models.py:70
+#: terminal/models.py:101
msgid "Memory Used"
msgstr "内存使用"
-#: terminal/models.py:71
+#: terminal/models.py:102
msgid "Connections"
msgstr "连接数"
-#: terminal/models.py:72
+#: terminal/models.py:103
msgid "Threads"
msgstr "线程数"
-#: terminal/models.py:73
+#: terminal/models.py:104
msgid "Boot Time"
msgstr "运行时间"
-#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74
+#: terminal/models.py:127 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr"
msgstr "远端地址"
-#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100
+#: terminal/models.py:129 terminal/templates/terminal/session_list.html:100
msgid "Replay"
msgstr "回放"
-#: terminal/models.py:102
+#: terminal/models.py:133
msgid "Date end"
msgstr "结束日期"
-#: terminal/models.py:119
+#: terminal/models.py:150
msgid "Args"
msgstr "参数"
@@ -2810,5 +2860,8 @@ msgstr "密码更新"
msgid "Public key update"
msgstr "秘钥更新"
+#~ msgid "Enable LDAP Auth"
+#~ msgstr "LDAP认证"
+
#~ msgid "Connect"
#~ msgstr "连接"
diff --git a/apps/terminal/api.py b/apps/terminal/api.py
index 87311d5b2..279ec0121 100644
--- a/apps/terminal/api.py
+++ b/apps/terminal/api.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
from collections import OrderedDict
-import copy
import logging
import os
import uuid
@@ -21,7 +20,8 @@ from .serializers import TerminalSerializer, StatusSerializer, \
SessionSerializer, TaskSerializer, ReplaySerializer
from .hands import IsSuperUserOrAppUser, IsAppUser, \
IsSuperUserOrAppUserOrUserReadonly
-from .backends import get_command_store, SessionCommandSerializer
+from .backends import get_command_store, get_multi_command_store, \
+ SessionCommandSerializer
logger = logging.getLogger(__file__)
@@ -197,6 +197,7 @@ class CommandViewSet(viewsets.ViewSet):
"""
command_store = get_command_store()
+ multi_command_storage = get_multi_command_store()
serializer_class = SessionCommandSerializer
permission_classes = (IsSuperUserOrAppUser,)
@@ -217,7 +218,7 @@ class CommandViewSet(viewsets.ViewSet):
return Response({"msg": msg}, status=401)
def list(self, request, *args, **kwargs):
- queryset = list(self.command_store.all())
+ queryset = self.multi_command_storage.filter()
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data)
@@ -260,3 +261,13 @@ class SessionReplayViewSet(viewsets.ViewSet):
return redirect(url)
else:
return HttpResponseNotFound()
+
+
+class TerminalConfig(APIView):
+ permission_classes = (IsAppUser,)
+
+ def get(self, request):
+ user = request.user
+ terminal = user.terminal
+ configs = terminal.config
+ return Response(configs, status=200)
diff --git a/apps/terminal/backends/__init__.py b/apps/terminal/backends/__init__.py
index 6baaed3c4..33f63f4de 100644
--- a/apps/terminal/backends/__init__.py
+++ b/apps/terminal/backends/__init__.py
@@ -2,9 +2,39 @@ from importlib import import_module
from django.conf import settings
from .command.serializers import SessionCommandSerializer
+TYPE_ENGINE_MAPPING = {
+ 'elasticsearch': 'terminal.backends.command.es',
+}
+
def get_command_store():
- command_engine = import_module(settings.COMMAND_STORAGE_BACKEND)
- command_store = command_engine.CommandStore()
- return command_store
+ params = settings.COMMAND_STORAGE
+ engine_class = import_module(params['ENGINE'])
+ storage = engine_class.CommandStore(params)
+ return storage
+
+
+def get_terminal_command_store():
+ storage_list = {}
+ for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
+ tp = params['TYPE']
+ if tp == 'server':
+ storage = get_command_store()
+ else:
+ if not TYPE_ENGINE_MAPPING.get(tp):
+ raise AssertionError("Command storage type should in {}".format(
+ ', '.join(TYPE_ENGINE_MAPPING.keys()))
+ )
+ engine_class = import_module(TYPE_ENGINE_MAPPING[tp])
+ storage = engine_class.CommandStore(params)
+ storage_list[name] = storage
+ return storage_list
+
+
+def get_multi_command_store():
+ from .command.multi import CommandStore
+ storage_list = get_terminal_command_store().values()
+ storage = CommandStore(storage_list)
+ return storage
+
diff --git a/apps/terminal/backends/command/base.py b/apps/terminal/backends/command/base.py
index 0aa689738..585930a5d 100644
--- a/apps/terminal/backends/command/base.py
+++ b/apps/terminal/backends/command/base.py
@@ -19,3 +19,9 @@ class CommandBase(object):
input=None, session=None):
pass
+ @abc.abstractmethod
+ def count(self, date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
+ pass
+
diff --git a/apps/terminal/backends/command/db.py b/apps/terminal/backends/command/db.py
index 27ee19a14..18652a26f 100644
--- a/apps/terminal/backends/command/db.py
+++ b/apps/terminal/backends/command/db.py
@@ -8,7 +8,7 @@ from .base import CommandBase
class CommandStore(CommandBase):
- def __init__(self):
+ def __init__(self, params):
from terminal.models import Command
self.model = Command
@@ -37,9 +37,11 @@ class CommandStore(CommandBase):
))
return self.model.objects.bulk_create(_commands)
- def filter(self, date_from=None, date_to=None,
- user=None, asset=None, system_user=None,
- input=None, session=None):
+ @staticmethod
+ def make_filter_kwargs(
+ date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
filter_kwargs = {}
date_from_default = timezone.now() - datetime.timedelta(days=7)
date_to_default = timezone.now()
@@ -60,10 +62,28 @@ class CommandStore(CommandBase):
if session:
filter_kwargs['session'] = session
+ return filter_kwargs
+
+ def filter(self, date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
+ filter_kwargs = self.make_filter_kwargs(
+ date_from=date_from, date_to=date_to, user=user,
+ asset=asset, system_user=system_user, input=input,
+ session=session,
+ )
queryset = self.model.objects.filter(**filter_kwargs)
- return queryset
+ return [command.to_dict() for command in queryset]
+
+ def count(self, date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
+ filter_kwargs = self.make_filter_kwargs(
+ date_from=date_from, date_to=date_to, user=user,
+ asset=asset, system_user=system_user, input=input,
+ session=session,
+ )
+ count = self.model.objects.filter(**filter_kwargs).count()
+ return count
- def all(self):
- """返回所有数据"""
- return self.model.objects.iterator()
diff --git a/apps/terminal/backends/command/es.py b/apps/terminal/backends/command/es.py
new file mode 100644
index 000000000..2e0995ad9
--- /dev/null
+++ b/apps/terminal/backends/command/es.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+#
+
+from jms_es_sdk import ESStore
+from .base import CommandBase
+
+
+class CommandStore(CommandBase, ESStore):
+ def __init__(self, params):
+ hosts = params.get('HOSTS', ['http://localhost'])
+ ESStore.__init__(self, hosts=hosts)
+
+ def save(self, command):
+ return ESStore.save(self, command)
+
+ def bulk_save(self, commands):
+ return ESStore.bulk_save(self, commands)
+
+ def filter(self, date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
+
+ data = ESStore.filter(
+ self, date_from=date_from, date_to=date_to,
+ user=user, asset=asset, system_user=system_user,
+ input=input, session=session
+ )
+ return [item["_source"] for item in data["hits"] if item]
+
+ def count(self, date_from=None, date_to=None,
+ user=None, asset=None, system_user=None,
+ input=None, session=None):
+ amount = ESStore.count(
+ self, date_from=date_from, date_to=date_to,
+ user=user, asset=asset, system_user=system_user,
+ input=input, session=session
+ )
+ return amount
diff --git a/apps/terminal/backends/command/models.py b/apps/terminal/backends/command/models.py
index 2d1387427..186769b48 100644
--- a/apps/terminal/backends/command/models.py
+++ b/apps/terminal/backends/command/models.py
@@ -18,5 +18,26 @@ class AbstractSessionCommand(models.Model):
class Meta:
abstract = True
+ @classmethod
+ def from_dict(cls, d):
+ self = cls()
+ for k, v in d.items():
+ setattr(self, k, v)
+ return self
+
+ @classmethod
+ def from_multi_dict(cls, l):
+ commands = []
+ for d in l:
+ command = cls.from_dict(d)
+ commands.append(command)
+ return commands
+
+ def to_dict(self):
+ d = {}
+ for field in self._meta.fields:
+ d[field.name] = getattr(self, field.name)
+ return d
+
def __str__(self):
return self.input
diff --git a/apps/terminal/backends/command/multi.py b/apps/terminal/backends/command/multi.py
new file mode 100644
index 000000000..8d51cfd38
--- /dev/null
+++ b/apps/terminal/backends/command/multi.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+#
+
+from .base import CommandBase
+
+
+class CommandStore(CommandBase):
+ def __init__(self, storage_list):
+ self.storage_list = storage_list
+
+ def filter(self, **kwargs):
+ queryset = []
+ for storage in self.storage_list:
+ queryset.extend(storage.filter(**kwargs))
+ return sorted(queryset, key=lambda command: command["timestamp"])
+
+ def count(self, **kwargs):
+ amount = 0
+ for storage in self.storage_list:
+ amount += storage.count(**kwargs)
+ return amount
+
+ def save(self, command):
+ pass
+
+ def bulk_save(self, commands):
+ pass
\ No newline at end of file
diff --git a/apps/terminal/forms.py b/apps/terminal/forms.py
index 5b6278c02..4253da70a 100644
--- a/apps/terminal/forms.py
+++ b/apps/terminal/forms.py
@@ -10,7 +10,7 @@ from .models import Terminal
class TerminalForm(forms.ModelForm):
class Meta:
model = Terminal
- fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment']
+ fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment', 'command_storage']
help_texts = {
'ssh_port': _("Coco ssh listen port"),
'http_port': _("Coco http/ws listen port"),
diff --git a/apps/terminal/models.py b/apps/terminal/models.py
index f545baa5f..617977946 100644
--- a/apps/terminal/models.py
+++ b/apps/terminal/models.py
@@ -4,17 +4,28 @@ import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
from users.models import User
from .backends.command.models import AbstractSessionCommand
+def get_all_command_storage():
+ # storage_choices = []
+ from common.models import Setting
+ Setting.refresh_all_settings()
+ for k, v in settings.TERMINAL_COMMAND_STORAGE.items():
+ yield (k, k)
+
+
class Terminal(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name'))
remote_addr = models.CharField(max_length=128, verbose_name=_('Remote Address'))
ssh_port = models.IntegerField(verbose_name=_('SSH Port'), default=2222)
http_port = models.IntegerField(verbose_name=_('HTTP Port'), default=5000)
+ command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default', choices=get_all_command_storage())
+ replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default')
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE)
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
is_deleted = models.BooleanField(default=False)
@@ -33,6 +44,26 @@ class Terminal(models.Model):
self.user.is_active = active
self.user.save()
+ def get_common_storage(self):
+ storage_all = settings.TERMINAL_COMMAND_STORAGE
+ if self.command_storage in storage_all:
+ storage = storage_all.get(self.command_storage)
+ else:
+ storage = storage_all.get('default')
+ return {"TERMINAL_COMMAND_STORAGE": storage}
+
+ def get_replay_storage(self):
+ pass
+
+ @property
+ def config(self):
+ configs = {}
+ for k in dir(settings):
+ if k.startswith('TERMINAL'):
+ configs[k] = getattr(settings, k)
+ configs.update(self.get_common_storage())
+ return configs
+
def create_app_user(self):
random = uuid.uuid4().hex[:6]
user, access_key = User.create_app_user(name="{}-{}".format(self.name, random), comment=self.comment)
diff --git a/apps/terminal/serializers.py b/apps/terminal/serializers.py
index 52b4d2b3c..275853411 100644
--- a/apps/terminal/serializers.py
+++ b/apps/terminal/serializers.py
@@ -5,7 +5,7 @@ from django.utils import timezone
from rest_framework import serializers
from .models import Terminal, Status, Session, Task
-from .backends import get_command_store
+from .backends import get_multi_command_store
class TerminalSerializer(serializers.ModelSerializer):
@@ -43,14 +43,14 @@ class TerminalSerializer(serializers.ModelSerializer):
class SessionSerializer(serializers.ModelSerializer):
command_amount = serializers.SerializerMethodField()
- command_store = get_command_store()
+ command_store = get_multi_command_store()
class Meta:
model = Session
fields = '__all__'
def get_command_amount(self, obj):
- return len(self.command_store.filter(session=obj.session))
+ return self.command_store.count(session=str(obj.id))
class StatusSerializer(serializers.ModelSerializer):
diff --git a/apps/terminal/templates/terminal/terminal_modal_accept.html b/apps/terminal/templates/terminal/terminal_modal_accept.html
index b4a18b17c..fe70bb342 100644
--- a/apps/terminal/templates/terminal/terminal_modal_accept.html
+++ b/apps/terminal/templates/terminal/terminal_modal_accept.html
@@ -12,6 +12,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %}
+ {% bootstrap_field form.command_storage layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
diff --git a/apps/terminal/templates/terminal/terminal_update.html b/apps/terminal/templates/terminal/terminal_update.html
index 9ebd9d2d8..cbf745608 100644
--- a/apps/terminal/templates/terminal/terminal_update.html
+++ b/apps/terminal/templates/terminal/terminal_update.html
@@ -35,6 +35,7 @@
{% bootstrap_field form.remote_addr layout="horizontal" %}
{% bootstrap_field form.ssh_port layout="horizontal" %}
{% bootstrap_field form.http_port layout="horizontal" %}
+ {% bootstrap_field form.command_storage layout="horizontal" %}
{% trans 'Other' %}
diff --git a/apps/terminal/templatetags/terminal_tags.py b/apps/terminal/templatetags/terminal_tags.py
index 22b517880..cd7120fec 100644
--- a/apps/terminal/templatetags/terminal_tags.py
+++ b/apps/terminal/templatetags/terminal_tags.py
@@ -1,13 +1,12 @@
# ~*~ coding: utf-8 ~*~
from django import template
-from ..backends import get_command_store
+from ..backends import get_multi_command_store
register = template.Library()
-command_store = get_command_store()
+command_store = get_multi_command_store()
@register.filter
def get_session_command_amount(session_id):
- return len(command_store.filter(session=str(session_id)))
-
+ return command_store.count(session=session_id)
diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py
index 55d77ee00..a3ebc2129 100644
--- a/apps/terminal/urls/api_urls.py
+++ b/apps/terminal/urls/api_urls.py
@@ -20,7 +20,8 @@ urlpatterns = [
url(r'^v1/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$',
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
name='session-replay'),
- url(r'^v1/terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key')
+ url(r'^v1/terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'),
+ url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
]
urlpatterns += router.urls
diff --git a/apps/terminal/views/command.py b/apps/terminal/views/command.py
index 8b0479d3e..6fdf905bd 100644
--- a/apps/terminal/views/command.py
+++ b/apps/terminal/views/command.py
@@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
from common.mixins import DatetimeSearchMixin
from ..models import Command
from .. import utils
-from ..backends import get_command_store
+from ..backends import get_multi_command_store
__all__ = ['CommandListView']
-command_store = get_command_store()
+common_storage = get_multi_command_store()
class CommandListView(DatetimeSearchMixin, ListView):
@@ -39,7 +39,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
filter_kwargs['system_user'] = self.system_user
if self.command:
filter_kwargs['input'] = self.command
- queryset = command_store.filter(**filter_kwargs)
+ queryset = common_storage.filter(**filter_kwargs)
return queryset
def get_context_data(self, **kwargs):
diff --git a/apps/terminal/views/session.py b/apps/terminal/views/session.py
index 22d04fbaf..0970cade2 100644
--- a/apps/terminal/views/session.py
+++ b/apps/terminal/views/session.py
@@ -10,7 +10,7 @@ from django.conf import settings
from users.utils import AdminUserRequiredMixin
from common.mixins import DatetimeSearchMixin
from ..models import Session, Command, Terminal
-from ..backends import get_command_store
+from ..backends import get_multi_command_store
from .. import utils
@@ -19,7 +19,7 @@ __all__ = [
'SessionDetailView',
]
-command_store = get_command_store()
+command_store = get_multi_command_store()
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
diff --git a/apps/users/forms.py b/apps/users/forms.py
index 018c39a18..b239f58ee 100644
--- a/apps/users/forms.py
+++ b/apps/users/forms.py
@@ -172,7 +172,6 @@ class UserBulkUpdateForm(forms.ModelForm):
if self.data.get(field) is not None:
changed_fields.append(field)
- print(changed_fields)
cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields}
users = cleaned_data.pop('users', '')