perf: 解决冲突

This commit is contained in:
jiangweidong 2023-02-22 11:44:46 +08:00
commit cde59f7ae8
26 changed files with 76 additions and 93 deletions

View File

@ -45,6 +45,7 @@ class AccountViewSet(OrgBulkModelViewSet):
accounts = asset.accounts.all() accounts = asset.accounts.all()
else: else:
accounts = [] accounts = []
accounts = self.filter_queryset(accounts)
serializer = serializers.AccountSerializer(accounts, many=True) serializer = serializers.AccountSerializer(accounts, many=True)
return Response(data=serializer.data) return Response(data=serializer.data)

View File

@ -3,6 +3,7 @@ from rest_framework.response import Response
from accounts import serializers from accounts import serializers
from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task
from assets.exceptions import NotSupportedTemporarilyError
__all__ = [ __all__ = [
'AccountsTaskCreateAPI', 'AccountsTaskCreateAPI',
@ -28,6 +29,11 @@ class AccountsTaskCreateAPI(CreateAPIView):
if data['action'] == 'push': if data['action'] == 'push':
task = push_accounts_to_assets_task.delay(account_ids) task = push_accounts_to_assets_task.delay(account_ids)
else: else:
account = accounts[0]
asset = account.asset
if not asset.auto_info['ansible_enabled'] or \
not asset.auto_info['ping_enabled']:
raise NotSupportedTemporarilyError()
task = verify_accounts_connectivity_task.delay(account_ids) task = verify_accounts_connectivity_task.delay(account_ids)
data = getattr(serializer, '_data', {}) data = getattr(serializer, '_data', {})

View File

@ -86,6 +86,10 @@ class ChangeSecretManager(AccountBasePlaybookManager):
accounts = accounts.filter(username__in=self.snapshot_account_usernames) accounts = accounts.filter(username__in=self.snapshot_account_usernames)
accounts = accounts.filter(secret_type=self.secret_type) accounts = accounts.filter(secret_type=self.secret_type)
if not accounts:
print('没有发现待改密账号: %s 用户名: %s 类型: %s' % (asset.name, account.username, self.secret_type))
return []
method_attr = getattr(automation, self.method_type() + '_method') method_attr = getattr(automation, self.method_type() + '_method')
method_hosts = self.method_hosts_mapper[method_attr] method_hosts = self.method_hosts_mapper[method_attr]
method_hosts = [h for h in method_hosts if h != host['name']] method_hosts = [h for h in method_hosts if h != host['name']]
@ -137,8 +141,10 @@ class ChangeSecretManager(AccountBasePlaybookManager):
recorder.status = 'success' recorder.status = 'success'
recorder.date_finished = timezone.now() recorder.date_finished = timezone.now()
recorder.save() recorder.save()
print('recorder.new_secret', recorder.new_secret)
account = recorder.account account = recorder.account
if not account:
print("Account not found, deleted ?", recorder)
return
account.secret = recorder.new_secret account.secret = recorder.new_secret
account.save(update_fields=['secret']) account.save(update_fields=['secret'])

View File

@ -36,7 +36,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
def get_accounts(self, privilege_account, accounts: QuerySet): def get_accounts(self, privilege_account, accounts: QuerySet):
if not privilege_account: if not privilege_account:
logger.debug(f'not privilege account') print(f'not privilege account')
return [] return []
snapshot_account_usernames = self.execution.snapshot['accounts'] snapshot_account_usernames = self.execution.snapshot['accounts']
if '*' in snapshot_account_usernames: if '*' in snapshot_account_usernames:

View File

@ -6,8 +6,8 @@
tasks: tasks:
- name: Verify account - name: Verify account
mongodb_ping: mongodb_ping:
login_user: "{{ jms_account.username }}" login_user: "{{ account.username }}"
login_password: "{{ jms_account.secret }}" login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}" login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}" login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}" login_database: "{{ jms_asset.spec_info.db_name }}"

View File

@ -6,9 +6,9 @@
tasks: tasks:
- name: Verify account - name: Verify account
oracle_ping: oracle_ping:
login_user: "{{ jms_account.username }}" login_user: "{{ account.username }}"
login_password: "{{ jms_account.secret }}" login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}" login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}" login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}" login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ jms_account.mode }}" mode: "{{ account.mode }}"

View File

@ -6,8 +6,8 @@
tasks: tasks:
- name: Verify account - name: Verify account
community.general.mssql_script: community.general.mssql_script:
login_user: "{{ jms_account.username }}" login_user: "{{ account.username }}"
login_password: "{{ jms_account.secret }}" login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}" login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}" login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}' name: '{{ jms_asset.spec_info.db_name }}'

View File

@ -1,17 +0,0 @@
# Generated by Django 3.2.14 on 2023-02-21 05:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0007_alter_account_options'),
]
operations = [
migrations.AlterModelOptions(
name='account',
options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'},
),
]

View File

@ -68,6 +68,9 @@ class Account(AbsConnectivity, BaseAccount):
('push_account', _('Can push account')), ('push_account', _('Can push account')),
] ]
def __str__(self):
return '{}'.format(self.username)
@lazyproperty @lazyproperty
def platform(self): def platform(self):
return self.asset.platform return self.asset.platform
@ -78,9 +81,6 @@ class Account(AbsConnectivity, BaseAccount):
return self.username return self.username
return self.name return self.name
def __str__(self):
return '{}'.format(self.username)
@lazyproperty @lazyproperty
def has_secret(self): def has_secret(self):
return bool(self.secret) return bool(self.secret)
@ -99,14 +99,6 @@ class Account(AbsConnectivity, BaseAccount):
""" 排除自己和以自己为 su-from 的账号 """ """ 排除自己和以自己为 su-from 的账号 """
return self.asset.accounts.exclude(id=self.id).exclude(su_from=self) return self.asset.accounts.exclude(id=self.id).exclude(su_from=self)
def secret_changed(self):
history = self.history.first()
if not history:
return True
if history.secret != self.secret or history.secret_type != self.secret_type:
return True
return False
class AccountTemplate(BaseAccount): class AccountTemplate(BaseAccount):
class Meta: class Meta:

View File

@ -109,7 +109,7 @@ class BaseAccount(JMSOrgBaseModel):
@property @property
def private_key_path(self): def private_key_path(self):
if not self.secret_type != SecretType.SSH_KEY \ if self.secret_type != SecretType.SSH_KEY \
or not self.secret \ or not self.secret \
or not self.private_key: or not self.private_key:
return None return None

View File

@ -43,7 +43,7 @@ class AccountSerializerCreateValidateMixin:
def push_account(instance, push_now): def push_account(instance, push_now):
if not push_now: if not push_now:
return return
push_accounts_to_assets_task.delay([instance.id], [instance.asset_id]) push_accounts_to_assets_task.delay([instance.id])
def create(self, validated_data): def create(self, validated_data):
push_now = validated_data.pop('push_now', None) push_now = validated_data.pop('push_now', None)
@ -102,7 +102,7 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
class Meta(BaseAccountSerializer.Meta): class Meta(BaseAccountSerializer.Meta):
model = Account model = Account
fields = BaseAccountSerializer.Meta.fields \ fields = BaseAccountSerializer.Meta.fields \
+ ['su_from', 'version', 'asset'] \ + ['su_from', 'asset'] \
+ ['template', 'push_now', 'source'] + ['template', 'push_now', 'source']
extra_kwargs = { extra_kwargs = {
**BaseAccountSerializer.Meta.extra_kwargs, **BaseAccountSerializer.Meta.extra_kwargs,

View File

@ -38,6 +38,8 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe
} }
def validate_name(self, name): def validate_name(self, name):
if self.instance:
return name
if BaseAutomation.objects.filter(name=name, type=self.model_type).exists(): if BaseAutomation.objects.filter(name=name, type=self.model_type).exists():
raise serializers.ValidationError(_('Name already exists')) raise serializers.ValidationError(_('Name already exists'))
return name return name

View File

@ -1,17 +1,3 @@
from django.db.models.signals import pre_save
from django.dispatch import receiver
from common.utils import get_logger from common.utils import get_logger
from .models import Account
logger = get_logger(__name__) logger = get_logger(__name__)
@receiver(pre_save, sender=Account)
def on_account_pre_create(sender, instance, update_fields=(), **kwargs):
# 这是创建时
if instance.version == 0 or instance.secret_changed():
instance.version += 1
# 即使在 root 组织也不怕
instance.org_id = instance.asset.org_id

View File

@ -8,12 +8,10 @@ from rest_framework.response import Response
from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task
from assets import serializers from assets import serializers
from assets.exceptions import NotSupportedTemporarilyError
from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend
from assets.models import Asset, Gateway from assets.models import Asset, Gateway
from assets.tasks import ( from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual
test_assets_connectivity_manual,
update_assets_hardware_info_manual
)
from common.api import SuggestionMixin from common.api import SuggestionMixin
from common.drf.filters import BaseFilterSet from common.drf.filters import BaseFilterSet
from common.utils import get_logger, is_uuid from common.utils import get_logger, is_uuid
@ -154,6 +152,10 @@ class AssetsTaskMixin:
if data["action"] == "refresh": if data["action"] == "refresh":
task = update_assets_hardware_info_manual(assets) task = update_assets_hardware_info_manual(assets)
else: else:
asset = assets[0]
if not asset.auto_info['ansible_enabled'] or \
not asset.auto_info['ping_enabled']:
raise NotSupportedTemporarilyError()
task = test_assets_connectivity_manual(assets) task = test_assets_connectivity_manual(assets)
return task return task
@ -205,9 +207,9 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
asset_ids = [asset.id] asset_ids = [asset.id]
account_ids = accounts.values_list("id", flat=True) account_ids = accounts.values_list("id", flat=True)
if action == "push_account": if action == "push_account":
task = push_accounts_to_assets_task.delay(account_ids, asset_ids) task = push_accounts_to_assets_task.delay(account_ids)
elif action == "test_account": elif action == "test_account":
task = verify_accounts_connectivity_task.delay(account_ids, asset_ids) task = verify_accounts_connectivity_task.delay(account_ids)
else: else:
task = None task = None
return task return task

View File

@ -67,7 +67,7 @@ class BasePlaybookManager:
if not os.path.exists(path): if not os.path.exists(path):
os.makedirs(path, exist_ok=True, mode=0o755) os.makedirs(path, exist_ok=True, mode=0o755)
if settings.DEBUG_DEV: if settings.DEBUG_DEV:
logger.debug('Ansible runtime dir: {}'.format(path)) print(f'Ansible runtime dir: {path}')
return path return path
@staticmethod @staticmethod
@ -156,10 +156,9 @@ class BasePlaybookManager:
return sub_playbook_path return sub_playbook_path
def get_runners(self): def get_runners(self):
# TODO 临时打印一下 找一下打印不出日志的原因
print('ansible runner: 任务开始执行')
assets_group_by_platform = self.get_assets_group_by_platform() assets_group_by_platform = self.get_assets_group_by_platform()
print('ansible runner: 获取资产分组', assets_group_by_platform) if settings.DEBUG_DEV:
print("assets_group_by_platform: {}".format(assets_group_by_platform))
runners = [] runners = []
for platform, assets in assets_group_by_platform.items(): for platform, assets in assets_group_by_platform.items():
assets_bulked = [assets[i:i + self.bulk_size] for i in range(0, len(assets), self.bulk_size)] assets_bulked = [assets[i:i + self.bulk_size] for i in range(0, len(assets), self.bulk_size)]
@ -213,6 +212,7 @@ class BasePlaybookManager:
def file_to_json(path): def file_to_json(path):
with open(path, 'r') as f: with open(path, 'r') as f:
d = json.load(f) d = json.load(f)
return d return d
@staticmethod @staticmethod

View File

@ -1,6 +1,12 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import status from rest_framework import status
from common.exceptions import JMSException from common.exceptions import JMSException
class NodeIsBeingUpdatedByOthers(JMSException): class NodeIsBeingUpdatedByOthers(JMSException):
status_code = status.HTTP_409_CONFLICT status_code = status.HTTP_409_CONFLICT
class NotSupportedTemporarilyError(JMSException):
default_detail = _("This function is not supported temporarily")

View File

@ -12,6 +12,6 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name='asset', name='asset',
options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetaccount', 'Can push account to asset'), ('test_account', 'Can verify account'), ('match_asset', 'Can match asset'), ('change_assettonode', 'Can change asset nodes')], 'verbose_name': 'Asset'}, options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'},
), ),
] ]

View File

@ -1,17 +0,0 @@
# Generated by Django 3.2.14 on 2023-02-21 05:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0109_alter_asset_options'),
]
operations = [
migrations.AlterModelOptions(
name='asset',
options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'},
),
]

View File

@ -277,6 +277,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali
@atomic @atomic
def update(self, instance, validated_data): def update(self, instance, validated_data):
if not validated_data.get('accounts'):
validated_data.pop('accounts', None)
nodes_display = validated_data.pop('nodes_display', '') nodes_display = validated_data.pop('nodes_display', '')
instance = super().update(instance, validated_data) instance = super().update(instance, validated_data)
self.perform_nodes_display_create(instance, nodes_display) self.perform_nodes_display_create(instance, nodes_display)

View File

@ -530,7 +530,7 @@ class Config(dict):
'PERIOD_TASK_ENABLED': True, 'PERIOD_TASK_ENABLED': True,
# 导航栏 帮助 # 导航栏 帮助
'HELP_DOCUMENT_URL': 'http://docs.jumpserver.org', 'HELP_DOCUMENT_URL': 'https://docs.jumpserver.org/zh/v3/',
'HELP_SUPPORT_URL': 'http://www.jumpserver.org/support/', 'HELP_SUPPORT_URL': 'http://www.jumpserver.org/support/',
'FORGOT_PASSWORD_URL': '', 'FORGOT_PASSWORD_URL': '',

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:331188bb5169bb463da018a635589e12a2136d476db264ac7e5d6e5d63ca474a oid sha256:af57d16430705feb02ebbb99fc3a2f5fc3bab69209f558aa4d69b1e8055a6f5f
size 135916 size 136036

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-21 18:29+0800\n" "POT-Creation-Date: 2023-02-21 22:44+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -908,7 +908,7 @@ msgstr "アプリケーション"
msgid "Can match application" msgid "Can match application"
msgstr "アプリケーションを一致させることができます" msgstr "アプリケーションを一致させることができます"
#: assets/api/asset/asset.py:144 #: assets/api/asset/asset.py:142
msgid "Cannot create asset directly, you should create a host or other" msgid "Cannot create asset directly, you should create a host or other"
msgstr "" msgstr ""
"資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま"
@ -1051,6 +1051,10 @@ msgstr "基本"
msgid "Script" msgid "Script"
msgstr "脚本" msgstr "脚本"
#: assets/exceptions.py:12
msgid "This function is not supported temporarily"
msgstr "この機能は一時的にサポートされていません"
#: assets/models/_user.py:25 #: assets/models/_user.py:25
msgid "SSH private key" msgid "SSH private key"
msgstr "SSH秘密鍵" msgstr "SSH秘密鍵"

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2cdc2b875c98f41bd698833a989195d8cc4245f39f52b7eab41ad4d95075cb17 oid sha256:3b6ee4a378810f2515be5020e3fa0b1297e1c207260ca60bb14dc5407ca19c43
size 111666 size 111750

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-21 18:29+0800\n" "POT-Creation-Date: 2023-02-21 22:44+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -902,7 +902,7 @@ msgstr "应用程序"
msgid "Can match application" msgid "Can match application"
msgstr "匹配应用" msgstr "匹配应用"
#: assets/api/asset/asset.py:144 #: assets/api/asset/asset.py:142
msgid "Cannot create asset directly, you should create a host or other" msgid "Cannot create asset directly, you should create a host or other"
msgstr "不能直接创建资产, 你应该创建主机或其他资产" msgstr "不能直接创建资产, 你应该创建主机或其他资产"
@ -1043,6 +1043,10 @@ msgstr "基本"
msgid "Script" msgid "Script"
msgstr "脚本" msgstr "脚本"
#: assets/exceptions.py:12
msgid "This function is not supported temporarily"
msgstr "暂时不支持此功能"
#: assets/models/_user.py:25 #: assets/models/_user.py:25
msgid "SSH private key" msgid "SSH private key"
msgstr "SSH密钥" msgstr "SSH密钥"

View File

@ -6,6 +6,7 @@ from django.conf import settings
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import status from rest_framework import status
from common.exceptions import JMSException
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from ..exception import PlaybookNoValidEntry from ..exception import PlaybookNoValidEntry
from ..models import Playbook from ..models import Playbook
@ -39,7 +40,11 @@ class PlaybookViewSet(OrgBulkModelViewSet):
if 'multipart/form-data' in self.request.headers['Content-Type']: if 'multipart/form-data' in self.request.headers['Content-Type']:
src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name) src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name)
dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__()) dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__())
unzip_playbook(src_path, dest_path) try:
unzip_playbook(src_path, dest_path)
except RuntimeError as e:
raise JMSException(code='invalid_playbook_file', detail={"msg": "Unzip failed"})
if 'main.yml' not in os.listdir(dest_path): if 'main.yml' not in os.listdir(dest_path):
raise PlaybookNoValidEntry raise PlaybookNoValidEntry

View File

@ -111,7 +111,8 @@ class Applet(JMSBaseModel):
return instance return instance
def select_host_account(self): def select_host_account(self):
hosts = list(self.hosts.all()) # 选择激活的发布机
hosts = list(self.hosts.filter(is_active=True).all())
if not hosts: if not hosts:
return None return None