perf: 修改 signal handler

pull/9463/head
ibuler 2023-02-08 10:14:09 +08:00
parent 22953c0306
commit 17fce76ac4
17 changed files with 186 additions and 148 deletions

View File

@ -2,9 +2,8 @@ from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver from django.dispatch import receiver
from assets.models import Asset from assets.models import Asset
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
from .automations.push_account.manager import PushAccountManager
from .models import Account from .models import Account
logger = get_logger(__name__) logger = get_logger(__name__)

View File

@ -90,6 +90,11 @@ class Migration(migrations.Migration):
old_name='ip', old_name='ip',
new_name='address', new_name='address',
), ),
migrations.AlterField(
model_name='asset',
name='address',
field=models.CharField(db_index=True, max_length=1024, verbose_name='Address'),
),
migrations.AddField( migrations.AddField(
model_name='asset', model_name='asset',
name='date_updated', name='date_updated',

View File

@ -105,7 +105,7 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel):
Type = const.AllTypes Type = const.AllTypes
name = models.CharField(max_length=128, verbose_name=_('Name')) name = models.CharField(max_length=128, verbose_name=_('Name'))
address = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True) address = models.CharField(max_length=1024, verbose_name=_('Address'), db_index=True)
platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets') platform = models.ForeignKey(Platform, on_delete=models.PROTECT, verbose_name=_("Platform"), related_name='assets')
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets',
verbose_name=_("Domain"), on_delete=models.SET_NULL) verbose_name=_("Domain"), on_delete=models.SET_NULL)

View File

@ -1,11 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.db.models.signals import post_save, m2m_changed, pre_delete, post_delete, pre_save from django.db.models.signals import (
post_save, m2m_changed, pre_delete, post_delete, pre_save
)
from django.dispatch import receiver from django.dispatch import receiver
from assets.models import Asset, Node, Cloud, Device, Host, Web, Database from assets.models import Asset, Node, Cloud, Device, Host, Web, Database
from assets.tasks.ping import test_assets_connectivity_util
from common.const.signals import POST_ADD, POST_REMOVE, PRE_REMOVE from common.const.signals import POST_ADD, POST_REMOVE, PRE_REMOVE
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
logger = get_logger(__file__) logger = get_logger(__file__)
@ -28,8 +31,7 @@ def on_asset_create(sender, instance=None, created=False, **kwargs):
logger.info("Asset create signal recv: {}".format(instance)) logger.info("Asset create signal recv: {}".format(instance))
# 获取资产硬件信息 # 获取资产硬件信息
# update_assets_fact_util.delay([instance]) test_assets_connectivity_util([instance])
# test_asset_connectivity_util.delay([instance])
# 确保资产存在一个节点 # 确保资产存在一个节点
has_node = instance.nodes.all().exists() has_node = instance.nodes.all().exists()
@ -60,34 +62,6 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
for node in nodes: for node in nodes:
nodes_ancestors_keys.update(Node.get_node_ancestor_keys(node, with_self=True)) nodes_ancestors_keys.update(Node.get_node_ancestor_keys(node, with_self=True))
# 查询所有祖先节点关联的系统用户,都是要跟资产建立关系的
# system_user_ids = SystemUser.objects.filter(
# nodes__key__in=nodes_ancestors_keys
# ).distinct().values_list('id', flat=True)
# 查询所有已存在的关系
# m2m_model = SystemUser.assets.through
# exist = set(m2m_model.objects.filter(
# systemuser_id__in=system_user_ids, asset_id__in=asset_ids
# ).values_list('systemuser_id', 'asset_id'))
# TODO 优化
# to_create = []
# for system_user_id in system_user_ids:
# asset_ids_to_push = []
# for asset_id in asset_ids:
# if (system_user_id, asset_id) in exist:
# continue
# asset_ids_to_push.append(asset_id)
# to_create.append(m2m_model(
# systemuser_id=system_user_id,
# asset_id=asset_id,
# org_id=instance.org_id
# ))
# if asset_ids_to_push:
# push_system_user_to_assets.delay(system_user_id, asset_ids_to_push)
# m2m_model.objects.bulk_create(to_create)
#
RELATED_NODE_IDS = '_related_node_ids' RELATED_NODE_IDS = '_related_node_ids'

View File

@ -1,23 +1,22 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from celery import shared_task from celery import shared_task
from django.utils.translation import gettext_noop, gettext_lazy as _ from django.utils.translation import gettext_noop
from common.utils import get_logger
from assets.const import AutomationTypes, GATEWAY_NAME from assets.const import AutomationTypes, GATEWAY_NAME
from common.utils import get_logger
from orgs.utils import org_aware_func from orgs.utils import org_aware_func
from .common import automation_execute_start from .common import automation_execute_start
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'test_asset_connectivity_util', 'test_assets_connectivity_task',
'test_assets_connectivity_manual', 'test_assets_connectivity_manual',
'test_node_assets_connectivity_manual', 'test_node_assets_connectivity_manual',
] ]
def test_connectivity_util(assets, tp, task_name, local_port=None): def _test_connectivity_util(assets, tp, task_name, local_port=None):
if not assets: if not assets:
return return
@ -30,8 +29,9 @@ def test_connectivity_util(assets, tp, task_name, local_port=None):
automation_execute_start(task_name, tp, child_snapshot) automation_execute_start(task_name, tp, child_snapshot)
@shared_task
@org_aware_func('assets') @org_aware_func('assets')
def test_asset_connectivity_util(assets, task_name=None, local_port=None): def test_assets_connectivity_task(assets, task_name=None, local_port=None):
from assets.models import PingAutomation from assets.models import PingAutomation
if task_name is None: if task_name is None:
task_name = gettext_noop("Test assets connectivity ") task_name = gettext_noop("Test assets connectivity ")
@ -39,26 +39,24 @@ def test_asset_connectivity_util(assets, task_name=None, local_port=None):
task_name = PingAutomation.generate_unique_name(task_name) task_name = PingAutomation.generate_unique_name(task_name)
gateway_assets = assets.filter(platform__name=GATEWAY_NAME) gateway_assets = assets.filter(platform__name=GATEWAY_NAME)
test_connectivity_util( _test_connectivity_util(
gateway_assets, AutomationTypes.ping_gateway, task_name, local_port gateway_assets, AutomationTypes.ping_gateway, task_name, local_port
) )
non_gateway_assets = assets.exclude(platform__name=GATEWAY_NAME) non_gateway_assets = assets.exclude(platform__name=GATEWAY_NAME)
test_connectivity_util(non_gateway_assets, AutomationTypes.ping, task_name) _test_connectivity_util(non_gateway_assets, AutomationTypes.ping, task_name)
@shared_task(queue="ansible", verbose_name=_('Manually test the connectivity of a asset'))
def test_assets_connectivity_manual(asset_ids, local_port=None): def test_assets_connectivity_manual(asset_ids, local_port=None):
from assets.models import Asset from assets.models import Asset
assets = Asset.objects.filter(id__in=asset_ids) assets = Asset.objects.filter(id__in=asset_ids)
task_name = gettext_noop("Test assets connectivity ") task_name = gettext_noop("Test assets connectivity ")
test_asset_connectivity_util(assets, task_name, local_port) test_assets_connectivity_task.delay(assets, task_name, local_port)
@shared_task(queue="ansible", verbose_name=_('Manually test the connectivity of assets under a node'))
def test_node_assets_connectivity_manual(node_id, local_port=None): def test_node_assets_connectivity_manual(node_id, local_port=None):
from assets.models import Node from assets.models import Node
node = Node.objects.get(id=node_id) node = Node.objects.get(id=node_id)
task_name = gettext_noop("Test if the assets under the node are connectable ") task_name = gettext_noop("Test if the assets under the node are connectable ")
assets = node.get_all_assets() assets = node.get_all_assets()
test_asset_connectivity_util(assets, task_name, local_port) test_assets_connectivity_task.delay(*assets, task_name, local_port)

View File

@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
#
import functools
import threading
import time
import uuid
from django.core.cache import cache
from django.db import transaction
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
class Singleton(object):
""" 单例类 """
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
def _run_func_if_is_last(ttl, func, *args, **kwargs):
ix = uuid.uuid4().__str__()
key = f'DELAY_RUN_{func.__name__}'
cache.set(key, ix, ttl)
st = (ttl - 2 > 1) and ttl - 2 or 1
time.sleep(st)
got = cache.get(key, None)
if ix == got:
func(*args, **kwargs)
cache.delete(key)
def delay_run(ttl=5):
def inner(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
t = threading.Thread(target=_run_func_if_is_last, args=(ttl, func, *args), kwargs=kwargs)
t.start()
return wrapper
return inner
@delay_run(ttl=10)
def run_it_many(username, year=2000):
print("Hello, %s, now is %s" % (username, year))
if __name__ == '__main__':
for i in range(20):
run_it_many('test', 2000)

134
apps/common/decorators.py Normal file
View File

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
#
import functools
import inspect
import time
import uuid
from concurrent.futures import ThreadPoolExecutor
from django.core.cache import cache
from django.db import transaction
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
class Singleton(object):
""" 单例类 """
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
def _run_func_if_is_last(ttl, func, *args, **kwargs):
ix = uuid.uuid4().__str__()
key = f'DELAY_RUN_{func.__name__}'
cache.set(key, ix, ttl)
st = (ttl - 2 > 1) and ttl - 2 or 2
time.sleep(st)
got = cache.get(key, None)
if ix == got:
func(*args, **kwargs)
executor = ThreadPoolExecutor(10)
def delay_run(ttl=5):
def inner(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
executor.submit(_run_func_if_is_last, ttl, func, *args, **kwargs)
return wrapper
return inner
def _merge_run(ttl, func, *args, **kwargs):
if not args or not isinstance(args[0], (list, tuple)):
raise ValueError('args[0] must be list or tuple')
key = f'DELAY_MERGE_RUN_{func.__name__}'
ix = uuid.uuid4().__str__()
value = cache.get(key, [])
value.extend(args[0])
st = (ttl - 2 > 1) and ttl - 2 or 2
time.sleep(st)
got = cache.get(key, None)
if ix == got:
func(*args, **kwargs)
def merge_delay_run(ttl):
"""
合并 func 参数延迟执行, ttl 秒内, 只执行最后一次
func 参数必须是 *args
:param ttl:
:return:
"""
def inner(func):
sigs = inspect.signature(func)
if len(sigs.parameters) != 1:
raise ValueError('func must have one arguments: %s' % func.__name__)
param = list(sigs.parameters.values())[0]
if not str(param).startswith('*'):
raise ValueError('func args must be startswith *: %s' % func.__name__)
@functools.wraps(func)
def wrapper(*args):
key = f'DELAY_MERGE_RUN_{func.__name__}'
values = cache.get(key, [])
new_arg = [*values, *args]
cache.set(key, new_arg, ttl)
return delay_run(ttl)(func)(*new_arg)
return wrapper
return inner
def delay_run(ttl=5):
"""
延迟执行函数, ttl 秒内, 只执行最后一次
:param ttl:
:return:
"""
def inner(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
executor.submit(_run_func_if_is_last, ttl, func, *args, **kwargs)
return wrapper
return inner
@delay_run(ttl=10)
def test_delay_run(username, year=2000):
print("Hello, %s, now is %s" % (username, year))
@merge_delay_run(ttl=10)
def test_merge_delay_run(*users):
name = ','.join(users)
print("Hello, %s, now is %s" % (name, time.time()))

View File

@ -7,7 +7,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
from common.utils.connection import RedisPubSub from common.utils.connection import RedisPubSub
from notifications.backends import BACKEND from notifications.backends import BACKEND

View File

@ -10,7 +10,7 @@ from django.dispatch import receiver
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from common.const.signals import PRE_REMOVE, POST_REMOVE from common.const.signals import PRE_REMOVE, POST_REMOVE
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.signals import django_ready from common.signals import django_ready
from common.utils import get_logger from common.utils import get_logger
from common.utils.connection import RedisPubSub from common.utils.connection import RedisPubSub

View File

@ -4,27 +4,25 @@ from collections import defaultdict
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from users.models import User
from assets.models import Asset from assets.models import Asset
from assets.utils import NodeAssetsUtil from assets.utils import NodeAssetsUtil
from common.db.models import output_as_string
from common.decorators import on_transaction_commit
from common.utils import get_logger
from common.utils.common import lazyproperty, timeit
from orgs.models import Organization from orgs.models import Organization
from orgs.utils import ( from orgs.utils import (
current_org, current_org,
tmp_to_org, tmp_to_org,
tmp_to_root_org tmp_to_root_org
) )
from common.decorator import on_transaction_commit
from common.utils import get_logger
from common.utils.common import lazyproperty, timeit
from common.db.models import output_as_string
from perms.locks import UserGrantedTreeRebuildLock from perms.locks import UserGrantedTreeRebuildLock
from perms.models import ( from perms.models import (
AssetPermission, AssetPermission,
UserAssetGrantedTreeNodeRelation, UserAssetGrantedTreeNodeRelation,
PermNode PermNode
) )
from users.models import User
from .permission import AssetPermissionUtil from .permission import AssetPermissionUtil
logger = get_logger(__name__) logger = get_logger(__name__)
@ -78,7 +76,7 @@ class UserPermTreeRefreshUtil(_UserPermTreeCacheMixin):
end = time.time() end = time.time()
logger.info( logger.info(
'Refresh user [{user}] org [{org}] perm tree, user {use_time:.2f}s' 'Refresh user [{user}] org [{org}] perm tree, user {use_time:.2f}s'
''.format(user=self.user, org=org, use_time=end-start) ''.format(user=self.user, org=org, use_time=end - start)
) )
def _clean_user_perm_tree_for_legacy_org(self): def _clean_user_perm_tree_for_legacy_org(self):

View File

@ -8,7 +8,7 @@ from django.db.utils import ProgrammingError, OperationalError
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.signals import django_ready from common.signals import django_ready
from common.utils import get_logger, ssh_key_gen from common.utils import get_logger, ssh_key_gen
from common.utils.connection import RedisPubSub from common.utils.connection import RedisPubSub

View File

@ -2,7 +2,7 @@ from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from assets.models import Asset from assets.models import Asset
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.signals import django_ready from common.signals import django_ready
from common.utils import get_logger from common.utils import get_logger
from ..utils import db_port_manager from ..utils import db_port_manager

View File

@ -2,7 +2,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
from common.utils.connection import RedisPubSub from common.utils.connection import RedisPubSub
from ..models import Task from ..models import Task

View File

@ -1,16 +1,16 @@
import os import os
import time
import socket import socket
import threading import threading
import time
from django.conf import settings from django.conf import settings
from common.db.utils import close_old_connections from common.db.utils import close_old_connections
from common.decorator import Singleton from common.decorators import Singleton
from common.utils import get_disk_usage, get_cpu_load, get_memory_usage, get_logger from common.utils import get_disk_usage, get_cpu_load, get_memory_usage
from .serializers.terminal import TerminalRegistrationSerializer, StatSerializer
from .const import TerminalType from .const import TerminalType
from .models import Terminal from .models import Terminal
from .serializers.terminal import TerminalRegistrationSerializer, StatSerializer
__all__ = ['CoreTerminal', 'CeleryTerminal'] __all__ = ['CoreTerminal', 'CeleryTerminal']

View File

@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
from assets.const import DatabaseTypes from assets.const import DatabaseTypes
from assets.models import Database from assets.models import Database
from common.decorator import Singleton from common.decorators import Singleton
from common.exceptions import JMSException from common.exceptions import JMSException
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.dispatch import receiver
from django.db.models.signals import post_save, m2m_changed from django.db.models.signals import post_save, m2m_changed
from common.decorator import on_transaction_commit from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
from tickets.models import Ticket from tickets.models import Ticket

View File

@ -1,20 +1,19 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.dispatch import receiver
from django_auth_ldap.backend import populate_user
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django_cas_ng.signals import cas_user_authenticated
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver
from django_auth_ldap.backend import populate_user
from django_cas_ng.signals import cas_user_authenticated
from authentication.backends.oauth2.signals import oauth2_create_or_update_user
from authentication.backends.oidc.signals import openid_create_or_update_user from authentication.backends.oidc.signals import openid_create_or_update_user
from authentication.backends.saml2.signals import saml2_create_or_update_user from authentication.backends.saml2.signals import saml2_create_or_update_user
from authentication.backends.oauth2.signals import oauth2_create_or_update_user from common.decorators import on_transaction_commit
from common.utils import get_logger from common.utils import get_logger
from common.decorator import on_transaction_commit
from .signals import post_user_create
from .models import User, UserPasswordHistory from .models import User, UserPasswordHistory
from .signals import post_user_create
logger = get_logger(__file__) logger = get_logger(__file__)
@ -47,9 +46,9 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def save_passwd_change(sender, instance: User, **kwargs): def save_passwd_change(sender, instance: User, **kwargs):
passwords = UserPasswordHistory.objects\ passwords = UserPasswordHistory.objects \
.filter(user=instance) \ .filter(user=instance) \
.order_by('-date_created')\ .order_by('-date_created') \
.values_list('password', flat=True) .values_list('password', flat=True)
passwords = passwords[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)] passwords = passwords[:int(settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT)]