From 411d199e18c50340a50f1e3f2bb838843acba45b Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Tue, 27 Dec 2022 16:53:23 +0800 Subject: [PATCH 1/4] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20asset=20serial?= =?UTF-8?q?izer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/asset/asset.py | 1 + apps/assets/serializers/asset/common.py | 70 ++++++++++++------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index cd4166390..1394acbb7 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -60,6 +60,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ordering = ("name",) serializer_classes = ( ("default", serializers.AssetSerializer), + ("retrieve", serializers.AssetDetailSerializer), ("suggestion", serializers.MiniAssetSerializer), ("platform", serializers.PlatformSerializer), ("gateways", serializers.GatewaySerializer), diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index e34af1f5f..36bb66803 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- # -from rest_framework import serializers -from django.utils.translation import ugettext_lazy as _ -from django.db.transaction import atomic from django.db.models import F +from django.db.transaction import atomic +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers -from common.drf.serializers import WritableNestedModelSerializer from common.drf.fields import LabeledChoiceField, ObjectRelatedField +from common.drf.serializers import WritableNestedModelSerializer from orgs.mixins.serializers import BulkOrgResourceSerializerMixin from ..account import AccountSerializer -from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol from ...const import Category, AllTypes +from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol __all__ = [ 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer', + 'AssetDetailSerializer', ] @@ -66,21 +67,19 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali nodes = ObjectRelatedField(many=True, required=False, queryset=Node.objects, label=_('Nodes')) labels = AssetLabelSerializer(many=True, required=False, label=_('Labels')) protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols')) - accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts')) - enabled_info = serializers.SerializerMethodField() class Meta: model = Asset - fields_mini = ['id', 'name', 'address', 'enabled_info'] + fields_mini = ['id', 'name', 'address'] fields_small = fields_mini + ['is_active', 'comment'] fields_fk = ['domain', 'platform', 'platform'] fields_m2m = [ 'nodes', 'labels', 'protocols', 'accounts', 'nodes_display', ] read_only_fields = [ - 'category', 'type', 'specific', 'info', - 'connectivity', 'date_verified', 'created_by', - 'date_created' + 'category', 'type', 'info', + 'connectivity', 'date_verified', + 'created_by', 'date_created' ] fields = fields_small + fields_fk + fields_m2m + read_only_fields extra_kwargs = { @@ -88,36 +87,13 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali 'address': {'label': _('Address')}, } - def get_field_names(self, declared_fields, info): - names = super().get_field_names(declared_fields, info) - if self.__class__.__name__ != 'AssetSerializer': - names.remove('specific') - return names - - @staticmethod - def get_enabled_info(obj): - platform = obj.platform - automation = platform.automation - return { - 'su_enabled': platform.su_enabled, - 'ping_enabled': automation.ping_enabled, - 'domain_enabled': platform.domain_enabled, - 'ansible_enabled': automation.ansible_enabled, - 'protocols_enabled': platform.protocols_enabled, - 'gather_facts_enabled': automation.gather_facts_enabled, - 'push_account_enabled': automation.push_account_enabled, - 'change_secret_enabled': automation.change_secret_enabled, - 'verify_account_enabled': automation.verify_account_enabled, - 'gather_accounts_enabled': automation.gather_accounts_enabled, - } - @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related('domain', 'platform', 'protocols') \ .annotate(category=F("platform__category")) \ .annotate(type=F("platform__type")) - queryset = queryset.prefetch_related('nodes', 'labels') + queryset = queryset.prefetch_related('nodes', 'labels', 'accounts') return queryset def perform_nodes_display_create(self, instance, nodes_display): @@ -188,6 +164,30 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali return instance +class AssetDetailSerializer(AssetSerializer): + accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts')) + enabled_info = serializers.SerializerMethodField() + + class Meta(AssetSerializer.Meta): + fields = AssetSerializer.Meta.fields + ['accounts', 'enabled_info', 'info', 'specific'] + + @staticmethod + def get_enabled_info(obj): + platform = obj.platform + automation = platform.automation + return { + 'su_enabled': platform.su_enabled, + 'ping_enabled': automation.ping_enabled, + 'domain_enabled': platform.domain_enabled, + 'ansible_enabled': automation.ansible_enabled, + 'protocols_enabled': platform.protocols_enabled, + 'gather_facts_enabled': automation.gather_facts_enabled, + 'change_secret_enabled': automation.change_secret_enabled, + 'verify_account_enabled': automation.verify_account_enabled, + 'gather_accounts_enabled': automation.gather_accounts_enabled, + } + + class MiniAssetSerializer(serializers.ModelSerializer): class Meta: model = Asset From 1b9aad594ca764a155c5655e0097d2b13d9136e9 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Tue, 27 Dec 2022 16:54:47 +0800 Subject: [PATCH 2/4] perf: remove push account --- apps/assets/const/cloud.py | 1 - apps/assets/const/database.py | 1 - apps/assets/const/device.py | 1 - apps/assets/const/host.py | 1 - apps/assets/serializers/platform.py | 2 -- 5 files changed, 6 deletions(-) diff --git a/apps/assets/const/cloud.py b/apps/assets/const/cloud.py index be2637ddf..22240cc77 100644 --- a/apps/assets/const/cloud.py +++ b/apps/assets/const/cloud.py @@ -25,7 +25,6 @@ class CloudTypes(BaseType): 'gather_facts_enabled': False, 'verify_account_enabled': False, 'change_secret_enabled': False, - 'push_account_enabled': False, 'gather_accounts_enabled': False, } } diff --git a/apps/assets/const/database.py b/apps/assets/const/database.py index 7df58e0a5..53c06bff0 100644 --- a/apps/assets/const/database.py +++ b/apps/assets/const/database.py @@ -33,7 +33,6 @@ class DatabaseTypes(BaseType): 'gather_accounts_enabled': True, 'verify_account_enabled': True, 'change_secret_enabled': True, - 'push_account_enabled': True, } } return constrains diff --git a/apps/assets/const/device.py b/apps/assets/const/device.py index 2508900dc..cbd9d7b27 100644 --- a/apps/assets/const/device.py +++ b/apps/assets/const/device.py @@ -40,7 +40,6 @@ class DeviceTypes(BaseType): 'gather_accounts_enabled': False, 'verify_account_enabled': False, 'change_secret_enabled': False, - 'push_account_enabled': False, } } diff --git a/apps/assets/const/host.py b/apps/assets/const/host.py index 99e7c4598..91c6b51be 100644 --- a/apps/assets/const/host.py +++ b/apps/assets/const/host.py @@ -54,7 +54,6 @@ class HostTypes(BaseType): 'gather_accounts_enabled': True, 'verify_account_enabled': True, 'change_secret_enabled': True, - 'push_account_enabled': True, }, cls.WINDOWS: { 'ansible_config': { diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 6627592f9..ab72cecc9 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -56,8 +56,6 @@ class PlatformAutomationSerializer(serializers.ModelSerializer): "gather_facts_method": {"label": "收集信息方式"}, "verify_account_enabled": {"label": "启用校验账号"}, "verify_account_method": {"label": "校验账号方式"}, - "push_account_enabled": {"label": "启用推送账号"}, - "push_account_method": {"label": "推送账号方式"}, "change_secret_enabled": {"label": "启用账号改密"}, "change_secret_method": {"label": "账号改密方式"}, "gather_accounts_enabled": {"label": "启用账号收集"}, From ddca4dce41839bf298b33bdce826e4d5d2963895 Mon Sep 17 00:00:00 2001 From: Aaron3S <chenyang@fit2cloud.com> Date: Tue, 27 Dec 2022 15:04:43 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81pyhton=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/models/job.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 1b6a33b59..98ec9797f 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -165,12 +165,11 @@ class JobExecution(JMSOrgBaseModel): if self.current_job.type != 'adhoc': return result = self.current_job.args - result += " chdir={}".format(self.current_job.chdir) - + if self.current_job.chdir: + result += " chdir={}".format(self.current_job.chdir) if self.current_job.module in ['python']: result += " executable={}".format(self.current_job.module) - print(result) - return self.job.args + return result def get_runner(self): inv = self.current_job.inventory @@ -238,7 +237,7 @@ class JobExecution(JMSOrgBaseModel): @property def is_finished(self): - return self.status in [JobStatus.success, JobStatus.failed] + return self.status in [JobStatus.success, JobStatus.failed, JobStatus.timeout] @property def is_success(self): From 1cfe8d9cc88108c9d5bf2b93c5d8ffa66767f2c1 Mon Sep 17 00:00:00 2001 From: Aaron3S <chenyang@fit2cloud.com> Date: Tue, 27 Dec 2022 16:06:21 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=97=B6=E9=97=B4=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/ansible/callback.py | 1 + apps/ops/ansible/runner.py | 4 ++- apps/ops/api/job.py | 3 +- apps/ops/const.py | 2 ++ .../ops/migrations/0035_auto_20221227_1520.py | 33 +++++++++++++++++++ apps/ops/models/job.py | 3 +- 6 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 apps/ops/migrations/0035_auto_20221227_1520.py diff --git a/apps/ops/ansible/callback.py b/apps/ops/ansible/callback.py index 7d2b1f39d..4bcb9be60 100644 --- a/apps/ops/ansible/callback.py +++ b/apps/ops/ansible/callback.py @@ -8,6 +8,7 @@ class DefaultCallback: 'failed': 'failed', 'running': 'running', 'pending': 'pending', + 'timeout': 'timeout', 'unknown': 'unknown' } diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 8c7517ade..280f8f05c 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -14,7 +14,7 @@ class AdHocRunner: ] def __init__(self, inventory, module, module_args='', pattern='*', project_dir='/tmp/', extra_vars={}, - dry_run=False): + dry_run=False, timeout=-1): self.id = uuid.uuid4() self.inventory = inventory self.pattern = pattern @@ -25,6 +25,7 @@ class AdHocRunner: self.runner = None self.extra_vars = extra_vars self.dry_run = dry_run + self.timeout = timeout def check_module(self): if self.module not in self.cmd_modules_choices: @@ -41,6 +42,7 @@ class AdHocRunner: os.mkdir(self.project_dir, 0o755) ansible_runner.run( + timeout=self.timeout if self.timeout > 0 else None, extravars=self.extra_vars, host_pattern=self.pattern, private_data_dir=self.project_dir, diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 1c7cac33d..817e5040e 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -7,7 +7,7 @@ from ops.models import Job, JobExecution from ops.serializers.job import JobSerializer, JobExecutionSerializer __all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', - 'JobAssetDetail', 'JobExecutionTaskDetail','FrequentUsernames'] + 'JobAssetDetail', 'JobExecutionTaskDetail', 'FrequentUsernames'] from ops.tasks import run_ops_job_execution from ops.variables import JMS_JOB_VARIABLE_HELP @@ -110,6 +110,7 @@ class JobExecutionTaskDetail(APIView): with tmp_to_org(org): execution = get_object_or_404(JobExecution, task_id=task_id) return Response(data={ + 'status': execution.status, 'is_finished': execution.is_finished, 'is_success': execution.is_success, 'time_cost': execution.time_cost, diff --git a/apps/ops/const.py b/apps/ops/const.py index 8288a663e..c383ef3c7 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -43,9 +43,11 @@ class RunasPolicies(models.TextChoices): class Modules(models.TextChoices): shell = 'shell', _('Shell') winshell = 'win_shell', _('Powershell') + python = 'python', _('Python') class JobStatus(models.TextChoices): running = 'running', _('Running') success = 'success', _('Success') + timeout = 'timeout', _('Timeout') failed = 'failed', _('Failed') diff --git a/apps/ops/migrations/0035_auto_20221227_1520.py b/apps/ops/migrations/0035_auto_20221227_1520.py new file mode 100644 index 000000000..36752a9bb --- /dev/null +++ b/apps/ops/migrations/0035_auto_20221227_1520.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.14 on 2022-12-27 07:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ops', '0034_alter_celerytask_options'), + ] + + operations = [ + migrations.AlterField( + model_name='historicaljob', + name='module', + field=models.CharField(choices=[('shell', 'Shell'), ('win_shell', 'Powershell'), ('python', 'Python')], default='shell', max_length=128, null=True, verbose_name='Module'), + ), + migrations.AlterField( + model_name='historicaljob', + name='timeout', + field=models.IntegerField(default=-1, verbose_name='Timeout (Seconds)'), + ), + migrations.AlterField( + model_name='job', + name='module', + field=models.CharField(choices=[('shell', 'Shell'), ('win_shell', 'Powershell'), ('python', 'Python')], default='shell', max_length=128, null=True, verbose_name='Module'), + ), + migrations.AlterField( + model_name='job', + name='timeout', + field=models.IntegerField(default=-1, verbose_name='Timeout (Seconds)'), + ), + ] diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 98ec9797f..85f960df1 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -27,7 +27,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin): module = models.CharField(max_length=128, choices=Modules.choices, default=Modules.shell, verbose_name=_('Module'), null=True) chdir = models.CharField(default="", max_length=1024, verbose_name=_('Chdir'), null=True, blank=True) - timeout = models.IntegerField(default=60, verbose_name=_('Timeout (Seconds)')) + timeout = models.IntegerField(default=-1, verbose_name=_('Timeout (Seconds)')) playbook = models.ForeignKey('ops.Playbook', verbose_name=_("Playbook"), null=True, on_delete=models.SET_NULL) type = models.CharField(max_length=128, choices=Types.choices, default=Types.adhoc, verbose_name=_("Type")) creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) @@ -197,6 +197,7 @@ class JobExecution(JMSOrgBaseModel): runner = AdHocRunner( self.inventory_path, module, + timeout=self.current_job.timeout, module_args=args, pattern="all", project_dir=self.private_dir,