merge: v3

pull/9248/head
Bai 2022-12-27 17:13:29 +08:00
commit 0328fd1bb0
13 changed files with 83 additions and 47 deletions

View File

@ -60,6 +60,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet):
ordering = ("name",) ordering = ("name",)
serializer_classes = ( serializer_classes = (
("default", serializers.AssetSerializer), ("default", serializers.AssetSerializer),
("retrieve", serializers.AssetDetailSerializer),
("suggestion", serializers.MiniAssetSerializer), ("suggestion", serializers.MiniAssetSerializer),
("platform", serializers.PlatformSerializer), ("platform", serializers.PlatformSerializer),
("gateways", serializers.GatewaySerializer), ("gateways", serializers.GatewaySerializer),

View File

@ -25,7 +25,6 @@ class CloudTypes(BaseType):
'gather_facts_enabled': False, 'gather_facts_enabled': False,
'verify_account_enabled': False, 'verify_account_enabled': False,
'change_secret_enabled': False, 'change_secret_enabled': False,
'push_account_enabled': False,
'gather_accounts_enabled': False, 'gather_accounts_enabled': False,
} }
} }

View File

@ -33,7 +33,6 @@ class DatabaseTypes(BaseType):
'gather_accounts_enabled': True, 'gather_accounts_enabled': True,
'verify_account_enabled': True, 'verify_account_enabled': True,
'change_secret_enabled': True, 'change_secret_enabled': True,
'push_account_enabled': True,
} }
} }
return constrains return constrains

View File

@ -40,7 +40,6 @@ class DeviceTypes(BaseType):
'gather_accounts_enabled': False, 'gather_accounts_enabled': False,
'verify_account_enabled': False, 'verify_account_enabled': False,
'change_secret_enabled': False, 'change_secret_enabled': False,
'push_account_enabled': False,
} }
} }

View File

@ -54,7 +54,6 @@ class HostTypes(BaseType):
'gather_accounts_enabled': True, 'gather_accounts_enabled': True,
'verify_account_enabled': True, 'verify_account_enabled': True,
'change_secret_enabled': True, 'change_secret_enabled': True,
'push_account_enabled': True,
}, },
cls.WINDOWS: { cls.WINDOWS: {
'ansible_config': { 'ansible_config': {

View File

@ -1,21 +1,22 @@
# -*- coding: utf-8 -*- # -*- 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.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.fields import LabeledChoiceField, ObjectRelatedField
from common.drf.serializers import WritableNestedModelSerializer
from orgs.mixins.serializers import BulkOrgResourceSerializerMixin from orgs.mixins.serializers import BulkOrgResourceSerializerMixin
from ..account import AccountSerializer from ..account import AccountSerializer
from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol
from ...const import Category, AllTypes from ...const import Category, AllTypes
from ...models import Asset, Node, Platform, Label, Domain, Account, Protocol
__all__ = [ __all__ = [
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer', 'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer',
'AssetDetailSerializer',
] ]
@ -71,16 +72,16 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali
class Meta: class Meta:
model = Asset model = Asset
fields_mini = ['id', 'name', 'address', 'enabled_info'] fields_mini = ['id', 'name', 'address']
fields_small = fields_mini + ['is_active', 'comment'] fields_small = fields_mini + ['is_active', 'comment']
fields_fk = ['domain', 'platform', 'platform'] fields_fk = ['domain', 'platform', 'platform']
fields_m2m = [ fields_m2m = [
'nodes', 'labels', 'protocols', 'accounts', 'nodes_display', 'nodes', 'labels', 'protocols', 'accounts', 'nodes_display',
] ]
read_only_fields = [ read_only_fields = [
'category', 'type', 'specific', 'info', 'category', 'type', 'info',
'connectivity', 'date_verified', 'created_by', 'connectivity', 'date_verified',
'date_created' 'created_by', 'date_created'
] ]
fields = fields_small + fields_fk + fields_m2m + read_only_fields fields = fields_small + fields_fk + fields_m2m + read_only_fields
extra_kwargs = { extra_kwargs = {
@ -89,36 +90,13 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali
'nodes_display': {'label': _('Node path')}, 'nodes_display': {'label': _('Node path')},
} }
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 @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('domain', 'platform', 'protocols') \ queryset = queryset.prefetch_related('domain', 'platform', 'protocols') \
.annotate(category=F("platform__category")) \ .annotate(category=F("platform__category")) \
.annotate(type=F("platform__type")) .annotate(type=F("platform__type"))
queryset = queryset.prefetch_related('nodes', 'labels') queryset = queryset.prefetch_related('nodes', 'labels', 'accounts')
return queryset return queryset
def perform_nodes_display_create(self, instance, nodes_display): def perform_nodes_display_create(self, instance, nodes_display):
@ -189,6 +167,30 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali
return instance 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 MiniAssetSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset

View File

@ -56,8 +56,6 @@ class PlatformAutomationSerializer(serializers.ModelSerializer):
"gather_facts_method": {"label": "收集信息方式"}, "gather_facts_method": {"label": "收集信息方式"},
"verify_account_enabled": {"label": "启用校验账号"}, "verify_account_enabled": {"label": "启用校验账号"},
"verify_account_method": {"label": "校验账号方式"}, "verify_account_method": {"label": "校验账号方式"},
"push_account_enabled": {"label": "启用推送账号"},
"push_account_method": {"label": "推送账号方式"},
"change_secret_enabled": {"label": "启用账号改密"}, "change_secret_enabled": {"label": "启用账号改密"},
"change_secret_method": {"label": "账号改密方式"}, "change_secret_method": {"label": "账号改密方式"},
"gather_accounts_enabled": {"label": "启用账号收集"}, "gather_accounts_enabled": {"label": "启用账号收集"},

View File

@ -8,6 +8,7 @@ class DefaultCallback:
'failed': 'failed', 'failed': 'failed',
'running': 'running', 'running': 'running',
'pending': 'pending', 'pending': 'pending',
'timeout': 'timeout',
'unknown': 'unknown' 'unknown': 'unknown'
} }

View File

@ -14,7 +14,7 @@ class AdHocRunner:
] ]
def __init__(self, inventory, module, module_args='', pattern='*', project_dir='/tmp/', extra_vars={}, 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.id = uuid.uuid4()
self.inventory = inventory self.inventory = inventory
self.pattern = pattern self.pattern = pattern
@ -25,6 +25,7 @@ class AdHocRunner:
self.runner = None self.runner = None
self.extra_vars = extra_vars self.extra_vars = extra_vars
self.dry_run = dry_run self.dry_run = dry_run
self.timeout = timeout
def check_module(self): def check_module(self):
if self.module not in self.cmd_modules_choices: if self.module not in self.cmd_modules_choices:
@ -41,6 +42,7 @@ class AdHocRunner:
os.mkdir(self.project_dir, 0o755) os.mkdir(self.project_dir, 0o755)
ansible_runner.run( ansible_runner.run(
timeout=self.timeout if self.timeout > 0 else None,
extravars=self.extra_vars, extravars=self.extra_vars,
host_pattern=self.pattern, host_pattern=self.pattern,
private_data_dir=self.project_dir, private_data_dir=self.project_dir,

View File

@ -7,7 +7,7 @@ from ops.models import Job, JobExecution
from ops.serializers.job import JobSerializer, JobExecutionSerializer from ops.serializers.job import JobSerializer, JobExecutionSerializer
__all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', __all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView',
'JobAssetDetail', 'JobExecutionTaskDetail','FrequentUsernames'] 'JobAssetDetail', 'JobExecutionTaskDetail', 'FrequentUsernames']
from ops.tasks import run_ops_job_execution from ops.tasks import run_ops_job_execution
from ops.variables import JMS_JOB_VARIABLE_HELP from ops.variables import JMS_JOB_VARIABLE_HELP
@ -110,6 +110,7 @@ class JobExecutionTaskDetail(APIView):
with tmp_to_org(org): with tmp_to_org(org):
execution = get_object_or_404(JobExecution, task_id=task_id) execution = get_object_or_404(JobExecution, task_id=task_id)
return Response(data={ return Response(data={
'status': execution.status,
'is_finished': execution.is_finished, 'is_finished': execution.is_finished,
'is_success': execution.is_success, 'is_success': execution.is_success,
'time_cost': execution.time_cost, 'time_cost': execution.time_cost,

View File

@ -43,9 +43,11 @@ class RunasPolicies(models.TextChoices):
class Modules(models.TextChoices): class Modules(models.TextChoices):
shell = 'shell', _('Shell') shell = 'shell', _('Shell')
winshell = 'win_shell', _('Powershell') winshell = 'win_shell', _('Powershell')
python = 'python', _('Python')
class JobStatus(models.TextChoices): class JobStatus(models.TextChoices):
running = 'running', _('Running') running = 'running', _('Running')
success = 'success', _('Success') success = 'success', _('Success')
timeout = 'timeout', _('Timeout')
failed = 'failed', _('Failed') failed = 'failed', _('Failed')

View File

@ -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)'),
),
]

View File

@ -27,7 +27,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
module = models.CharField(max_length=128, choices=Modules.choices, default=Modules.shell, module = models.CharField(max_length=128, choices=Modules.choices, default=Modules.shell,
verbose_name=_('Module'), null=True) verbose_name=_('Module'), null=True)
chdir = models.CharField(default="", max_length=1024, verbose_name=_('Chdir'), null=True, blank=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) 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")) 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) creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True)
@ -165,12 +165,11 @@ class JobExecution(JMSOrgBaseModel):
if self.current_job.type != 'adhoc': if self.current_job.type != 'adhoc':
return return
result = self.current_job.args 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']: if self.current_job.module in ['python']:
result += " executable={}".format(self.current_job.module) result += " executable={}".format(self.current_job.module)
print(result) return result
return self.job.args
def get_runner(self): def get_runner(self):
inv = self.current_job.inventory inv = self.current_job.inventory
@ -198,6 +197,7 @@ class JobExecution(JMSOrgBaseModel):
runner = AdHocRunner( runner = AdHocRunner(
self.inventory_path, self.inventory_path,
module, module,
timeout=self.current_job.timeout,
module_args=args, module_args=args,
pattern="all", pattern="all",
project_dir=self.private_dir, project_dir=self.private_dir,
@ -238,7 +238,7 @@ class JobExecution(JMSOrgBaseModel):
@property @property
def is_finished(self): def is_finished(self):
return self.status in [JobStatus.success, JobStatus.failed] return self.status in [JobStatus.success, JobStatus.failed, JobStatus.timeout]
@property @property
def is_success(self): def is_success(self):