mirror of https://github.com/jumpserver/jumpserver
[Update] 准备优化 asset user
parent
cdbdc853ea
commit
1448d23ca6
|
@ -20,6 +20,7 @@ from common.mixins import IDInCacheFilterMixin, ApiMessageMixin
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
|
from orgs.mixins import OrgBulkModelViewSet
|
||||||
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
|
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
|
||||||
from ..models import Asset, AdminUser, Node
|
from ..models import Asset, AdminUser, Node
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -36,7 +37,7 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AssetViewSet(IDInCacheFilterMixin, LabelFilter, ApiMessageMixin, BulkModelViewSet):
|
class AssetViewSet(LabelFilter, ApiMessageMixin, OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint that allows Asset to be viewed or edited.
|
API endpoint that allows Asset to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
|
@ -100,11 +101,6 @@ class AssetViewSet(IDInCacheFilterMixin, LabelFilter, ApiMessageMixin, BulkModel
|
||||||
queryset = self.filter_admin_user_id(queryset)
|
queryset = self.filter_admin_user_id(queryset)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().distinct()
|
|
||||||
queryset = self.get_serializer_class().setup_eager_loading(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class AssetListUpdateApi(IDInCacheFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
class AssetListUpdateApi(IDInCacheFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -127,15 +127,14 @@ class AssetUserAuthInfoApi(generics.RetrieveAPIView):
|
||||||
return Response(serializer.data, status=status_code)
|
return Response(serializer.data, status=status_code)
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
username = self.request.GET.get('username')
|
query_params = self.request.query_params
|
||||||
asset_id = self.request.GET.get('asset_id')
|
username = query_params.get('username')
|
||||||
prefer = self.request.GET.get("prefer")
|
asset_id = query_params.get('asset_id')
|
||||||
|
prefer = query_params.get("prefer")
|
||||||
asset = get_object_or_none(Asset, pk=asset_id)
|
asset = get_object_or_none(Asset, pk=asset_id)
|
||||||
try:
|
try:
|
||||||
manger = AssetUserManager()
|
manger = AssetUserManager()
|
||||||
if prefer:
|
instance = manger.get(username, asset, prefer=prefer)
|
||||||
manger.prefer(prefer)
|
|
||||||
instance = manger.get(username, asset)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -22,6 +22,7 @@ from rest_framework.pagination import LimitOffsetPagination
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
from common.mixins import IDInCacheFilterMixin
|
from common.mixins import IDInCacheFilterMixin
|
||||||
|
from orgs.mixins import OrgBulkModelViewSet
|
||||||
from ..models import SystemUser, Asset
|
from ..models import SystemUser, Asset
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import push_system_user_to_assets_manual, \
|
from ..tasks import push_system_user_to_assets_manual, \
|
||||||
|
@ -39,7 +40,7 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SystemUserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
class SystemUserViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
System user api set, for add,delete,update,list,retrieve resource
|
System user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -14,6 +14,11 @@ class AssetUserBackend(BaseBackend):
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter(cls, username=None, assets=None, **kwargs):
|
def filter(cls, username=None, assets=None, **kwargs):
|
||||||
queryset = cls.model.objects.all()
|
queryset = cls.model.objects.all()
|
||||||
|
prefer_id = kwargs.get('prefer_id')
|
||||||
|
if prefer_id:
|
||||||
|
queryset = queryset.filter(id=prefer_id)
|
||||||
|
instances = cls.construct_authbook_objects(queryset, assets)
|
||||||
|
return instances
|
||||||
if username:
|
if username:
|
||||||
queryset = queryset.filter(username=username)
|
queryset = queryset.filter(username=username)
|
||||||
if assets:
|
if assets:
|
||||||
|
|
|
@ -7,11 +7,13 @@ from abc import abstractmethod
|
||||||
class BaseBackend:
|
class BaseBackend:
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def filter(cls, username=None, assets=None, latest=True):
|
def filter(cls, username=None, assets=None, latest=True, prefer=None, prefer_id=None):
|
||||||
"""
|
"""
|
||||||
:param username: 用户名
|
:param username: 用户名
|
||||||
:param assets: <Asset>对象
|
:param assets: <Asset>对象
|
||||||
:param latest: 是否是最新记录
|
:param latest: 是否是最新记录
|
||||||
|
:param prefer: 优先使用
|
||||||
|
:param prefer_id: 使用id
|
||||||
:return: 元素为<AuthBook>的可迭代对象(<list> or <QuerySet>)
|
:return: 元素为<AuthBook>的可迭代对象(<list> or <QuerySet>)
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -7,7 +7,7 @@ from .base import BaseBackend
|
||||||
|
|
||||||
class AuthBookBackend(BaseBackend):
|
class AuthBookBackend(BaseBackend):
|
||||||
@classmethod
|
@classmethod
|
||||||
def filter(cls, username=None, assets=None, latest=True):
|
def filter(cls, username=None, assets=None, latest=True, **kwargs):
|
||||||
queryset = AuthBook.objects.all()
|
queryset = AuthBook.objects.all()
|
||||||
if username is not None:
|
if username is not None:
|
||||||
queryset = queryset.filter(username=username)
|
queryset = queryset.filter(username=username)
|
||||||
|
|
|
@ -30,24 +30,22 @@ class AssetUserManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
_prefer = "system_user"
|
_prefer = "system_user"
|
||||||
_using = None
|
|
||||||
|
|
||||||
def filter(self, username=None, assets=None, latest=True):
|
def filter(self, username=None, assets=None, latest=True, prefer=None, prefer_id=None):
|
||||||
if assets is not None and not assets:
|
if assets is not None and not assets:
|
||||||
return AssetUserQuerySet([])
|
return AssetUserQuerySet([])
|
||||||
|
|
||||||
if self._using:
|
if prefer:
|
||||||
backend = dict(self.backends).get(self._using)
|
self._prefer = prefer
|
||||||
if not backend:
|
|
||||||
return self.none()
|
|
||||||
instances = backend.filter(username=username, assets=assets, latest=latest)
|
|
||||||
return AssetUserQuerySet(instances)
|
|
||||||
|
|
||||||
instances_map = {}
|
instances_map = {}
|
||||||
instances = []
|
instances = []
|
||||||
for name, backend in self.backends:
|
for name, backend in self.backends:
|
||||||
|
if name != "db" and self._prefer != name:
|
||||||
|
continue
|
||||||
_instances = backend.filter(
|
_instances = backend.filter(
|
||||||
username=username, assets=assets, latest=latest
|
username=username, assets=assets, latest=latest,
|
||||||
|
prefer=self._prefer, prefer_id=prefer_id,
|
||||||
)
|
)
|
||||||
instances_map[name] = _instances
|
instances_map[name] = _instances
|
||||||
|
|
||||||
|
@ -64,12 +62,12 @@ class AssetUserManager:
|
||||||
else:
|
else:
|
||||||
ordering.extend(["admin_user", "system_user"])
|
ordering.extend(["admin_user", "system_user"])
|
||||||
# 根据prefer决定优先使用系统用户或管理用户谁的
|
# 根据prefer决定优先使用系统用户或管理用户谁的
|
||||||
ordering_instances = [instances_map.get(i) for i in ordering]
|
ordering_instances = [instances_map.get(i, []) for i in ordering]
|
||||||
instances = self._merge_instances(*ordering_instances)
|
instances = self._merge_instances(*ordering_instances)
|
||||||
return AssetUserQuerySet(instances)
|
return AssetUserQuerySet(instances)
|
||||||
|
|
||||||
def get(self, username, asset):
|
def get(self, username, asset, **kwargs):
|
||||||
instances = self.filter(username, assets=[asset])
|
instances = self.filter(username, assets=[asset], **kwargs)
|
||||||
if len(instances) == 1:
|
if len(instances) == 1:
|
||||||
return instances[0]
|
return instances[0]
|
||||||
elif len(instances) == 0:
|
elif len(instances) == 0:
|
||||||
|
@ -95,10 +93,6 @@ class AssetUserManager:
|
||||||
self._prefer = s
|
self._prefer = s
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def using(self, s):
|
|
||||||
self._using = s
|
|
||||||
return self
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def none():
|
def none():
|
||||||
return AssetUserQuerySet()
|
return AssetUserQuerySet()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import uuid
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
|
||||||
import sshpubkeys
|
import sshpubkeys
|
||||||
|
from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -34,7 +35,10 @@ class AssetUser(OrgModelMixin):
|
||||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
|
||||||
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
|
||||||
|
|
||||||
CONNECTIVITY_ASSET_CACHE_KEY = "ASSET_USER_ASSET_CONNECTIVITY_{}"
|
CONNECTIVITY_ASSET_CACHE_KEY = "ASSET_USER_{}_ASSET_CONNECTIVITY"
|
||||||
|
CONNECTIVITY_AMOUNT_CACHE_KEY = "ASSET_USER_{}_CONNECTIVITY_AMOUNT"
|
||||||
|
ASSETS_AMOUNT_CACHE_KEY = "ASSET_USER_{}_ASSETS_AMOUNT"
|
||||||
|
ASSET_USER_CACHE_TIME = 3600 * 24
|
||||||
|
|
||||||
_prefer = "system_user"
|
_prefer = "system_user"
|
||||||
|
|
||||||
|
@ -67,6 +71,11 @@ class AssetUser(OrgModelMixin):
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def part_id(self):
|
||||||
|
i = '-'.join(str(self.id).split('-')[:3])
|
||||||
|
return i
|
||||||
|
|
||||||
def get_related_assets(self):
|
def get_related_assets(self):
|
||||||
assets = self.assets.all()
|
assets = self.assets.all()
|
||||||
return assets
|
return assets
|
||||||
|
@ -97,10 +106,14 @@ class AssetUser(OrgModelMixin):
|
||||||
self.set_asset_connectivity(asset, Connectivity.reachable())
|
self.set_asset_connectivity(asset, Connectivity.reachable())
|
||||||
else:
|
else:
|
||||||
self.set_asset_connectivity(asset, Connectivity.unknown())
|
self.set_asset_connectivity(asset, Connectivity.unknown())
|
||||||
|
cache_key = self.CONNECTIVITY_AMOUNT_CACHE_KEY.format(self.part_id)
|
||||||
|
cache.delete(cache_key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connectivity(self):
|
def connectivity(self):
|
||||||
assets = self.get_related_assets()
|
assets = self.get_related_assets()\
|
||||||
|
.select_related('admin_user')\
|
||||||
|
.only('id', 'hostname', 'admin_user')
|
||||||
data = {
|
data = {
|
||||||
'unreachable': [],
|
'unreachable': [],
|
||||||
'reachable': [],
|
'reachable': [],
|
||||||
|
@ -118,11 +131,25 @@ class AssetUser(OrgModelMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def connectivity_amount(self):
|
def connectivity_amount(self):
|
||||||
return {k: len(v) for k, v in self.connectivity.items()}
|
cache_key = self.CONNECTIVITY_AMOUNT_CACHE_KEY.format(self.part_id)
|
||||||
|
amount = cache.get(cache_key)
|
||||||
|
if not amount:
|
||||||
|
connectivity = {k: len(v) for k, v in self.connectivity.items()}
|
||||||
|
cache.set(cache_key, connectivity, self.ASSET_USER_CACHE_TIME)
|
||||||
|
return amount
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_amount(self):
|
def assets_amount(self):
|
||||||
return self.get_related_assets().count()
|
cache_key = self.ASSETS_AMOUNT_CACHE_KEY.format(self.id)
|
||||||
|
cached = cache.get(cache_key)
|
||||||
|
if not cached:
|
||||||
|
cached = self.get_related_assets().count()
|
||||||
|
cache.set(cache_key, cached, self.ASSET_USER_CACHE_TIME)
|
||||||
|
return cached
|
||||||
|
|
||||||
|
def expire_assets_amount(self):
|
||||||
|
cache_key = self.ASSETS_AMOUNT_CACHE_KEY.format(self.id)
|
||||||
|
cache.delete(cache_key)
|
||||||
|
|
||||||
def get_asset_connectivity(self, asset):
|
def get_asset_connectivity(self, asset):
|
||||||
i = self.generate_id_with_asset(asset)
|
i = self.generate_id_with_asset(asset)
|
||||||
|
@ -133,12 +160,15 @@ class AssetUser(OrgModelMixin):
|
||||||
i = self.generate_id_with_asset(asset)
|
i = self.generate_id_with_asset(asset)
|
||||||
key = self.CONNECTIVITY_ASSET_CACHE_KEY.format(i)
|
key = self.CONNECTIVITY_ASSET_CACHE_KEY.format(i)
|
||||||
Connectivity.set(key, c)
|
Connectivity.set(key, c)
|
||||||
|
# 当为某个系统用户或管理用户设置的的时候,失效掉他们的连接数量
|
||||||
|
amount_key = self.CONNECTIVITY_AMOUNT_CACHE_KEY.format(self.part_id)
|
||||||
|
cache.delete(amount_key)
|
||||||
|
|
||||||
def get_asset_user(self, asset):
|
def get_asset_user(self, asset):
|
||||||
from ..backends import AssetUserManager
|
from ..backends import AssetUserManager
|
||||||
try:
|
try:
|
||||||
manager = AssetUserManager().prefer(self._prefer)
|
manager = AssetUserManager().prefer(self._prefer)
|
||||||
other = manager.get(username=self.username, asset=asset)
|
other = manager.get(username=self.username, asset=asset, prefer_id=self.id)
|
||||||
return other
|
return other
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e, exc_info=True)
|
logger.error(e, exc_info=True)
|
||||||
|
@ -150,9 +180,12 @@ class AssetUser(OrgModelMixin):
|
||||||
self._merge_auth(instance)
|
self._merge_auth(instance)
|
||||||
|
|
||||||
def _merge_auth(self, other):
|
def _merge_auth(self, other):
|
||||||
self.password = other.password
|
if other.password:
|
||||||
self.public_key = other.public_key
|
self.password = other.password
|
||||||
self.private_key = other.private_key
|
if other.public_key:
|
||||||
|
self.public_key = other.public_key
|
||||||
|
if other.private_key:
|
||||||
|
self.private_key = other.private_key
|
||||||
|
|
||||||
def clear_auth(self):
|
def clear_auth(self):
|
||||||
self.password = ''
|
self.password = ''
|
||||||
|
@ -185,7 +218,7 @@ class AssetUser(OrgModelMixin):
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_id_with_asset(self, asset):
|
def generate_id_with_asset(self, asset):
|
||||||
user_id = str(self.id).split('-')[:3]
|
user_id = [self.part_id]
|
||||||
asset_id = str(asset.id).split('-')[3:]
|
asset_id = str(asset.id).split('-')[3:]
|
||||||
ids = user_id + asset_id
|
ids = user_id + asset_id
|
||||||
return '-'.join(ids)
|
return '-'.join(ids)
|
||||||
|
|
|
@ -192,6 +192,7 @@ class AssetsAmountMixin:
|
||||||
_assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}'
|
_assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}'
|
||||||
_assets_amount = None
|
_assets_amount = None
|
||||||
key = ''
|
key = ''
|
||||||
|
cache_time = 3600 * 24 * 7
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def assets_amount(self):
|
def assets_amount(self):
|
||||||
|
@ -213,7 +214,7 @@ class AssetsAmountMixin:
|
||||||
def assets_amount(self, value):
|
def assets_amount(self, value):
|
||||||
self._assets_amount = value
|
self._assets_amount = value
|
||||||
cache_key = self._assets_amount_cache_key.format(self.key)
|
cache_key = self._assets_amount_cache_key.format(self.key)
|
||||||
cache.set(cache_key, value)
|
cache.set(cache_key, value, self.cache_time)
|
||||||
|
|
||||||
def expire_assets_amount(self):
|
def expire_assets_amount(self):
|
||||||
ancestor_keys = self.get_ancestor_keys(with_self=True)
|
ancestor_keys = self.get_ancestor_keys(with_self=True)
|
||||||
|
|
|
@ -17,7 +17,7 @@ class AdminUserSerializer(BulkOrgResourceModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
# list_serializer_class = AdaptedBulkListSerializer
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
model = AdminUser
|
model = AdminUser
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'username', 'password', 'private_key', 'public_key',
|
'id', 'name', 'username', 'password', 'private_key', 'public_key',
|
||||||
|
|
|
@ -76,8 +76,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_eager_loading(cls, queryset):
|
def setup_eager_loading(cls, queryset):
|
||||||
""" Perform necessary eager loading of data. """
|
""" Perform necessary eager loading of data. """
|
||||||
queryset = queryset.prefetch_related('labels', 'nodes')\
|
queryset = queryset.prefetch_related('labels', 'nodes', 'protocols')\
|
||||||
.select_related('admin_user')
|
.select_related('admin_user', 'domain')
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
|
@ -20,7 +20,7 @@ class SystemUserSerializer(BulkOrgResourceModelSerializer):
|
||||||
'id', 'name', 'username', 'password', 'public_key', 'private_key',
|
'id', 'name', 'username', 'password', 'public_key', 'private_key',
|
||||||
'login_mode', 'login_mode_display', 'priority', 'protocol',
|
'login_mode', 'login_mode_display', 'priority', 'protocol',
|
||||||
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
|
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
|
||||||
'assets', 'assets_amount', 'connectivity_amount'
|
'assets_amount', 'connectivity_amount'
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'password': {"write_only": True},
|
'password': {"write_only": True},
|
||||||
|
@ -32,6 +32,12 @@ class SystemUserSerializer(BulkOrgResourceModelSerializer):
|
||||||
'created_by': {'read_only': True},
|
'created_by': {'read_only': True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_eager_loading(cls, queryset):
|
||||||
|
""" Perform necessary eager loading of data. """
|
||||||
|
queryset = queryset.prefetch_related('cmd_filters', 'nodes')
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthSerializer(AuthSerializer):
|
class SystemUserAuthSerializer(AuthSerializer):
|
||||||
"""
|
"""
|
||||||
|
@ -47,8 +53,6 @@ class SystemUserAuthSerializer(AuthSerializer):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
系统用户最基本信息的数据结构
|
系统用户最基本信息的数据结构
|
||||||
|
|
|
@ -27,11 +27,6 @@ def test_asset_conn_on_created(asset):
|
||||||
test_asset_connectivity_util.delay([asset])
|
test_asset_connectivity_util.delay([asset])
|
||||||
|
|
||||||
|
|
||||||
def set_asset_root_node(asset):
|
|
||||||
logger.debug("Set asset default node: {}".format(Node.root()))
|
|
||||||
asset.nodes.add(Node.root())
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
|
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
|
||||||
@on_transaction_commit
|
@on_transaction_commit
|
||||||
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
|
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
|
||||||
|
|
|
@ -84,7 +84,7 @@ function initTable() {
|
||||||
$(td).html(innerHtml)
|
$(td).html(innerHtml)
|
||||||
}},
|
}},
|
||||||
{targets: 5, createdCell: function (td, cellData) {
|
{targets: 5, createdCell: function (td, cellData) {
|
||||||
var data = cellData['unreachable'];
|
var data = cellData.unreachable;
|
||||||
var innerHtml = "";
|
var innerHtml = "";
|
||||||
if (data !== 0) {
|
if (data !== 0) {
|
||||||
innerHtml = "<span class='text-danger'>" + data + "</span>";
|
innerHtml = "<span class='text-danger'>" + data + "</span>";
|
||||||
|
|
|
@ -80,7 +80,7 @@ function initTable() {
|
||||||
}},
|
}},
|
||||||
{targets: 6, createdCell: function (td, cellData) {
|
{targets: 6, createdCell: function (td, cellData) {
|
||||||
var innerHtml = "";
|
var innerHtml = "";
|
||||||
var data = cellData['reachable'];
|
var data = cellData.reachable;
|
||||||
if (data !== 0) {
|
if (data !== 0) {
|
||||||
innerHtml = "<span class='text-navy'>" + data + "</span>";
|
innerHtml = "<span class='text-navy'>" + data + "</span>";
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,7 +89,7 @@ function initTable() {
|
||||||
$(td).html(innerHtml)
|
$(td).html(innerHtml)
|
||||||
}},
|
}},
|
||||||
{targets: 7, createdCell: function (td, cellData) {
|
{targets: 7, createdCell: function (td, cellData) {
|
||||||
var data = cellData['unreachable'];
|
var data = cellData.unreachable;
|
||||||
var innerHtml = "";
|
var innerHtml = "";
|
||||||
if (data !== 0) {
|
if (data !== 0) {
|
||||||
innerHtml = "<span class='text-danger'>" + data + "</span>";
|
innerHtml = "<span class='text-danger'>" + data + "</span>";
|
||||||
|
@ -103,7 +103,7 @@ function initTable() {
|
||||||
var innerHtml = "";
|
var innerHtml = "";
|
||||||
var total = rowData.assets_amount;
|
var total = rowData.assets_amount;
|
||||||
var reachable = cellData.reachable;
|
var reachable = cellData.reachable;
|
||||||
if (total !== 0) {
|
if (total && total !== 0) {
|
||||||
val = reachable/total * 100;
|
val = reachable/total * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,8 @@ function initTable() {
|
||||||
var update_btn = '<a href="{% url "assets:system-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var update_btn = '<a href="{% url "assets:system-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
$(td).html(update_btn + del_btn)
|
$(td).html(update_btn + del_btn)
|
||||||
}}],
|
}},
|
||||||
|
],
|
||||||
ajax_url: '{% url "api-assets:system-user-list" %}',
|
ajax_url: '{% url "api-assets:system-user-list" %}',
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "login_mode_display"}, {data: "assets_amount" },
|
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "login_mode_display"}, {data: "assets_amount" },
|
||||||
|
|
|
@ -220,6 +220,11 @@ class AssetDetailView(PermissionsMixin, DetailView):
|
||||||
template_name = 'assets/asset_detail.html'
|
template_name = 'assets/asset_detail.html'
|
||||||
permission_classes = [IsValidUser]
|
permission_classes = [IsValidUser]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().prefetch_related(
|
||||||
|
"nodes", "labels", "protocols"
|
||||||
|
).select_related('admin_user', 'domain')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
nodes_remain = Node.objects.exclude(assets=self.object)
|
nodes_remain = Node.objects.exclude(assets=self.object)
|
||||||
context = {
|
context = {
|
||||||
|
|
|
@ -97,7 +97,10 @@ class SystemUserAssetView(PermissionsMixin, DetailView):
|
||||||
permission_classes = [IsOrgAdmin]
|
permission_classes = [IsOrgAdmin]
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
nodes_remain = sorted(Node.objects.exclude(systemuser=self.object), reverse=True)
|
from ..utils import NodeUtil
|
||||||
|
nodes_remain = Node.objects.exclude(systemuser=self.object)
|
||||||
|
util = NodeUtil()
|
||||||
|
nodes_remain = util.get_nodes_by_queryset(nodes_remain)
|
||||||
context = {
|
context = {
|
||||||
'app': _('assets'),
|
'app': _('assets'),
|
||||||
'action': _('System user asset'),
|
'action': _('System user asset'),
|
||||||
|
|
|
@ -8,11 +8,12 @@ from django.core.signals import request_finished
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
|
|
||||||
from .utils import get_logger
|
from common.utils import get_logger
|
||||||
from .local import thread_local
|
from .local import thread_local
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
|
||||||
pattern = re.compile(r'FROM `(\w+)`')
|
pattern = re.compile(r'FROM `(\w+)`')
|
||||||
|
# logger = logging.getLogger('jmsdb')
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Counter:
|
class Counter:
|
||||||
|
|
|
@ -27,7 +27,11 @@ class OrgModelViewSet(IDInCacheFilterMixin, ModelViewSet):
|
||||||
|
|
||||||
class OrgBulkModelViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
class OrgBulkModelViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().all()
|
queryset = super().get_queryset().all()
|
||||||
|
if hasattr(self, 'serializer_class') and \
|
||||||
|
hasattr(self.serializer_class, 'setup_eager_loading'):
|
||||||
|
queryset = self.serializer_class.setup_eager_loading(queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class OrgMembershipModelViewSetMixin:
|
class OrgMembershipModelViewSetMixin:
|
||||||
|
|
|
@ -31,7 +31,8 @@ class SessionViewSet(OrgBulkModelViewSet):
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
|
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
|
||||||
filter_fields = [
|
filter_fields = [
|
||||||
"user", "asset", "system_user", "terminal", "is_finished",
|
"user", "asset", "system_user", "remote_addr",
|
||||||
|
"protocol", "terminal", "is_finished",
|
||||||
]
|
]
|
||||||
date_range_filter_fields = [
|
date_range_filter_fields = [
|
||||||
('date_start', ('date_from', 'date_to'))
|
('date_start', ('date_from', 'date_to'))
|
||||||
|
|
|
@ -191,26 +191,22 @@ class Session(OrgModelMixin):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _date_start_first_has_replay_rdp_session(self):
|
def _date_start_first_has_replay_rdp_session(self):
|
||||||
if self._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:
|
if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:
|
||||||
instance = self.__class__.objects.filter(
|
instance = self.__class__.objects.filter(
|
||||||
protocol='rdp', has_replay=True).order_by('date_start').first()
|
protocol='rdp', has_replay=True
|
||||||
|
).order_by('date_start').first()
|
||||||
if not instance:
|
if not instance:
|
||||||
return None
|
date_start = timezone.now() - timezone.timedelta(days=365)
|
||||||
self._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = instance.date_start
|
else:
|
||||||
|
date_start = instance.date_start
|
||||||
return self._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION
|
self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION = date_start
|
||||||
|
return self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION
|
||||||
|
|
||||||
def can_replay(self):
|
def can_replay(self):
|
||||||
if self.has_replay:
|
if self.has_replay:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 判断对RDP Session添加上报has_replay状态机制之前的录像回放
|
|
||||||
if self._date_start_first_has_replay_rdp_session is None:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if self.date_start < self._date_start_first_has_replay_rdp_session:
|
if self.date_start < self._date_start_first_has_replay_rdp_session:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def save_to_storage(self, f):
|
def save_to_storage(self, f):
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
{% block content_bottom_left %}{% endblock %}
|
{% block content_bottom_left %}{% endblock %}
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}" charset="UTF-8"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}" charset="UTF-8"></script>
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js' %}" charset="UTF-8"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js' %}" ></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var table;
|
var table;
|
||||||
|
@ -136,8 +136,8 @@ $(document).ready(function () {
|
||||||
$('.dropdown-menu.search-help').hide();
|
$('.dropdown-menu.search-help').hide();
|
||||||
keyword.focus()
|
keyword.focus()
|
||||||
})
|
})
|
||||||
.on("click", "document", function () {
|
.on('click', 'body', function (e) {
|
||||||
$('.dropdown-menu.search-help').hide();
|
$('.dropdown-menu.search-help').hide()
|
||||||
})
|
})
|
||||||
.on('click', '.toggle', function (e) {
|
.on('click', '.toggle', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -162,12 +162,7 @@ $(document).ready(function () {
|
||||||
detailRows.push(tr.attr('id'));
|
detailRows.push(tr.attr('id'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).on('click', 'body', function (e) {
|
})
|
||||||
$('.dropdown-menu.search-help').hide()
|
|
||||||
}).on('change', '#date_start', function () {
|
|
||||||
console.log("date change")
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function format(d) {
|
function format(d) {
|
||||||
|
|
|
@ -71,13 +71,15 @@
|
||||||
<li><a class="search-item" data-value="user">{% trans 'User' %}</a></li>
|
<li><a class="search-item" data-value="user">{% trans 'User' %}</a></li>
|
||||||
<li><a class="search-item" data-value="asset">{% trans 'Asset' %}</a></li>
|
<li><a class="search-item" data-value="asset">{% trans 'Asset' %}</a></li>
|
||||||
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
|
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
|
||||||
<li><a class="search-item" data-value="input">{% trans 'Command' %}</a></li>
|
<li><a class="search-item" data-value="remote_addr">{% trans 'Remote addr' %}</a></li>
|
||||||
|
<li><a class="search-item" data-value="protocol">{% trans 'Protocol' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.zh-CN.min.js' %}" ></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
function terminateSession(data) {
|
function terminateSession(data) {
|
||||||
|
@ -182,19 +184,42 @@ function finishedSession(data) {
|
||||||
}
|
}
|
||||||
var table;
|
var table;
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
table = initTable("#session_table");
|
table = initTable("#session_table").on('init', function () {
|
||||||
$('.select2').select2({
|
var dateFromRef = $("#date_from");
|
||||||
dropdownAutoWidth: true,
|
var dateToRef = $("#date_to");
|
||||||
width: "auto"
|
var options = {
|
||||||
});
|
format: "yyyy-mm-dd",
|
||||||
$('.input-daterange.input-group').datepicker({
|
todayBtn: "linked",
|
||||||
format: "yyyy-mm-dd",
|
keyboardNavigation: false,
|
||||||
todayBtn: "linked",
|
forceParse: false,
|
||||||
keyboardNavigation: false,
|
calendarWeeks: true,
|
||||||
forceParse: false,
|
autoclose: true,
|
||||||
calendarWeeks: true,
|
language: navigator.language || "en",
|
||||||
autoclose: true
|
};
|
||||||
|
dateFromRef.datepicker(options).on("changeDate", function () {
|
||||||
|
if (!$(this).val()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var value = $(this).val() + ' 0:0:0';
|
||||||
|
var date = new Date(value);
|
||||||
|
var url = table.ajax.url();
|
||||||
|
url = setUrlParam(url, "date_from", date.toISOString());
|
||||||
|
table.ajax.url(url);
|
||||||
|
table.ajax.reload();
|
||||||
|
});
|
||||||
|
dateToRef.datepicker(options).on("changeDate", function () {
|
||||||
|
if (!$(this).val()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var value = $(this).val() + ' 23:59:59';
|
||||||
|
var date = new Date(value);
|
||||||
|
var url = table.ajax.url();
|
||||||
|
url = setUrlParam(url, "date_to", date.toISOString());
|
||||||
|
table.ajax.url(url);
|
||||||
|
table.ajax.reload();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}).on('click', '.btn-term', function () {
|
}).on('click', '.btn-term', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var session_id = $this.attr('value');
|
var session_id = $this.attr('value');
|
||||||
|
@ -207,6 +232,34 @@ $(document).ready(function() {
|
||||||
var replayUrl = "/luna/replay/" + sessionID;
|
var replayUrl = "/luna/replay/" + sessionID;
|
||||||
window.open(replayUrl, "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no");
|
window.open(replayUrl, "height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no");
|
||||||
})
|
})
|
||||||
|
.on("click", '#session_table_filter input', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
var offset1 = $('#session_table_filter input').offset();
|
||||||
|
var x = offset1.left;
|
||||||
|
var y = offset1.top;
|
||||||
|
var offset = $(".search-help").parent().offset();
|
||||||
|
x -= offset.left;
|
||||||
|
y -= offset.top;
|
||||||
|
x += 18;
|
||||||
|
y += 80;
|
||||||
|
$('.search-help').css({"top":y+"px", "left":x+"px", "position": "absolute"});
|
||||||
|
$('.dropdown-menu.search-help').show();
|
||||||
|
})
|
||||||
|
.on('click', '.search-item', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
var keyword = $("#session_table_filter input");
|
||||||
|
var value = $(this).data('value');
|
||||||
|
var old_value = keyword.val();
|
||||||
|
var new_value = old_value + ' ' + value + ':';
|
||||||
|
keyword.val(new_value.trim());
|
||||||
|
$('.dropdown-menu.search-help').hide();
|
||||||
|
keyword.focus()
|
||||||
|
})
|
||||||
|
.on('click', 'body', function (e) {
|
||||||
|
$('.dropdown-menu.search-help').hide()
|
||||||
|
})
|
||||||
.on('click', '#btn_bulk_update', function () {
|
.on('click', '#btn_bulk_update', function () {
|
||||||
var action = $('#slct_bulk_update').val();
|
var action = $('#slct_bulk_update').val();
|
||||||
var idList = table.selected;
|
var idList = table.selected;
|
||||||
|
|
Loading…
Reference in New Issue