Unverified Commit 1fe18e80 authored by 老广's avatar 老广 Committed by GitHub

Bugfix (#3153)

* [Update] 修改一部分bug

* [Update] 修改一些
parent de1a08a5
......@@ -63,15 +63,15 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet):
show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true')
# 当前节点是顶层节点, 并且仅显示直接资产
if node.is_root() and show_current_asset:
if node.is_org_root() and show_current_asset:
queryset = queryset.filter(
Q(nodes=node_id) | Q(nodes__isnull=True)
).distinct()
# 当前节点是顶层节点,显示所有资产
elif node.is_root() and not show_current_asset:
elif node.is_org_root() and not show_current_asset:
return queryset
# 当前节点不是鼎城节点,只显示直接资产
elif not node.is_root() and show_current_asset:
elif not node.is_org_root() and show_current_asset:
queryset = queryset.filter(nodes=node)
else:
children = node.get_all_children(with_self=True)
......
......@@ -50,13 +50,13 @@ class NodeViewSet(OrgModelViewSet):
# 仅支持根节点指直接创建,子节点下的节点需要通过children接口创建
def perform_create(self, serializer):
child_key = Node.root().get_next_child_key()
child_key = Node.org_root().get_next_child_key()
serializer.validated_data["key"] = child_key
serializer.save()
def perform_update(self, serializer):
node = self.get_object()
if node.is_root() and node.value != serializer.validated_data['value']:
if node.is_org_root() and node.value != serializer.validated_data['value']:
msg = _("You can't update the root node name")
raise ValidationError({"error": msg})
return super().perform_update(serializer)
......@@ -97,6 +97,7 @@ class NodeChildrenApi(generics.ListCreateAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.NodeSerializer
instance = None
is_initial = False
def initial(self, request, *args, **kwargs):
self.instance = self.get_object()
......@@ -117,7 +118,8 @@ class NodeChildrenApi(generics.ListCreateAPIView):
pk = self.kwargs.get('pk') or self.request.query_params.get('id')
key = self.request.query_params.get("key")
if not pk and not key:
node = Node.root()
node = Node.org_root()
self.is_initial = True
return node
if pk:
node = get_object_or_404(Node, pk=pk)
......@@ -130,7 +132,7 @@ class NodeChildrenApi(generics.ListCreateAPIView):
if not self.instance:
return Node.objects.none()
if self.instance.is_root():
if self.is_initial:
with_self = True
else:
with_self = False
......@@ -234,7 +236,7 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
def perform_update(self, serializer):
assets = serializer.validated_data.get('assets')
instance = self.get_object()
if instance != Node.root():
if instance != Node.org_root():
instance.assets.remove(*tuple(assets))
else:
assets = [asset for asset in assets if asset.nodes.count() > 1]
......@@ -287,5 +289,4 @@ class RefreshAssetsAmount(APIView):
model = Node
def get(self, request, *args, **kwargs):
self.model.expire_nodes_assets_amount()
return Response("Ok")
......@@ -6,8 +6,7 @@ import uuid
import logging
import random
from functools import reduce
from collections import OrderedDict, defaultdict
from django.core.cache import cache
from collections import OrderedDict
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -32,7 +31,7 @@ def default_cluster():
def default_node():
try:
from .node import Node
root = Node.root()
root = Node.org_root()
return root
except:
return None
......@@ -103,33 +102,11 @@ class NodesRelationMixin:
id = ""
_all_nodes_keys = None
@classmethod
def get_all_nodes_keys(cls):
"""
:return: {asset.id: [node.key, ]}
"""
from .node import Node
cache_key = cls.ALL_ASSET_NODES_CACHE_KEY
cached = cache.get(cache_key)
if cached:
return cached
assets = Asset.objects.all().only('id').prefetch_related(
models.Prefetch('nodes', queryset=Node.objects.all().only('key'))
)
assets_nodes_keys = {}
for asset in assets:
assets_nodes_keys[asset.id] = [n.key for n in asset.nodes.all()]
cache.set(cache_key, assets_nodes_keys, cls.CACHE_TIME)
return assets_nodes_keys
@classmethod
def expire_all_nodes_keys_cache(cls):
cache_key = cls.ALL_ASSET_NODES_CACHE_KEY
cache.delete(cache_key)
def get_nodes(self):
from .node import Node
nodes = self.nodes.all() or [Node.root()]
nodes = self.nodes.all()
if not nodes:
nodes = Node.objects.filter(id=Node.org_root().id)
return nodes
def get_all_nodes(self, flat=False):
......
......@@ -24,40 +24,34 @@ class NodeQuerySet(models.QuerySet):
class TreeMixin:
time_tree_updated = None
time_tree_updated_cache_key = 'NODE_TREE_CREATED_AT'
tree_cache_time = 3600
tree_created_time = None
tree_updated_time_cache_key = 'NODE_TREE_CREATED_AT'
tree_update_time_cache_time = 3600
_tree_service = None
@classmethod
def tree(cls):
# Todo: 有待优化, 因为每次刷新都会导致其他节点的tree失效
# Todo: ungroup node
# Todo: 有待优化, 因为每次刷新都会导致其他节点的tree失效 完成
# TOdo: 游离的资产,在树上显示的数量不对
# Todo: api key页面有bug
# Todo: ungroup node
# Todo: api key页面有bug 完成
from ..utils import TreeService
cache_updated_time = cls.get_cache_time()
if not cls.time_tree_updated or \
cache_updated_time != cls.time_tree_updated:
t = TreeService.new()
cls.update_cache_tree(t)
return t
tree_updated_time = cache.get(cls.tree_updated_time_cache_key, 0)
if not cls.tree_created_time or \
tree_updated_time > cls.tree_created_time:
print("New tree")
tree = TreeService.new()
cls.tree_created_time = time.time()
cls._tree_service = tree
return tree
return cls._tree_service
@classmethod
def get_cache_time(cls):
return cache.get(cls.time_tree_updated_cache_key)
@classmethod
def update_cache_tree(cls, t):
cls._tree_service = t
now = time.time()
cls.time_tree_updated = now
cache.set(cls.time_tree_updated_cache_key, now, cls.tree_cache_time)
@classmethod
def expire_cache_tree(cls):
cache.delete(cls.time_tree_updated_cache_key)
key = cls.tree_updated_time_cache_key
ttl = cls.tree_update_time_cache_time
value = time.time()
cache.set(key, value, ttl)
@classmethod
def refresh_tree(cls):
......@@ -74,6 +68,20 @@ class FamilyMixin:
__all_children = None
is_node = True
@staticmethod
def clean_children_keys(nodes_keys):
nodes_keys = sorted(list(nodes_keys), key=lambda x: (len(x), x))
nodes_keys_clean = []
for key in nodes_keys[::-1]:
found = False
for k in nodes_keys:
if key.startswith(k + ':'):
found = True
break
if not found:
nodes_keys_clean.append(key)
return nodes_keys_clean
@property
def children(self):
return self.get_children(with_self=False)
......@@ -108,7 +116,7 @@ class FamilyMixin:
@property
def parent(self):
if self.is_root():
if self.is_org_root():
return self
parent_key = self.parent_key
return Node.objects.get(key=parent_key)
......@@ -122,10 +130,10 @@ class FamilyMixin:
old_key = self.key
with transaction.atomic():
self.key = parent.get_next_child_key()
self.save()
for child in children:
child.key = child.key.replace(old_key, self.key, 1)
child.save()
self.save()
def get_siblings(self, with_self=False):
key = ':'.join(self.key.split(':')[:-1])
......@@ -182,21 +190,17 @@ class FullValueMixin:
@property
def full_value(self):
if self.is_root():
if self.is_org_root():
return self.value
if self._full_value is not None:
return self._full_value
print("Get full value")
value = self._tree.get_node_full_tag(self.key)
return value
class NodeAssetsMixin:
_assets_amount_cache_key = '_NODE_ASSETS_AMOUNT_{}'
_assets_cache_key = '_NODE_ASSETS_{}'
_assets_amount = None
key = ''
cache_time = 3600 * 24 * 7
id = None
@property
......@@ -210,24 +214,19 @@ class NodeAssetsMixin:
amount = self._tree.assets_amount(self.key)
return amount
# TOdo: 是否依赖tree
def get_all_assets(self):
from .asset import Asset
if self.is_root():
if self.is_org_root():
return Asset.objects.filter(org_id=self.org_id)
assets_ids = self._tree.all_assets(self.key)
return Asset.objects.filter(id__in=assets_ids)
def assets_ids(self):
assets_ids = self._tree.assets(self.key)
return assets_ids
pattern = '^{0}$|^{0}:'.format(self.key)
return Asset.objects.filter(nodes__key__regex=pattern).distinct()
def get_assets(self):
from .asset import Asset
if self.is_default_node():
assets = Asset.objects.filter(Q(nodes__id=self.id) | Q(nodes__isnull=True))
if self.is_org_root():
assets = Asset.objects.filter(Q(nodes=self) | Q(nodes__isnull=True))
else:
assets = Asset.objects.filter(id=self.assets_ids())
assets = Asset.objects.filter(nodes=self)
return assets.distinct()
def get_valid_assets(self):
......@@ -236,6 +235,16 @@ class NodeAssetsMixin:
def get_all_valid_assets(self):
return self.get_all_assets().valid()
@classmethod
def get_nodes_all_assets(cls, nodes_keys):
from .asset import Asset
nodes_keys = cls.clean_children_keys(nodes_keys)
pattern = set()
for key in nodes_keys:
pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
return Asset.objects.filter(nodes__key__regex=pattern)
class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
......@@ -261,22 +270,13 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
return self.id == other.id
def __gt__(self, other):
# if self.is_root() and not other.is_root():
# return False
# elif not self.is_root() and other.is_root():
# return True
self_key = [int(k) for k in self.key.split(':')]
other_key = [int(k) for k in other.key.split(':')]
self_parent_key = self_key[:-1]
other_parent_key = other_key[:-1]
if self_parent_key and other_parent_key and \
self_parent_key == other_parent_key:
if self_parent_key and self_parent_key == other_parent_key:
return self.value > other.value
# if len(self_parent_key) < len(other_parent_key):
# return True
# elif len(self_parent_key) > len(other_parent_key):
# return False
return self_key > other_key
def __lt__(self, other):
......@@ -310,7 +310,9 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
def create_child(self, value, _id=None):
with transaction.atomic():
child_key = self.get_next_child_key()
child = self.__class__.objects.create(id=_id, key=child_key, value=value)
child = self.__class__.objects.create(
id=_id, key=child_key, value=value
)
return child
@classmethod
......@@ -318,37 +320,39 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
cls.refresh_tree()
def is_default_node(self):
return self.is_root() and self.key == '1'
return self.key == '1'
def is_root(self):
def is_org_root(self):
if self.key.isdigit():
return True
else:
return False
@classmethod
def create_root_node(cls):
def create_org_root_node(cls):
# 如果使用current_org 在set_current_org时会死循环
_current_org = get_current_org()
ori_org = get_current_org()
with transaction.atomic():
if not _current_org.is_real():
if not ori_org.is_real():
return cls.default_node()
set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or ['1']
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
if not org_nodes_roots_keys:
org_nodes_roots_keys = ['1']
key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(_current_org)
root = cls.objects.create(key=key, value=_current_org.name)
set_current_org(ori_org)
root = cls.objects.create(key=key, value=ori_org.name)
return root
@classmethod
def root(cls):
def org_root(cls):
root = cls.objects.filter(key__regex=r'^[0-9]+$')
if root:
return root[0]
else:
return cls.create_root_node()
return cls.create_org_root_node()
@classmethod
def default_node(cls):
......@@ -365,7 +369,7 @@ class Node(OrgModelMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixi
'title': name,
'pId': self.parent_key,
'isParent': True,
'open': self.is_root(),
'open': self.is_org_root(),
'meta': {
'node': {
"id": self.id,
......
......@@ -148,9 +148,11 @@ class SystemUser(AssetUser):
return True, None
def get_all_assets(self):
from .node import Node
args = [Q(systemuser=self)]
pattern = set()
nodes_keys = self.nodes.all().values_list('key', flat=True)
nodes_keys = Node.clean_children_keys(nodes_keys)
for key in nodes_keys:
pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
......
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.db.models.signals import post_save, m2m_changed, pre_delete
from django.db.models.signals import (
post_save, m2m_changed, pre_delete, pre_save, pre_init, post_init
)
from django.dispatch import receiver
from common.utils import get_logger
......@@ -30,6 +32,9 @@ def test_asset_conn_on_created(asset):
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
@on_transaction_commit
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
"""
当资产创建时,更新硬件信息,更新可连接性
"""
if created:
logger.info("Asset `{}` create signal received".format(instance))
......@@ -37,78 +42,81 @@ def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance)
# 过期节点资产数量
Node.refresh_nodes()
@receiver(pre_delete, sender=Asset, dispatch_uid="my_unique_identifier")
def on_asset_delete(sender, instance=None, **kwargs):
# 过期节点资产数量
"""
当资产删除时,刷新节点,节点中存在节点和资产的关系
"""
Node.refresh_nodes()
@receiver(post_save, sender=SystemUser, dispatch_uid="my_unique_identifier")
def on_system_user_update(sender, instance=None, created=True, **kwargs):
"""
当系统用户更新时,可能更新了秘钥,用户名等,这时要自动推送系统用户到资产上,
其实应该当 用户名,密码,秘钥 sudo等更新时再推送,这里偷个懒,
这里直接取了 instance.assets 因为nodes和系统用户发生变化时,会自动将nodes下的资产
关联到上面
"""
if instance and not created:
logger.info("System user `{}` update signal received".format(instance))
assets = instance.assets.all()
assets = instance.assets.all().valid()
push_system_user_to_assets.delay(instance, assets)
# @receiver(m2m_changed, sender=SystemUser.nodes.through)
# def on_system_user_nodes_change(sender, instance=None, **kwargs):
# if instance and kwargs["action"] == "post_add":
# logger.info("System user `{}` nodes update signal received".format(instance))
# assets = set()
# nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
# for node in nodes:
# assets.update(set(node.get_all_assets()))
# instance.assets.add(*tuple(assets))
#
@receiver(m2m_changed, sender=SystemUser.assets.through)
@receiver(m2m_changed, sender=SystemUser.assets.through, dispatch_uid="my_unique_identifier")
def on_system_user_assets_change(sender, instance=None, **kwargs):
"""
当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中
"""
if instance and kwargs["action"] == "post_add":
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
push_system_user_to_assets.delay(instance, assets)
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_node_changed(sender, instance=None, **kwargs):
logger.debug("Asset nodes change signal received")
Asset.expire_all_nodes_keys_cache()
@receiver(m2m_changed, sender=SystemUser.nodes.through, dispatch_uid="my_unique_identifier")
def on_system_user_nodes_change(sender, instance=None, **kwargs):
"""
当系统用户和节点关系发生变化时,应该将节点关联到新的系统用户上
"""
if instance and kwargs["action"] == "post_add":
logger.info("System user `{}` nodes update signal received".format(instance))
nodes_keys = kwargs['model'].objects.filter(
pk__in=kwargs['pk_set']
).values_list('key', flat=True)
assets = Node.get_nodes_all_assets(nodes_keys)
instance.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=Asset.nodes.through, dispatch_uid="my_unique_identifier")
def on_asset_nodes_changed(sender, instance=None, **kwargs):
"""
当资产的节点发生变化时,或者 当节点的资产关系发生变化时,
节点下新增的资产,添加到节点关联的系统用户中
并刷新节点
"""
if isinstance(instance, Asset):
# nodes = []
# if kwargs['action'] == 'pre_remove':
# nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
logger.debug("Asset nodes change signal received: {}".format(instance))
# 节点资产发生变化时,将资产关联到节点关联的系统用户
if kwargs['action'] == 'post_add':
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
system_users_assets = defaultdict(set)
system_users = SystemUser.objects.filter(nodes__in=nodes)
for system_user in system_users:
system_users_assets[system_user].update({instance})
system_users_assets[system_user].add(instance)
for system_user, assets in system_users_assets.items():
system_user.assets.add(*tuple(assets))
Node.refresh_nodes()
@receiver(m2m_changed, sender=Asset.nodes.through)
def on_node_assets_changed(sender, instance=None, **kwargs):
if isinstance(instance, Node):
logger.debug("Node assets change signal {} received".format(instance))
# 当节点和资产关系发生改变时,过期资产数量缓存
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
if kwargs['action'] == 'post_add':
# 重新关联系统用户和资产的关系
system_users = SystemUser.objects.filter(nodes=instance)
for system_user in system_users:
system_user.assets.add(*tuple(assets))
logger.debug("Node assets change signal received: {}".format(instance))
Node.refresh_nodes()
@receiver(post_save, sender=Node)
def on_node_update_or_created(sender, instance=None, created=False, **kwargs):
if instance and not created:
Node.refresh_nodes()
# 刷新节点
Node.refresh_nodes()
@receiver(post_save, sender=AuthBook)
......
......@@ -32,7 +32,7 @@ class AssetListView(PermissionsMixin, TemplateView):
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
Node.root()
Node.org_root()
context = {
'app': _('Assets'),
'action': _('Asset list'),
......@@ -85,7 +85,7 @@ class AssetCreateView(PermissionsMixin, FormMixin, TemplateView):
if node_id:
node = get_object_or_none(Node, id=node_id)
else:
node = Node.root()
node = Node.org_root()
form["nodes"].initial = node
return form
......
......@@ -40,7 +40,7 @@
<script>
var table = null;
function initTable() {
function initAccessKeyTable() {
var options = {
ele: $('#access_key_list_table'),
columnDefs: [
......@@ -93,7 +93,7 @@ function initTable() {
$(document).ready(function () {
}).on("show.bs.modal", "#access_key_modal", function () {
if (!table) {
initTable()
initAccessKeyTable()
}
}).on("click", "#create-btn", function () {
var url = "{% url "api-auth:access-key-list" %}";
......
......@@ -16,7 +16,7 @@ def on_org_create_or_update(sender, instance=None, created=False, **kwargs):
if instance:
old_org = get_current_org()
set_current_org(instance)
node_root = Node.root()
node_root = Node.org_root()
if node_root.value != instance.name:
node_root.value = instance.name
node_root.save()
......
......@@ -97,6 +97,7 @@ class AssetPermission(BasePermission):
args = [Q(granted_by_permissions=self)]
pattern = set()
nodes_keys = self.nodes.all().values_list('key', flat=True)
nodes_keys = Node.clean_children_keys(nodes_keys)
for key in nodes_keys:
pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
......
......@@ -2,24 +2,15 @@
#
from django.db.models.signals import m2m_changed, post_save, post_delete
from django.dispatch import receiver
from django.db import transaction
from common.utils import get_logger
from common.decorator import on_transaction_commit
from .models import AssetPermission
logger = get_logger(__file__)
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
@receiver(post_save, sender=AssetPermission, dispatch_uid="my_unique_identifier")
@on_transaction_commit
def on_permission_created(sender, instance=None, created=False, **kwargs):
......
# coding: utf-8
import re
from collections import defaultdict
from functools import reduce
......@@ -250,6 +249,7 @@ class AssetPermissionUtilV2:
return system_users_actions
def get_permissions_nodes_and_assets(self):
from assets.models import Node
permissions = self.permissions.values_list('assets', 'nodes__key').distinct()
nodes_keys = set()
assets_ids = set()
......@@ -258,7 +258,7 @@ class AssetPermissionUtilV2:
assets_ids.add(asset_id)
if node_key:
nodes_keys.add(node_key)
nodes_keys = self.clean_nodes_keys(nodes_keys)
nodes_keys = Node.clean_children_keys(nodes_keys)
return nodes_keys, assets_ids
@staticmethod
......@@ -297,20 +297,6 @@ class AssetPermissionUtilV2:
queryset = Asset.objects.filter(id__in=assets_ids)
return queryset.valid().distinct()
@staticmethod
def clean_nodes_keys(nodes_keys):
nodes_keys = sorted(list(nodes_keys), key=lambda x: (len(x), x))
nodes_keys_clean = []
for key in nodes_keys[::-1]:
found = False
for k in nodes_keys:
if key.startswith(k + ':'):
found = True
break
if not found:
nodes_keys_clean.append(key)
return nodes_keys_clean
def get_nodes(self):
return [n.identifier for n in self.user_tree.all_nodes_itr()]
......@@ -357,7 +343,7 @@ class ParserNode:
'title': name,
'pId': node.parent_key,
'isParent': True,
'open': node.is_root(),
'open': node.is_org_root(),
'meta': {
'node': {
"id": node.id,
......
......@@ -51,7 +51,8 @@ class AssetPermissionCreateView(PermissionsMixin, CreateView):
if nodes_id:
nodes_id = nodes_id.split(",")
nodes = Node.objects.filter(id__in=nodes_id).exclude(id=Node.root().id)
nodes = Node.objects.filter(id__in=nodes_id)\
.exclude(id=Node.org_root().id)
form['nodes'].initial = nodes
if assets_id:
assets_id = assets_id.split(",")
......
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