Commit 99b4c66b authored by ibuler's avatar ibuler

[Feature] 添加signals 解耦代码

parent cbc00069
...@@ -181,10 +181,11 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView): ...@@ -181,10 +181,11 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView):
asset_id = kwargs.get('pk') asset_id = kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id) asset = get_object_or_404(Asset, pk=asset_id)
summary = update_assets_hardware_info([asset]) summary = update_assets_hardware_info([asset])
if len(summary['failed']) == 0: print(summary)
return super(AssetRefreshHardwareView, self).retrieve(request, *args, **kwargs) if summary.get('dark'):
return Response(summary['dark'].values(), status=501)
else: else:
return Response('', status=502) return Response({"msg": "ok"})
class AssetAdminUserTestView(AssetRefreshHardwareView): class AssetAdminUserTestView(AssetRefreshHardwareView):
......
...@@ -5,3 +5,8 @@ from django.apps import AppConfig ...@@ -5,3 +5,8 @@ from django.apps import AppConfig
class AssetsConfig(AppConfig): class AssetsConfig(AppConfig):
name = 'assets' name = 'assets'
def ready(self):
from .signals import on_app_ready
on_app_ready.send(self.__class__)
super().ready()
# -*- coding: utf-8 -*-
#
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
...@@ -10,52 +10,26 @@ logger = get_logger(__file__) ...@@ -10,52 +10,26 @@ logger = get_logger(__file__)
class AssetCreateForm(forms.ModelForm): class AssetCreateForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(
widget=forms.PasswordInput, max_length=100,
strip=True, required=False,
help_text=_('If also set private key, use that first'),
)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
obj = super().save(commit=commit)
password = self.cleaned_data['password']
private_key = self.cleaned_data['private_key_file']
if password:
obj.password = password
if private_key:
obj.private_key = private_key
obj.save()
return obj
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
if private_key_file:
private_key = private_key_file.read()
if not validate_ssh_private_key(private_key):
raise forms.ValidationError(_('Invalid private key'))
return private_key
return private_key_file
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
'cluster', 'groups', 'status', 'env', 'is_active', 'username', 'cluster', 'groups', 'status', 'env', 'is_active',
'admin_user'
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple( 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
attrs={'class': 'select2', 'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}),
'data-placeholder': _('Select asset groups')}), 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}),
} }
help_texts = { help_texts = {
'hostname': '* required', 'hostname': '* required',
'ip': '* required', 'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _('Host level admin user, If not set using cluster admin user default')
} }
...@@ -65,16 +39,18 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -65,16 +39,18 @@ class AssetUpdateForm(forms.ModelForm):
fields = [ fields = [
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active', 'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no', 'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
'cabinet_pos', 'number', 'comment' 'cabinet_pos', 'number', 'comment', 'admin_user',
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple( 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
attrs={'class': 'select2', 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _("Default using cluster admin user")})
'data-placeholder': _('Select asset groups')}),
} }
help_texts = { help_texts = {
'hostname': '* required', 'hostname': '* required',
'ip': '* required', 'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _('Host level admin user, If not set using cluster admin user default')
} }
......
...@@ -3,17 +3,13 @@ ...@@ -3,17 +3,13 @@
# #
import uuid import uuid
import os
import logging import logging
from hashlib import md5
from django.db import models from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache from django.core.cache import cache
from common.utils import signer, ssh_key_string_to_obj from ..const import ADMIN_USER_CONN_CACHE_KEY_PREFIX
from .utils import private_key_validator
from .cluster import Cluster from .cluster import Cluster
from .group import AssetGroup from .group import AssetGroup
from .user import AdminUser, SystemUser from .user import AdminUser, SystemUser
...@@ -59,9 +55,7 @@ class Asset(models.Model): ...@@ -59,9 +55,7 @@ class Asset(models.Model):
status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status')) status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status'))
# Auth # Auth
username = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('Username')) admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
# Some information # Some information
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
...@@ -105,39 +99,22 @@ class Asset(models.Model): ...@@ -105,39 +99,22 @@ class Asset(models.Model):
return False, warning return False, warning
@property @property
def password(self): def hardware_info(self):
if self._password: if self.cpu_count:
return signer.unsign(self._password) return '{} Core {} {}'.format(
self.cpu_count * self.cpu_cores,
self.memory, self.disk_total
)
else: else:
return '' return ''
@password.setter
def password(self, password_raw):
self._password = signer.sign(password_raw)
@property @property
def private_key(self): def is_connective(self):
if self._private_key: val = cache.get(ADMIN_USER_CONN_CACHE_KEY_PREFIX + self.hostname)
key_str = signer.unsign(self._private_key) if val == 1:
return ssh_key_string_to_obj(key_str) return True
else: else:
return None return False
@private_key.setter
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.encode()).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
def to_json(self): def to_json(self):
return { return {
...@@ -148,25 +125,28 @@ class Asset(models.Model): ...@@ -148,25 +125,28 @@ class Asset(models.Model):
'groups': [group.name for group in self.groups.all()], 'groups': [group.name for group in self.groups.all()],
} }
def is_connective(self):
return cache.get(self.hostname)
def _to_secret_json(self): def _to_secret_json(self):
""" """
Ansible use it create inventory Ansible use it create inventory, First using asset user,
otherwise using cluster admin user
Todo: May be move to ops implements it Todo: May be move to ops implements it
""" """
data = self.to_json() data = self.to_json()
if self.cluster and self.cluster.admin_user: admin_user = None
if self.admin_user:
admin_user = self.admin_user
elif self.cluster and self.cluster.admin_user:
admin_user = self.cluster.admin_user
if admin_user:
data.update({ data.update({
'username': self.cluster.admin_user.username, 'username': admin_user.username,
'password': self.cluster.admin_user.password, 'password': admin_user.password,
'private_key': self.cluster.admin_user.private_key_file, 'private_key': admin_user.private_key_file,
'become': { 'become': {
'method': self.cluster.admin_user.become_method, 'method': admin_user.become_method,
'user': self.cluster.admin_user.become_user, 'user': admin_user.become_user,
'pass': self.cluster.admin_user.become_pass, 'pass': admin_user.become_pass,
} }
}) })
return data return data
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from __future__ import unicode_literals
import logging import logging
import uuid import uuid
...@@ -18,7 +16,7 @@ logger = logging.getLogger(__name__) ...@@ -18,7 +16,7 @@ logger = logging.getLogger(__name__)
class Cluster(models.Model): class Cluster(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=32, verbose_name=_('Name')) name = models.CharField(max_length=32, verbose_name=_('Name'))
admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.CASCADE, verbose_name=_("Admin user")) admin_user = models.ForeignKey('assets.AdminUser', null=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth')) bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact')) contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone')) phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
......
...@@ -96,12 +96,16 @@ class AdminUser(models.Model): ...@@ -96,12 +96,16 @@ class AdminUser(models.Model):
def become_pass(self, password): def become_pass(self, password):
self._become_pass = signer.sign(password) self._become_pass = signer.sign(password)
def get_related_assets(self):
assets = []
for cluster in self.cluster_set.all():
assets.extend(cluster.assets.all())
assets.extend(self.asset_set.all())
return list(set(assets))
@property @property
def assets_amount(self): def assets_amount(self):
amount = 0 return len(self.get_related_assets())
for cluster in self.cluster_set.all():
amount += cluster.assets.all().count()
return amount
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
...@@ -209,9 +213,14 @@ class SystemUser(models.Model): ...@@ -209,9 +213,14 @@ class SystemUser(models.Model):
'private_key_file': self.private_key_file, 'private_key_file': self.private_key_file,
} }
def get_clusters_assets(self):
from .asset import Asset
clusters = self.cluster.all()
return Asset.objects.filter(cluster__in=clusters)
@property @property
def assets_amount(self): def assets_amount(self):
return self.assets.count() return len(self.get_clusters_assets())
def to_json(self): def to_json(self):
return { return {
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.cache import cache from django.core.cache import cache
from rest_framework import viewsets, serializers, generics from rest_framework import viewsets, serializers, generics
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin
from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser
from .tasks import SYSTEM_USER_CONN_CACHE_KEY_PREFIX, ADMIN_USER_CONN_CACHE_KEY_PREFIX from .tasks import SYSTEM_USER_CONN_CACHE_KEY_PREFIX, ADMIN_USER_CONN_CACHE_KEY_PREFIX
...@@ -140,36 +141,18 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer): ...@@ -140,36 +141,18 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# system_users = SystemUserSerializer(many=True, read_only=True)
# admin_user = AdminUserSerializer(many=False, read_only=True)
hardware = serializers.SerializerMethodField()
is_online = serializers.SerializerMethodField()
class Meta(object): class Meta(object):
model = Asset model = Asset
list_serializer_class = BulkListSerializer list_serializer_class = BulkListSerializer
fields = '__all__' fields = '__all__'
validators = [] # If not set to [], partial bulk update will be error
@staticmethod
def get_hardware(obj):
if obj.cpu_count:
return '{} Core {} {}'.format(obj.cpu_count*obj.cpu_cores, obj.memory, obj.disk_total)
else:
return ''
@staticmethod
def get_is_online(obj):
hostname = obj.hostname
if cache.get(hostname) == '1':
return True
elif cache.get(hostname) == '0':
return False
else:
return 'Unknown'
def get_field_names(self, declared_fields, info): def get_field_names(self, declared_fields, info):
fields = super(AssetSerializer, self).get_field_names(declared_fields, info) fields = super().get_field_names(declared_fields, info)
fields.extend(['get_type_display', 'get_env_display']) fields.extend([
'get_type_display', 'get_env_display',
'hardware_info', 'is_connective',
])
return fields return fields
......
# -*- coding: utf-8 -*-
#
from django.dispatch import Signal, receiver
from common.utils import get_logger
logger = get_logger(__file__)
on_asset_created = Signal(providing_args=['asset'])
on_app_ready = Signal()
@receiver(on_asset_created)
def update_asset_info(sender, asset=None, **kwargs):
from .tasks import update_assets_hardware_info
logger.debug("Receive asset create signal, update asset hardware info")
update_assets_hardware_info.delay([asset])
@receiver(on_asset_created)
def test_admin_user_connective(sender, asset=None, **kwargs):
from .tasks import test_admin_user_connectability_manual
logger.debug("Receive asset create signal, test admin user connectability")
test_admin_user_connectability_manual.delay(asset)
@receiver(on_app_ready)
def test_admin_user_on_app_ready(sender, **kwargs):
from .tasks import test_admin_user_connectability_period
logger.debug("Receive app ready signal, test admin connectability")
test_admin_user_connectability_period.delay()
...@@ -4,16 +4,14 @@ import json ...@@ -4,16 +4,14 @@ import json
from celery import shared_task from celery import shared_task
from django.core.cache import cache from django.core.cache import cache
from assets.models import SystemUser, AdminUser
from common.utils import get_object_or_none, capacity_convert, sum_capacity, encrypt_password, get_logger from common.utils import get_object_or_none, capacity_convert, sum_capacity, encrypt_password, get_logger
from .models import Asset from .models import SystemUser, AdminUser, Asset
from .const import ADMIN_USER_CONN_CACHE_KEY_PREFIX, SYSTEM_USER_CONN_CACHE_KEY_PREFIX
FORKS = 10 FORKS = 10
TIMEOUT = 60 TIMEOUT = 60
logger = get_logger(__file__) logger = get_logger(__file__)
ADMIN_USER_CONN_CACHE_KEY_PREFIX = "ADMIN_USER_CONN_"
SYSTEM_USER_CONN_CACHE_KEY_PREFIX = 'SYSTEM_USER_CONN_'
@shared_task @shared_task
...@@ -75,6 +73,12 @@ def update_assets_hardware_info(assets): ...@@ -75,6 +73,12 @@ def update_assets_hardware_info(assets):
if k.startswith('___'): if k.startswith('___'):
setattr(asset, k.strip('_'), v) setattr(asset, k.strip('_'), v)
asset.save() asset.save()
for hostname, task in summary['dark'].items():
logger.warn("Update {} hardware info error: {}".format(
hostname, task[name],
))
return summary return summary
...@@ -96,8 +100,7 @@ def test_admin_user_connectability(admin_user): ...@@ -96,8 +100,7 @@ def test_admin_user_connectability(admin_user):
:return: :return:
""" """
from ops.utils import run_adhoc from ops.utils import run_adhoc
assets = admin_user.assets.all() assets = admin_user.get_related_assets()
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
tasks = [ tasks = [
{ {
...@@ -126,6 +129,7 @@ def test_admin_user_connectability_period(): ...@@ -126,6 +129,7 @@ def test_admin_user_connectability_period():
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60) cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + i, 0, 60*60*60)
@shared_task
def test_admin_user_connectability_manual(asset): def test_admin_user_connectability_manual(asset):
from ops.utils import run_adhoc from ops.utils import run_adhoc
# assets = Asset.objects.filter(type__in=['Server', 'VM']) # assets = Asset.objects.filter(type__in=['Server', 'VM'])
...@@ -140,8 +144,10 @@ def test_admin_user_connectability_manual(asset): ...@@ -140,8 +144,10 @@ def test_admin_user_connectability_manual(asset):
] ]
result = run_adhoc(hosts, tasks=tasks, pattern="all", run_as_admin=True) result = run_adhoc(hosts, tasks=tasks, pattern="all", run_as_admin=True)
if result.results_summary['dark']: if result.results_summary['dark']:
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 0, 60*60*60)
return False return False
else: else:
cache.set(ADMIN_USER_CONN_CACHE_KEY_PREFIX + asset.hostname, 1, 60*60* 60)
return True return True
...@@ -153,7 +159,7 @@ def test_system_user_connectability(system_user): ...@@ -153,7 +159,7 @@ def test_system_user_connectability(system_user):
:return: :return:
""" """
from ops.utils import run_adhoc from ops.utils import run_adhoc
assets = system_user.assets.all() assets = system_user.get_clusters_assets()
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
tasks = [ tasks = [
{ {
...@@ -171,7 +177,7 @@ def test_system_user_connectability(system_user): ...@@ -171,7 +177,7 @@ def test_system_user_connectability(system_user):
def test_system_user_connectability_period(): def test_system_user_connectability_period():
for system_user in SystemUser.objects.all(): for system_user in SystemUser.objects.all():
summary = test_system_user_connectability(system_user) summary = test_system_user_connectability(system_user)
cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name , summary, 60*60*60) cache.set(SYSTEM_USER_CONN_CACHE_KEY_PREFIX + system_user.name, summary, 60*60*60)
def get_push_system_user_tasks(system_user): def get_push_system_user_tasks(system_user):
...@@ -207,19 +213,12 @@ def get_push_system_user_tasks(system_user): ...@@ -207,19 +213,12 @@ def get_push_system_user_tasks(system_user):
) )
} }
} }
] ]
return tasks return tasks
PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER {} PERIOD...' PUSH_SYSTEM_USER_PERIOD_TASK_NAME = 'PUSH SYSTEM USER [{}] PERIOD...'
PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER {} ASSETS' PUSH_SYSTEM_USER_TASK_NAME = 'PUSH SYSTEM USER [{}] ASSETS'
def get_push_system_user_task(system_user):
from ops.utils import get_task_by_name
task = get_task_by_name(PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(system_user.name))
return task
def push_system_user(system_user, assets, name): def push_system_user(system_user, assets, name):
...@@ -246,7 +245,7 @@ def push_system_user(system_user, assets, name): ...@@ -246,7 +245,7 @@ def push_system_user(system_user, assets, name):
def push_system_user_period(): def push_system_user_period():
logger.debug("Push system user period") logger.debug("Push system user period")
for s in SystemUser.objects.filter(auto_push=True): for s in SystemUser.objects.filter(auto_push=True):
assets = s.assets.all() assets = s.get_clusters_assets()
name = PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(s.name) name = PUSH_SYSTEM_USER_PERIOD_TASK_NAME.format(s.name)
push_system_user(s, assets, name) push_system_user(s, assets, name)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
{% block help_message %} {% block help_message %}
<div class="alert alert-info help-message"> <div class="alert alert-info help-message">
管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等 管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。可以设置主机级别管理用户,也设置集群级别管理用户,这样资产可以不用再单独设置
</div> </div>
{% endblock %} {% endblock %}
......
...@@ -14,20 +14,18 @@ ...@@ -14,20 +14,18 @@
<h3>{% trans 'Basic' %}</h3> <h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %}
{% bootstrap_field form.env layout="horizontal" %} {% bootstrap_field form.env layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3> <h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.username layout="horizontal" %} {% bootstrap_field form.admin_user layout="horizontal" %}
{% bootstrap_field form.password layout="horizontal" %}
{% bootstrap_field form.private_key_file layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Cluster and group' %}</h3> <h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.cluster layout="horizontal" %}
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
...@@ -50,11 +48,11 @@ ...@@ -50,11 +48,11 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
$("#id_tags").select2({ {# $("#id_tags").select2({#}
tags: true, {# tags: true,#}
maximumSelectionLength: 8 //最多能够选择的个数 {# maximumSelectionLength: 8 //最多能够选择的个数#}
//closeOnSelect: false {# //closeOnSelect: false#}
}); {# });#}
}) })
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
<li class="active"> <li class="active">
<a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Asset detail' %} </a> <a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Asset detail' %} </a>
</li> </li>
<li>
<a href="{% url 'assets:asset-detail' pk=asset.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Auth' %} </a>
</li>
{% if user.is_superuser %} {% if user.is_superuser %}
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a>
...@@ -198,14 +195,6 @@ ...@@ -198,14 +195,6 @@
</span> </span>
</td> </td>
</tr> </tr>
<tr>
<td>{% trans 'Reset auth' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_test_admin_user" style="width: 54px;">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
...@@ -239,7 +228,7 @@ ...@@ -239,7 +228,7 @@
<tr> <tr>
<td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td> <td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
<td> <td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button> <button class="btn btn-danger pull-right btn-xs btn-leave-group" type="button"><i class="fa fa-minus"></i></button>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -274,7 +263,7 @@ function updateAssetGroups(groups) { ...@@ -274,7 +263,7 @@ function updateAssetGroups(groups) {
$('#add-asset2group tbody').append( $('#add-asset2group tbody').append(
'<tr>' + '<tr>' +
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' + '<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>' + '<td><button class="btn btn-danger btn-xs pull-right btn-leave-group" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>' '</tr>'
) )
}); });
...@@ -288,44 +277,20 @@ function updateAssetGroups(groups) { ...@@ -288,44 +277,20 @@ function updateAssetGroups(groups) {
}); });
} }
function updateAssetSystemUser(system_users) {
var the_url = "{% url 'api-assets:asset-update-system-users' pk=asset.id %}";
var body = {
system_users: Object.assign([], system_users)
};
var success = function(data) {
$('.select2-selection__rendered').empty();
$('#groups_selected').val('');
$.map(jumpserver.system_user_selected, function(name, index) {
$('#opt_' + index).remove();
$('#add-asset2systemuser tbody').append(
'<tr>' +
'<td><b class="bdg_group" data-sid="' + index + '">' + name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_system" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.groups_selected
jumpserver.system_user_selected = {};
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success
});
}
function refreshAssetHardware() { function refreshAssetHardware() {
var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}"; var the_url = "{% url 'api-assets:asset-refresh' pk=asset.id %}";
var success = function (data) { var success = function (data) {
location.reload(); location.reload();
}; };
var error = function (data) {
alert(data)
};
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
success: success, success: success,
error: error,
method: 'GET' method: 'GET'
}) });
} }
...@@ -337,13 +302,6 @@ $(document).ready(function () { ...@@ -337,13 +302,6 @@ $(document).ready(function () {
var data = evt.params.data; var data = evt.params.data;
delete jumpserver.groups_selected[data.id] delete jumpserver.groups_selected[data.id]
}); });
$('.select2.system-user').select2().on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.system_user_selected[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.system_user_selected[data.id]
})
}).on('click', '#is_active', function () { }).on('click', '#is_active', function () {
var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}'; var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}';
var checked = $(this).prop('checked'); var checked = $(this).prop('checked');
...@@ -370,11 +328,11 @@ $(document).ready(function () { ...@@ -370,11 +328,11 @@ $(document).ready(function () {
return $(this).data('gid'); return $(this).data('gid');
}).get(); }).get();
$.map(jumpserver.groups_selected, function(value, index) { $.map(jumpserver.groups_selected, function(value, index) {
groups.push(parseInt(index)); groups.push(index);
$('#opt_' + index).remove(); $('#opt_' + index).remove();
}); });
updateAssetGroups(groups) updateAssetGroups(groups)
}).on('click', '.btn_leave_group', function() { }).on('click', '.btn-leave-group', function() {
var $this = $(this); var $this = $(this);
var $tr = $this.closest('tr'); var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_group'); var $badge = $tr.find('.bdg_group');
...@@ -388,34 +346,6 @@ $(document).ready(function () { ...@@ -388,34 +346,6 @@ $(document).ready(function () {
return $(this).data('gid'); return $(this).data('gid');
}).get(); }).get();
updateAssetGroups(groups) updateAssetGroups(groups)
}).on('click', '.btn-system-user', function () {
if (Object.keys(jumpserver.system_user_selected).length === 0) {
return false;
}
var system_users = $('.bdg_group').map(function() {
return $(this).data('sid');
}).get();
$.map(jumpserver.system_user_selected, function(value, index) {
system_users.push(parseInt(index));
$('#opt_' + index).remove();
});
updateAssetSystemUser(system_users)
}).on('click', '.btn_leave_system', function () {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_group');
var sid = $badge.data('sid');
var name = $badge.html() || $badge.text();
$('#groups_selected').append(
'<option value="' + sid + '" id="opt_' + sid + '">' + name + '</option>'
);
$tr.remove();
var system_users = $('.bdg_group').map(function () {
return $(this).data('sid');
}).get();
updateAssetSystemUser(system_users)
}).on('click', '.btn-delete-asset', function () { }).on('click', '.btn-delete-asset', function () {
var $this = $(this); var $this = $(this);
var name = "{{ asset.hostname }}"; var name = "{{ asset.hostname }}";
...@@ -424,7 +354,7 @@ $(document).ready(function () { ...@@ -424,7 +354,7 @@ $(document).ready(function () {
var redirect_url = "{% url 'assets:asset-list' %}"; var redirect_url = "{% url 'assets:asset-list' %}";
objectDelete($this, name, the_url, redirect_url); objectDelete($this, name, the_url, redirect_url);
}).on('click', '#btn_refresh_asset', function () { }).on('click', '#btn_refresh_asset', function () {
alert('请等待几秒, 等待完成'); alert('关闭alert, 等待完成, 自动刷新页面');
refreshAssetHardware() refreshAssetHardware()
}).on('click', '#btn_test_admin_user', function () { }).on('click', '#btn_test_admin_user', function () {
$.ajax({ $.ajax({
...@@ -436,6 +366,5 @@ $(document).ready(function () { ...@@ -436,6 +366,5 @@ $(document).ready(function () {
}) })
}) })
</script> </script>
{% endblock %} {% endblock %}
...@@ -67,16 +67,16 @@ ...@@ -67,16 +67,16 @@
<form> <form>
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td colspan="2"> <td colspan="2">
<select data-placeholder="{% trans 'Select assets' %}" class="select2 system-user-select" style="width: 100%" multiple="" tabindex="4"> <select data-placeholder="{% trans 'Select assets' %}" class="select2 asset-select" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users %} {% for asset in assets_remain %}
<option value="{{ system_user.id }}"> {{ system_user.name }} </option> <option value="{{ asset.id }}"> {{ asset.hostname }} </option>
{% endfor %} {% endfor %}
</select> </select>
</td> </td>
</tr> </tr>
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td colspan="2"> <td colspan="2">
<button type="button" class="btn btn-primary btn-sm btn-push-system-user">{% trans 'Push' %}</button> <button type="button" class="btn btn-primary btn-sm btn-add-asset">{% trans 'Add' %}</button>
</td> </td>
</tr> </tr>
</form> </form>
...@@ -93,28 +93,30 @@ ...@@ -93,28 +93,30 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
jumpserver.assets_selected = {}; jumpserver.assets_selected = {};
function updateGroupAssets(assets) { function updateGroupAssets(assets) {
var the_url = "{}"; var the_url = "{}";
var body = { var body = {
assets: Object.assign([], assets) assets: Object.assign([], assets)
}; };
var $data_table = $("#asset_list_table").DataTable(); var $data_table = $("#asset_list_table").DataTable();
var success = function(data) { var success = function(data) {
$('.select2-selection__rendered').empty(); $('.select2-selection__rendered').empty();
$.map(jumpserver.assets_selected, function(asset_ip, index) { $.map(jumpserver.assets_selected, function(asset_ip, index) {
$('#opt_' + index).remove(); $('#opt_' + index).remove();
$data_table.ajax.reload(); $data_table.ajax.reload();
});
jumpserver.groups_selected = {};
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'PUT',
success: success
}); });
} jumpserver.groups_selected = {};
};
console.log(body);
//APIUpdateAttr({
// url: the_url,
// body: JSON.stringify(body),
// method: 'PUT',
// success: success
//});
}
function leaveGroup(obj, name, url, data) { function leaveGroup(obj, name, url, data) {
function doDelete() { function doDelete() {
...@@ -203,20 +205,21 @@ $(document).ready(function () { ...@@ -203,20 +205,21 @@ $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
$('.select2.asset-select').select2() $('.select2.asset-select').select2()
.on('select2:select', function(evt) { .on('select2:select', function(evt) {
var data = evt.params.data; var data = evt.params.data;
jumpserver.assets_selected[data.id] = data.text; jumpserver.assets_selected[data.id] = data.text;
console.log(jumpserver.assets_selected) console.log(jumpserver.assets_selected)
}) })
.on('select2:unselect', function(evt) { .on('select2:unselect', function(evt) {
var data = evt.params.data; var data = evt.params.data;
delete jumpserver.assets_selected[data.id] delete jumpserver.assets_selected[data.id]
}); });
initTable(); initTable();
}) })
.on('click', ".btn-asset-group-add-asset", function () { .on('click', ".btn-add-asset", function () {
if (Object.keys(jumpserver.assets_selected).length === 0) { if (Object.keys(jumpserver.assets_selected).length === 0) {
return false; return false;
} }
...@@ -225,16 +228,8 @@ $(document).ready(function () { ...@@ -225,16 +228,8 @@ $(document).ready(function () {
}) })
.on('click', '.btn-push-system-user', function () {
var data = $('.system-user-select').select2();
var system_id = data.val()[0];
if (!system_id) {
return false
}
pushSystemUser(system_id)
})
.on('click', '.btn_asset_delete', function () { .on('click', '.btn-leave-group', function () {
var $this = $(this); var $this = $(this);
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}"; var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
var name = $(this).closest("tr").find(":nth-child(1) > a").html(); var name = $(this).closest("tr").find(":nth-child(1) > a").html();
......
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
<th class="text-center">{% trans 'Type' %}</th> <th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'Env' %}</th> <th class="text-center">{% trans 'Env' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th> <th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Valid' %}</th> <th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th> <th class="text-center">{% trans 'Connective' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
<option value="delete">{% trans 'Delete selected' %}</option> <option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option> <option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option> <option value="deactive">{% trans 'Deactive selected' %}</option>
<option value="active">{% trans 'Active' %}</option> <option value="active">{% trans 'Active selected' %}</option>
</select> </select>
<div class="input-group-btn pull-left" style="padding-left: 5px;"> <div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary"> <button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
...@@ -114,8 +114,8 @@ $(document).ready(function(){ ...@@ -114,8 +114,8 @@ $(document).ready(function(){
], ],
ajax_url: '{% url "api-assets:asset-list" %}', ajax_url: '{% url "api-assets:asset-list" %}',
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" }, columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware"}, {data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
{data: "is_active" }, {data: "is_online"}, {data: "id" }], {data: "is_active" }, {data: "is_connective"}, {data: "id" }],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
var table = jumpserver.initDataTable(options); var table = jumpserver.initDataTable(options);
...@@ -180,24 +180,28 @@ $(document).ready(function(){ ...@@ -180,24 +180,28 @@ $(document).ready(function(){
$data_table.rows({selected: true}).every(function(){ $data_table.rows({selected: true}).every(function(){
id_list.push(this.data().id); id_list.push(this.data().id);
}); });
if (id_list.length == 0) { if (id_list.length === 0) {
return false; return false;
} }
var the_url = "{% url 'api-assets:asset-list' %}"; var the_url = "{% url 'api-assets:asset-list' %}";
function doDeactive() { function doDeactive() {
var body = $.each(id_list, function(index, asset_object) { var data = [];
asset_object['is_active'] = false; $.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": false};
data.push(obj);
}); });
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)}); APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(data)});
$data_table.ajax.reload(); $data_table.ajax.reload();
jumpserver.checked = false; jumpserver.checked = false;
} }
function doActive() { function doActive() {
var body = $.each(id_list, function(index, asset_object) { var data = [];
asset_object['is_active'] = true; $.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": true};
data.push(obj);
}); });
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)}); APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(data)});
$data_table.ajax.reload(); $data_table.ajax.reload();
jumpserver.checked = false; jumpserver.checked = false;
} }
...@@ -220,8 +224,13 @@ $(document).ready(function(){ ...@@ -220,8 +224,13 @@ $(document).ready(function(){
var msg = "{% trans 'Asset Deleting failed.' %}"; var msg = "{% trans 'Asset Deleting failed.' %}";
swal("{% trans 'Asset Delete' %}", msg, "error"); swal("{% trans 'Asset Delete' %}", msg, "error");
}; };
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list); var url_delete = the_url + '?id__in=' + JSON.stringify(id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail}); APIUpdateAttr({
url: url_delete,
method: 'DELETE',
success: success,
error: fail
});
$data_table.ajax.reload(); $data_table.ajax.reload();
jumpserver.checked = false; jumpserver.checked = false;
}); });
...@@ -248,65 +257,6 @@ $(document).ready(function(){ ...@@ -248,65 +257,6 @@ $(document).ready(function(){
break; break;
} }
}); });
{##}
{#.on('click', '#btn_asset_bulk_update', function () {#}
{# var json_data = $("#fm_asset_bulk_update").serializeObject();#}
{# var body = {};#}
{# body.enable_otp = (json_data.enable_otp === 'on')? true: false;#}
{# if (json_data.type != '') {#}
{# body.type = json_data.type;#}
{# }#}
{# if (json_data.groups != undefined) {#}
{# body.groups = json_data.groups;#}
{# }#}
{# if (typeof body.groups === 'string') {#}
{# body.groups = [parseInt(body.groups)]#}
{# } else if(typeof body.groups === 'array') {#}
{# var new_groups = body.groups.map(Number);#}
{# body.groups = new_groups;#}
{# }#}
{##}
{# if (json_data.system_users != undefined) {#}
{# body.system_users = json_data.system_users;#}
{# }#}
{# if (typeof body.system_users === 'string') {#}
{# body.system_users = [parseInt(body.system_users)]#}
{# } else if(typeof body.system_users === 'array') {#}
{# var new_users = body.system_users.map(Number);#}
{# body.system_users = new_users;#}
{# }#}
{##}
{# if (json_data.tags != undefined) {#}
{# body.tags = json_data.tags;#}
{# }#}
{# if (typeof body.tags == 'string') {#}
{# body.tags = [parseInt(body.tags)];#}
{# } else if (typeof body.tags === 'array') {#}
{# var new_tags = body.tags.map(Number);#}
{# body.tags = new_tags;#}
{# }#}
{##}
{# var $data_table = $('#asset_list_table').DataTable();#}
{# var post_list = [];#}
{# $data_table.rows({selected: true}).every(function(){#}
{# var content = Object.assign({id: this.data().id}, body);#}
{# post_list.push(content);#}
{# });#}
{# if (post_list === []) {#}
{# return false#}
{# }#}
{# var the_url = "{% url 'api-assets:asset-list' %}";#}
{# var success = function() {#}
{# var msg = "{% trans 'The selected assets has been updated successfully.' %}";#}
{# swal("{% trans 'Asset Updated' %}", msg, "success");#}
{# $('#asset_list_table').DataTable().ajax.reload();#}
{# jumpserver.checked = false;#}
{# };#}
{# console.log(JSON.stringify(post_list));#}
{# console.log(the_url);#}
{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
{# $('#asset_bulk_update_modal').modal('hide');#}
{#})#}
</script> </script>
{% endblock %} {% endblock %}
...@@ -19,13 +19,18 @@ ...@@ -19,13 +19,18 @@
<h3>{% trans 'Basic' %}</h3> <h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.type layout="horizontal" %} {% bootstrap_field form.type layout="horizontal" %}
{% bootstrap_field form.env layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Cluster and group' %}</h3> <h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.cluster layout="horizontal" %} {% bootstrap_field form.admin_user layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
......
...@@ -30,30 +30,31 @@ ...@@ -30,30 +30,31 @@
<div class="panel blank-panel"> <div class="panel blank-panel">
<div class="panel-body"> <div class="panel-body">
<div class="tab-content"> <div class="tab-content">
<div id="tab-1" class="ibox float-e-margins tab-pane active"></div> <form id="ClusterForm" method="post" class="form-horizontal">
<form id="ClusterForm" method="post" class="form-horizontal"> {% csrf_token %}
{% csrf_token %} <h3 class="widget-head-color-box">{% trans 'Basic' %}</h3>
<h3 class="widget-head-color-box">基本信息</h3> {% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.address layout="horizontal" %}
{% bootstrap_field form.admin_user layout="horizontal" %} {% bootstrap_field form.contact layout="horizontal" %}
{% bootstrap_field form.address layout="horizontal" %} {% bootstrap_field form.phone layout="horizontal" %}
{% bootstrap_field form.contact layout="horizontal" %}
{% bootstrap_field form.phone layout="horizontal" %}
<div class="hr-line-dashed"></div> <h3 class="widget-head-color-box">{% trans 'Settings' %}</h3>
<h3 class="widget-head-color-box">IP段</h3> {% bootstrap_field form.admin_user layout="horizontal" %}
{% bootstrap_field form.operator layout="horizontal" %}
{% bootstrap_field form.intranet layout="horizontal" %}
{% bootstrap_field form.extranet layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <h3 class="widget-head-color-box">{% trans 'Other' %}</h3>
<div class="col-sm-4 col-sm-offset-2"> {% bootstrap_field form.operator layout="horizontal" %}
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button> {% bootstrap_field form.intranet layout="horizontal" %}
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> {% bootstrap_field form.extranet layout="horizontal" %}
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div> </div>
</form> </div>
</form>
</div> </div>
</div> </div>
</div> </div>
......
# coding:utf-8 # coding:utf-8
from django.conf.urls import url from django.conf.urls import url
from .. import api from .. import api
from rest_framework import routers
from rest_framework_bulk.routes import BulkRouter from rest_framework_bulk.routes import BulkRouter
app_name = 'assets' app_name = 'assets'
......
...@@ -15,3 +15,5 @@ def get_assets_by_hostname_list(hostname_list): ...@@ -15,3 +15,5 @@ def get_assets_by_hostname_list(hostname_list):
def get_system_user_by_name(name): def get_system_user_by_name(name):
system_user = get_object_or_none(SystemUser, name=name) system_user = get_object_or_none(SystemUser, name=name)
return system_user return system_user
...@@ -28,6 +28,7 @@ from .. import forms ...@@ -28,6 +28,7 @@ from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
from ..tasks import update_assets_hardware_info from ..tasks import update_assets_hardware_info
from ..signals import on_asset_created
__all__ = [ __all__ = [
...@@ -73,11 +74,12 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView): ...@@ -73,11 +74,12 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
def form_valid(self, form): def form_valid(self, form):
self.asset = asset = form.save() asset = form.save()
asset.created_by = self.request.user.username or 'Admin' asset.created_by = self.request.user.username or 'Admin'
asset.date_created = timezone.now() asset.date_created = timezone.now()
asset.save() asset.save()
return super(AssetCreateView, self).form_valid(form) on_asset_created.send(sender=self.__class__, asset=asset)
return super().form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
...@@ -85,11 +87,11 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView): ...@@ -85,11 +87,11 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
'action': 'Create asset', 'action': 'Create asset',
} }
kwargs.update(context) kwargs.update(context)
return super(AssetCreateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_url(self): def get_success_url(self):
update_assets_hardware_info.delay([self.asset._to_secret_json()]) # update_assets_hardware_info.delay([self.asset._to_secret_json()])
return super(AssetCreateView, self).get_success_url() return super().get_success_url()
class AssetModalListView(AdminUserRequiredMixin, ListView): class AssetModalListView(AdminUserRequiredMixin, ListView):
......
...@@ -66,20 +66,15 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView): ...@@ -66,20 +66,15 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
context_object_name = 'asset_group' context_object_name = 'asset_group'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all()) assets_remain = Asset.objects.exclude(groups__in=[self.object])
system_users = SystemUser.objects.all()
system_users_remain = SystemUser.objects.exclude(id__in=system_users)
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Asset group detail'), 'action': _('Asset group detail'),
'assets_remain': assets_remain, 'assets_remain': assets_remain,
'assets': [asset for asset in Asset.objects.all() 'assets': self.object.assets.all(),
if asset not in assets_remain],
'system_users': system_users,
'system_users_remain': system_users_remain,
} }
kwargs.update(context) kwargs.update(context)
return super(AssetGroupDetailView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView): class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
......
...@@ -17,8 +17,7 @@ app = Celery('jumpserver') ...@@ -17,8 +17,7 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to # Using a string here means the worker will not have to
# pickle the object when using Windows. # pickle the object when using Windows.
app.config_from_object('django.conf:settings') app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: [app_config.split('.')[0] app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
for app_config in settings.INSTALLED_APPS])
app.conf.update( app.conf.update(
CELERYBEAT_SCHEDULE={ CELERYBEAT_SCHEDULE={
......
# coding: utf-8 # coding: utf-8
import inspect
from django.db import models from django.db import models
from django.http import JsonResponse from django.http import JsonResponse
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class NoDeleteQuerySet(models.query.QuerySet): class NoDeleteQuerySet(models.query.QuerySet):
def delete(self): def delete(self):
...@@ -58,3 +60,31 @@ class IDInFilterMixin(object): ...@@ -58,3 +60,31 @@ class IDInFilterMixin(object):
if isinstance(ids, list): if isinstance(ids, list):
queryset = queryset.filter(id__in=ids) queryset = queryset.filter(id__in=ids)
return queryset return queryset
class BulkSerializerMixin(object):
"""
Become rest_framework_bulk not support uuid as a primary key
so rewrite it. https://github.com/miki725/django-rest-framework-bulk/issues/66
"""
def to_internal_value(self, data):
from rest_framework_bulk import BulkListSerializer
ret = super(BulkSerializerMixin, self).to_internal_value(data)
id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
# add update_lookup_field field back to validated data
# since super by default strips out read-only fields
# hence id will no longer be present in validated_data
if all((isinstance(self.root, BulkListSerializer),
id_attr,
request_method in ('PUT', 'PATCH'))):
id_field = self.fields[id_attr]
if data.get("id"):
id_value = id_field.to_internal_value(data.get("id"))
else:
id_value = id_field.to_internal_value(data.get("pk"))
ret[id_attr] = id_value
return ret
from __future__ import absolute_import
# from celery import shared_task
from django.core.mail import send_mail from django.core.mail import send_mail
from django.conf import settings from django.conf import settings
from common import celery_app as app from common import celery_app as app
from .utils import get_logger
logger = get_logger(__file__)
@app.task @app.task
...@@ -26,4 +27,7 @@ def send_mail_async(*args, **kwargs): ...@@ -26,4 +27,7 @@ def send_mail_async(*args, **kwargs):
args.insert(2, settings.EMAIL_HOST_USER) args.insert(2, settings.EMAIL_HOST_USER)
args = tuple(args) args = tuple(args)
send_mail(*args, **kwargs) try:
send_mail(*args, **kwargs)
except Exception as e:
logger.error("Sending mail error: {}".format(e))
...@@ -331,20 +331,16 @@ BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % { ...@@ -331,20 +331,16 @@ BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % {
'host': CONFIG.REDIS_HOST or '127.0.0.1', 'host': CONFIG.REDIS_HOST or '127.0.0.1',
'port': CONFIG.REDIS_PORT or 6379, 'port': CONFIG.REDIS_PORT or 6379,
} }
CELERY_TASK_SERIALIZER = 'pickle'
CELERY_RESULT_SERIALIZER = 'pickle'
CELERY_RESULT_BACKEND = BROKER_URL CELERY_RESULT_BACKEND = BROKER_URL
CELERY_ACCEPT_CONTENT = ['json', 'pickle']
CELERY_TASK_RESULT_EXPIRES = 3600
CELERYD_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
CELERYD_TASK_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
CELERY_TIMEZONE = TIME_ZONE
# TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30 # TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30
# crontab job
# CELERYBEAT_SCHEDULE = {
# Check applications is alive every 10m
# 'check_terminal_alive': {
# 'task': 'applications.tasks.check_terminal_alive',
# 'schedule': timedelta(seconds=TERMINAL_HEATBEAT_INTERVAL),
# 'args': (),
# },
# }
# Cache use redis # Cache use redis
CACHES = { CACHES = {
......
...@@ -21,6 +21,7 @@ def is_uuid(s): ...@@ -21,6 +21,7 @@ def is_uuid(s):
return False return False
def record_adhoc(func): def record_adhoc(func):
def _deco(adhoc, **options): def _deco(adhoc, **options):
record = AdHocRunHistory(adhoc=adhoc) record = AdHocRunHistory(adhoc=adhoc)
......
...@@ -156,7 +156,7 @@ function activeNav() { ...@@ -156,7 +156,7 @@ function activeNav() {
function APIUpdateAttr(props) { function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,} // props = {url: .., body: , success: , error: , method: ,}
props = props || {}; props = props || {};
var success_message = props.success_message || 'Update Successfully!'; var success_message = props.success_message || 'Update successfully!';
var fail_message = props.fail_message || 'Error occurred while updating.'; var fail_message = props.fail_message || 'Error occurred while updating.';
$.ajax({ $.ajax({
url: props.url, url: props.url,
...@@ -169,11 +169,10 @@ function APIUpdateAttr(props) { ...@@ -169,11 +169,10 @@ function APIUpdateAttr(props) {
if (typeof props.success === 'function') { if (typeof props.success === 'function') {
return props.success(data); return props.success(data);
} }
}).fail(function(jqXHR, textStatus, errorThrown) {
}).fail(function(jqXHR, textStatue, errorThrown) {
toastr.error(fail_message); toastr.error(fail_message);
if (typeof props.error === 'function') { if (typeof props.error === 'function') {
return props.error(errorThrown); return props.error(jqXHR.responseText);
} }
}); });
// return true; // return true;
...@@ -265,7 +264,7 @@ jumpserver.initDataTable = function (options) { ...@@ -265,7 +264,7 @@ jumpserver.initDataTable = function (options) {
language: { language: {
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json" url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
}, },
order: options.order || [[ 1, 'asc' ]], order: options.order || [],
select: options.select || 'multi', select: options.select || 'multi',
buttons: [], buttons: [],
columnDefs: columnDefs, columnDefs: columnDefs,
......
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }}" style="margin: 20px auto 0px"> <div class="alert alert-{{ message.tags }} help-message" >
{{ message|safe }} {{ message|safe }}
</div> </div>
{% endfor %} {% endfor %}
......
...@@ -5,3 +5,8 @@ from django.apps import AppConfig ...@@ -5,3 +5,8 @@ from django.apps import AppConfig
class ApplicationsConfig(AppConfig): class ApplicationsConfig(AppConfig):
name = 'terminal' name = 'terminal'
def ready(self):
from .signals import on_app_ready
on_app_ready.send(self.__class__)
super().ready()
# -*- coding: utf-8 -*-
#
ASSETS_CACHE_KEY = "terminal__session__assets"
USERS_CACHE_KEY = "terminal__session__users"
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
# -*- coding: utf-8 -*-
#
import threading
import time
from django.core.cache import cache
from django.dispatch import Signal, receiver
from django.db.utils import ProgrammingError, OperationalError
from common.utils import get_logger
from .const import ASSETS_CACHE_KEY, USERS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
RUNNING = False
logger = get_logger(__file__)
on_app_ready = Signal()
@receiver(on_app_ready)
def on_app_ready_set_cache(sender, **kwargs):
from .utils import get_session_asset_list, get_session_user_list, \
get_session_system_user_list
global RUNNING
def set_cache():
while True:
try:
assets = get_session_asset_list()
users = get_session_user_list()
system_users = get_session_system_user_list()
cache.set(ASSETS_CACHE_KEY, assets)
cache.set(USERS_CACHE_KEY, users)
cache.set(SYSTEM_USER_CACHE_KEY, system_users)
except (ProgrammingError, OperationalError):
pass
finally:
time.sleep(10)
if RUNNING:
return
threads = []
thread = threading.Thread(target=set_cache)
threads.append(thread)
logger.debug("Receive app ready signal: set cache task start")
for t in threads:
t.daemon = True
t.start()
RUNNING = True
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import threading
import time import time
from celery import shared_task from celery import shared_task
from django.core.cache import cache from django.core.cache import cache
from django.db.utils import ProgrammingError from django.db.utils import ProgrammingError, OperationalError
from .models import Session from .models import Session
ASSETS_CACHE_KEY = "terminal__session__assets"
USERS_CACHE_KEY = "terminal__session__users"
SYSTEM_USER_CACHE_KEY = "terminal__session__system_users"
CACHE_REFRESH_INTERVAL = 10 CACHE_REFRESH_INTERVAL = 10
RUNNING = False RUNNING = False
...@@ -24,46 +20,7 @@ def clean_terminal_history(): ...@@ -24,46 +20,7 @@ def clean_terminal_history():
pass pass
def get_session_asset_list():
return set(list(Session.objects.values_list('asset', flat=True)))
def get_session_user_list():
return set(list(Session.objects.values_list('user', flat=True)))
def get_session_system_user_list():
return set(list(Session.objects.values_list('system_user', flat=True)))
def set_cache():
while True:
try:
assets = get_session_asset_list()
users = get_session_user_list()
system_users = get_session_system_user_list()
cache.set(ASSETS_CACHE_KEY, assets)
cache.set(USERS_CACHE_KEY, users)
cache.set(SYSTEM_USER_CACHE_KEY, system_users)
except ProgrammingError:
pass
finally:
time.sleep(10)
def main():
global RUNNING
if RUNNING:
return
threads = []
thread = threading.Thread(target=set_cache)
threads.append(thread)
for t in threads:
t.daemon = True
t.start()
RUNNING = True
# Todo: 不能migrations了
main()
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.core.cache import cache from django.core.cache import cache
from .models import Session
from .const import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
def get_session_asset_list():
return set(list(Session.objects.values_list('asset', flat=True)))
def get_session_user_list():
return set(list(Session.objects.values_list('user', flat=True)))
def get_session_system_user_list():
return set(list(Session.objects.values_list('system_user', flat=True)))
from .tasks import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY
def get_user_list_from_cache(): def get_user_list_from_cache():
......
...@@ -4,11 +4,11 @@ from rest_framework import generics ...@@ -4,11 +4,11 @@ from rest_framework import generics
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework import viewsets
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from . import serializers from .serializers import UserSerializer, UserGroupSerializer, \
UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \
UserUpdateGroupSerializer
from .tasks import write_login_log_async from .tasks import write_login_log_async
from .models import User, UserGroup from .models import User, UserGroup
from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly
...@@ -20,49 +20,23 @@ from common.utils import get_logger ...@@ -20,49 +20,23 @@ from common.utils import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
# class UserListView(generics.ListAPIView): class UserViewSet(BulkModelViewSet):
# queryset = User.objects.all()
# serializer_class = serializers.UserSerializer
# filter_fields = ('username', 'email', 'name', 'id')
class UserViewSet(viewsets.ModelViewSet):
# class UserViewSet(IDInFilterMixin, BulkModelViewSet):
"""
retrieve:
Return a user instance .
list:
Return all users except app user, ordered by most recently joined.
create:
Create a new user.
delete:
Remove an existing user.
partial_update:
Update one or more fields on an existing user.
update:
Update a user.
"""
queryset = User.objects.all() queryset = User.objects.all()
# queryset = User.objects.all().exclude(role="App").order_by("date_joined") # queryset = User.objects.all().exclude(role="App").order_by("date_joined")
serializer_class = serializers.UserSerializer serializer_class = UserSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
filter_fields = ('username', 'email', 'name', 'id') filter_fields = ('username', 'email', 'name', 'id')
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView): class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = serializers.UserUpdateGroupSerializer serializer_class = UserUpdateGroupSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
class UserResetPasswordApi(generics.UpdateAPIView): class UserResetPasswordApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = serializers.UserSerializer serializer_class = UserSerializer
def perform_update(self, serializer): def perform_update(self, serializer):
# Note: we are not updating the user object here. # Note: we are not updating the user object here.
...@@ -77,7 +51,7 @@ class UserResetPasswordApi(generics.UpdateAPIView): ...@@ -77,7 +51,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
class UserResetPKApi(generics.UpdateAPIView): class UserResetPKApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = serializers.UserSerializer serializer_class = UserSerializer
def perform_update(self, serializer): def perform_update(self, serializer):
from .utils import send_reset_ssh_key_mail from .utils import send_reset_ssh_key_mail
...@@ -89,7 +63,7 @@ class UserResetPKApi(generics.UpdateAPIView): ...@@ -89,7 +63,7 @@ class UserResetPKApi(generics.UpdateAPIView):
class UserUpdatePKApi(generics.UpdateAPIView): class UserUpdatePKApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = serializers.UserPKUpdateSerializer serializer_class = UserPKUpdateSerializer
permission_classes = (IsCurrentUserOrReadOnly,) permission_classes = (IsCurrentUserOrReadOnly,)
def perform_update(self, serializer): def perform_update(self, serializer):
...@@ -100,12 +74,12 @@ class UserUpdatePKApi(generics.UpdateAPIView): ...@@ -100,12 +74,12 @@ class UserUpdatePKApi(generics.UpdateAPIView):
class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = UserGroup.objects.all() queryset = UserGroup.objects.all()
serializer_class = serializers.UserGroupSerializer serializer_class = UserGroupSerializer
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
queryset = UserGroup.objects.all() queryset = UserGroup.objects.all()
serializer_class = serializers.UserGroupUpdateMemeberSerializer serializer_class = UserGroupUpdateMemeberSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
......
...@@ -189,7 +189,7 @@ class User(AbstractUser): ...@@ -189,7 +189,7 @@ class User(AbstractUser):
return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon' return 'https://www.gravatar.com/avatar/c6812ab450230979465d7bf288eadce2a?s=120&d=identicon'
def generate_reset_token(self): def generate_reset_token(self):
return signer.sign_t({'reset': self.id, 'email': self.email}, expires_in=3600) return signer.sign_t({'reset': str(self.id), 'email': self.email}, expires_in=3600)
def to_json(self): def to_json(self):
return OrderedDict({ return OrderedDict({
......
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin from rest_framework_bulk import BulkListSerializer
from common.utils import signer, validate_ssh_public_key from common.utils import signer, validate_ssh_public_key
from common.mixins import BulkSerializerMixin
from .models import User, UserGroup from .models import User, UserGroup
......
# -*- coding: utf-8 -*-
#
from django.dispatch import Signal, receiver
from common.utils import get_logger
logger = get_logger(__file__)
on_user_created = Signal(providing_args=['user'])
@receiver(on_user_created)
def send_user_add_mail_to_user(sender, user=None, **kwargs):
from .utils import send_user_created_mail
logger.debug("Receive asset create signal, update asset hardware info")
send_user_created_mail(user)
...@@ -23,17 +23,18 @@ urlpatterns = [ ...@@ -23,17 +23,18 @@ urlpatterns = [
# User view # User view
url(r'^user$', views.UserListView.as_view(), name='user-list'), url(r'^user$', views.UserListView.as_view(), name='user-list'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), name='user-asset-permission-create'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'), url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'), url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'),
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'), url(r'^user/(?P<pk>[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'),
url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'), url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), name='user-asset-permission-create'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9a-zA-Z\-]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
# User group view # User group view
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'), url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
......
...@@ -31,7 +31,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin): ...@@ -31,7 +31,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
return True return True
def user_add_success_next(user): def send_user_created_mail(user):
subject = _('Create account successfully') subject = _('Create account successfully')
recipient_list = [user.email] recipient_list = [user.email]
message = _(""" message = _("""
...@@ -58,6 +58,8 @@ def user_add_success_next(user): ...@@ -58,6 +58,8 @@ def user_add_success_next(user):
'email': user.email, 'email': user.email,
'login_url': reverse('users:login', external=True), 'login_url': reverse('users:login', external=True),
} }
if settings.DEBUG:
print(message)
send_mail_async.delay(subject, message, recipient_list, html_message=message) send_mail_async.delay(subject, message, recipient_list, html_message=message)
......
...@@ -29,7 +29,8 @@ from django.contrib.auth import logout as auth_logout ...@@ -29,7 +29,8 @@ from django.contrib.auth import logout as auth_logout
from .. import forms from .. import forms
from ..models import User, UserGroup from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin, user_add_success_next from ..utils import AdminUserRequiredMixin, send_user_created_mail
from ..signals import on_user_created
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from perms.models import AssetPermission from perms.models import AssetPermission
...@@ -74,7 +75,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -74,7 +75,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
user = form.save(commit=False) user = form.save(commit=False)
user.created_by = self.request.user.username or 'System' user.created_by = self.request.user.username or 'System'
user.save() user.save()
user_add_success_next(user) on_user_created.send(self.__class__, user=user)
return super(UserCreateView, self).form_valid(form) return super(UserCreateView, self).form_valid(form)
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
...@@ -281,7 +282,7 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView): ...@@ -281,7 +282,7 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
user = User.objects.create(**user_dict) user = User.objects.create(**user_dict)
user.groups.set(groups) user.groups.set(groups)
created.append(user_dict['username']) created.append(user_dict['username'])
user_add_success_next(user) on_user_created.send(self.__class__, user=user)
except Exception as e: except Exception as e:
failed.append('%s: %s' % (user_dict['username'], str(e))) failed.append('%s: %s' % (user_dict['username'], str(e)))
else: else:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment