Merge: v3 to dev (#9160)

* fix: 修改 ConnectionTokenSecretSerializer

* perf: connect token secret (#9155)

Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>

* feat: 作业迁移至个人级别

* perf: asset enabled (#9157)

Co-authored-by: feng <1304903146@qq.com>

* perf: 修改ConnectionTokenSecret Gateway数据结构; 修改Domain Gateway Model方法

* perf: ConnectionTokenSecret  返回 domain 信息

* refactor: 移动 Gateway Model 到 asset 目录下

* refactor: 移动 Gateway Model 单独到 gateway 文件中

* perf: 修改 GatewaySerializer 目录

* perf: 修改 GatewaySerializer 目录

Co-authored-by: fit2bot <68588906+fit2bot@users.noreply.github.com>
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
pull/9161/head
Jiangjie.Bai 2022-12-06 11:03:14 +08:00 committed by GitHub
parent 9ef5f17d5e
commit 7842e3e5ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 284 additions and 243 deletions

View File

@ -3,6 +3,7 @@ from .platform import *
from .asset import *
from .label import Label
from .group import *
from .gateway import *
from .domain import *
from .node import *
from .utils import *

View File

@ -1,6 +1,7 @@
from assets.const import GATEWAY_NAME
from .common import Asset
__all__ = ['Host']
class Host(Asset):
pass

View File

@ -50,6 +50,16 @@ class AbsConnectivity(models.Model):
abstract = True
class BaseAccountQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
class BaseAccountManager(models.Manager):
def active(self):
return self.get_queryset().active()
class BaseAccount(JMSOrgBaseModel):
name = models.CharField(max_length=128, verbose_name=_("Name"))
username = models.CharField(max_length=128, blank=True, verbose_name=_('Username'), db_index=True)
@ -62,6 +72,8 @@ class BaseAccount(JMSOrgBaseModel):
comment = models.TextField(blank=True, verbose_name=_('Comment'))
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
objects = BaseAccountManager.from_queryset(BaseAccountQuerySet)()
@property
def has_secret(self):
return bool(self.secret)

View File

@ -2,21 +2,18 @@
#
import uuid
import random
import socket
import paramiko
from django.db import models
from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger, lazyproperty
from orgs.mixins.models import OrgModelMixin
from assets.models import Host, Platform
from assets.const import GATEWAY_NAME, SecretType, Connectivity
from orgs.mixins.models import OrgManager
from .gateway import Gateway
logger = get_logger(__file__)
__all__ = ['Domain', 'Gateway']
__all__ = ['Domain']
class Domain(OrgModelMixin):
@ -33,159 +30,26 @@ class Domain(OrgModelMixin):
def __str__(self):
return self.name
@classmethod
def get_gateway_queryset(cls):
return Gateway.objects.all()
@lazyproperty
def gateways(self):
return self.get_gateway_queryset().filter(domain=self, is_active=True)
def select_gateway(self):
return self.random_gateway()
def random_gateway(self):
gateways = [gw for gw in self.gateways if gw.is_connective]
if gateways:
return random.choice(gateways)
logger.warn(f'Gateway all bad. domain={self}, gateway_num={len(self.gateways)}.')
if self.gateways:
return random.choice(self.gateways)
class GatewayManager(OrgManager):
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(platform__name=GATEWAY_NAME)
return queryset
def bulk_create(self, objs, batch_size=None, ignore_conflicts=False):
platform = Gateway().default_platform
for obj in objs:
obj.platform_id = platform.id
return super().bulk_create(objs, batch_size, ignore_conflicts)
class Gateway(Host):
objects = GatewayManager()
class Meta:
proxy = True
gateways = [gw for gw in self.active_gateways if gw.is_connective]
if not gateways:
gateways = self.active_gateways
logger.warn(f'Gateway all bad. domain={self}, gateway_num={len(gateways)}.')
return random.choice(gateways)
@lazyproperty
def default_platform(self):
return Platform.objects.get(name=GATEWAY_NAME, internal=True)
def save(self, *args, **kwargs):
platform = self.default_platform
self.platform_id = platform.id
return super().save(*args, **kwargs)
def active_gateways(self):
return self.gateways.filter(is_active=True)
@lazyproperty
def select_accounts(self) -> dict:
account_dict = {}
accounts = self.accounts.filter(is_active=True).order_by('-privileged', '-date_updated')
password_account = accounts.filter(secret_type=SecretType.PASSWORD).first()
if password_account:
account_dict[SecretType.PASSWORD] = password_account
def gateways(self):
return self.get_gateway_queryset().filter(domain=self)
ssh_key_account = accounts.filter(secret_type=SecretType.SSH_KEY).first()
if ssh_key_account:
account_dict[SecretType.SSH_KEY] = ssh_key_account
return account_dict
@classmethod
def get_gateway_queryset(cls):
return Gateway.objects.all()
@property
def password(self):
account = self.select_accounts.get(SecretType.PASSWORD)
return account.secret if account else None
@property
def private_key(self):
account = self.select_accounts.get(SecretType.SSH_KEY)
return account.private_key if account else None
@property
def private_key_obj(self):
account = self.select_accounts.get(SecretType.SSH_KEY)
return account.private_key_obj if account else None
@property
def private_key_path(self):
account = self.select_accounts.get(SecretType.SSH_KEY)
return account.private_key_path if account else None
@lazyproperty
def username(self):
accounts = self.select_accounts.values()
if len(accounts) == 0:
return None
accounts = sorted(
accounts, key=lambda x: x['privileged'], reverse=True
)
return accounts[0].username
def test_connective(self, local_port=None):
local_port = self.port if local_port is None else local_port
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.SSHClient()
proxy.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
proxy.connect(
self.address,
port=self.port,
username=self.username,
password=self.password,
pkey=self.private_key_obj
)
except(
paramiko.AuthenticationException,
paramiko.BadAuthenticationType,
paramiko.SSHException,
paramiko.ChannelException,
paramiko.ssh_exception.NoValidConnectionsError,
socket.gaierror
) as e:
err = str(e)
if err.startswith('[Errno None] Unable to connect to port'):
err = _('Unable to connect to port {port} on {address}')
err = err.format(port=self.port, address=self.address)
elif err == 'Authentication failed.':
err = _('Authentication failed')
elif err == 'Connect failed':
err = _('Connect failed')
self.set_connectivity(Connectivity.FAILED)
return False, err
try:
sock = proxy.get_transport().open_channel(
'direct-tcpip', ('127.0.0.1', local_port), ('127.0.0.1', 0)
)
client.connect(
'127.0.0.1',
sock=sock,
timeout=5,
port=local_port,
username=self.username,
password=self.password,
key_filename=self.private_key_path,
)
except (
paramiko.SSHException,
paramiko.ssh_exception.SSHException,
paramiko.ChannelException,
paramiko.AuthenticationException,
TimeoutError
) as e:
err = getattr(e, 'text', str(e))
if err == 'Connect failed':
err = _('Connect failed')
self.set_connectivity(Connectivity.FAILED)
return False, err
finally:
client.close()
self.set_connectivity(Connectivity.OK)
return True, None

View File

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
#
import socket
import paramiko
from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger, lazyproperty
from orgs.mixins.models import OrgManager
from assets.const import GATEWAY_NAME, Connectivity
from assets.models.platform import Platform
from assets.models.account import Account
from .asset.host import Host
logger = get_logger(__file__)
__all__ = ['Gateway']
class GatewayManager(OrgManager):
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(platform__name=GATEWAY_NAME)
return queryset
def bulk_create(self, objs, batch_size=None, ignore_conflicts=False):
default_platform = Gateway.default_platform()
for obj in objs:
obj.platform = default_platform
return super().bulk_create(objs, batch_size, ignore_conflicts)
class Gateway(Host):
objects = GatewayManager()
class Meta:
proxy = True
def save(self, *args, **kwargs):
self.platform = self.default_platform()
return super().save(*args, **kwargs)
@classmethod
def default_platform(cls):
return Platform.objects.get(name=GATEWAY_NAME, internal=True)
@lazyproperty
def select_account(self):
account = self.accounts.active().order_by('-privileged', '-date_updated').first()
return account
def test_connective(self, local_port=None):
local_port = self.port if local_port is None else local_port
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.SSHClient()
proxy.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if not isinstance(self.select_account, Account):
err = _('No account')
return False, err
logger.debug('Test account: {}'.format(self.select_account))
try:
proxy.connect(
self.address,
port=self.port,
username=self.select_account.username,
password=self.select_account.secret,
pkey=self.select_account.private_key_obj
)
except(
paramiko.AuthenticationException,
paramiko.BadAuthenticationType,
paramiko.SSHException,
paramiko.ChannelException,
paramiko.ssh_exception.NoValidConnectionsError,
socket.gaierror
) as e:
err = str(e)
if err.startswith('[Errno None] Unable to connect to port'):
err = _('Unable to connect to port {port} on {address}')
err = err.format(port=self.port, address=self.address)
elif err == 'Authentication failed.':
err = _('Authentication failed')
elif err == 'Connect failed':
err = _('Connect failed')
self.set_connectivity(Connectivity.FAILED)
return False, err
try:
sock = proxy.get_transport().open_channel(
'direct-tcpip', ('127.0.0.1', local_port), ('127.0.0.1', 0)
)
client.connect(
'127.0.0.1',
sock=sock,
timeout=5,
port=local_port,
username=self.select_account.username,
password=self.select_account.secret,
key_filename=self.select_account.private_key_path,
)
except (
paramiko.SSHException,
paramiko.ssh_exception.SSHException,
paramiko.ChannelException,
paramiko.AuthenticationException,
TimeoutError
) as e:
err = getattr(e, 'text', str(e))
if err == 'Connect failed':
err = _('Connect failed')
self.set_connectivity(Connectivity.FAILED)
return False, err
finally:
client.close()
self.set_connectivity(Connectivity.OK)
return True, None

View File

@ -4,11 +4,11 @@
from .asset import *
from .label import *
from .node import *
from .gateway import *
from .domain import *
from .gathered_user import *
from .favorite_asset import *
from .account import *
from assets.serializers.account.backup import *
from .platform import *
from .cagegory import *
from .automations import *

View File

@ -1,2 +1,3 @@
from .account import *
from .template import *
from .backup import *

View File

@ -67,11 +67,11 @@ class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer)
labels = AssetLabelSerializer(many=True, required=False, label=_('Labels'))
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts'))
automation_enabled_info = serializers.SerializerMethodField()
enabled_info = serializers.SerializerMethodField()
class Meta:
model = Asset
fields_mini = ['id', 'name', 'address', 'automation_enabled_info']
fields_mini = ['id', 'name', 'address', 'enabled_info']
fields_small = fields_mini + ['is_active', 'comment']
fields_fk = ['domain', 'platform', 'platform']
fields_m2m = [
@ -95,11 +95,15 @@ class AssetSerializer(OrgResourceSerializerMixin, WritableNestedModelSerializer)
return names
@staticmethod
def get_automation_enabled_info(obj):
automation = obj.platform.automation
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,

View File

@ -1,18 +1,20 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.drf.serializers import SecretReadableMixin
from common.drf.fields import ObjectRelatedField
from ..serializers import HostSerializer
from ..models import Domain, Gateway, Asset
from ..serializers import GatewaySerializer
from ..models import Domain, Asset
__all__ = ['DomainSerializer', 'DomainWithGatewaySerializer']
class DomainSerializer(BulkOrgResourceModelSerializer):
asset_count = serializers.SerializerMethodField(label=_('Assets amount'))
gateway_count = serializers.SerializerMethodField(label=_('Gateways count'))
gateways = ObjectRelatedField(
many=True, required=False, queryset=Asset.objects, label=_('Gateway')
)
assets = ObjectRelatedField(
many=True, required=False, queryset=Asset.objects, label=_('Asset')
)
@ -21,40 +23,10 @@ class DomainSerializer(BulkOrgResourceModelSerializer):
model = Domain
fields_mini = ['id', 'name']
fields_small = fields_mini + ['comment']
fields_m2m = ['assets']
read_only_fields = ['asset_count', 'gateway_count', 'date_created']
fields_m2m = ['assets', 'gateways']
read_only_fields = ['date_created']
fields = fields_small + fields_m2m + read_only_fields
extra_kwargs = {
'assets': {'required': False, 'label': _('Assets')},
}
@staticmethod
def get_asset_count(obj):
return obj.assets.count()
@staticmethod
def get_gateway_count(obj):
return obj.gateways.count()
class GatewaySerializer(HostSerializer):
effective_accounts = serializers.SerializerMethodField()
class Meta(HostSerializer.Meta):
model = Gateway
fields = HostSerializer.Meta.fields + ['effective_accounts']
@staticmethod
def get_effective_accounts(obj):
accounts = obj.select_accounts.values()
return [
{
'id': account.id,
'username': account.username,
'secret_type': account.secret_type,
} for account in accounts
]
extra_kwargs = {}
class DomainWithGatewaySerializer(BulkOrgResourceModelSerializer):

View File

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
#
from ..serializers import HostSerializer
from ..models import Gateway
__all__ = ['GatewaySerializer']
class GatewaySerializer(HostSerializer):
class Meta(HostSerializer.Meta):
model = Gateway

View File

@ -153,7 +153,7 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
if not self.domain:
return
self.domain: Domain
return self.domain.random_gateway()
return self.domain.select_gateway()
@lazyproperty
def command_filter_acls(self):
@ -161,7 +161,7 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
kwargs = {
'user': self.user,
'asset': self.asset,
'account': self.account,
'account': self.account_object,
}
acls = CommandFilterACL.filter_queryset(**kwargs).valid()
return acls

View File

@ -1,13 +1,16 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from acls.models import CommandGroup
from assets.models import Asset, Account, Platform
from common.drf.fields import ObjectRelatedField
from acls.models import CommandGroup, CommandFilterACL
from assets.models import Asset, Account, Platform, Gateway, Domain
from assets.serializers import PlatformSerializer, AssetProtocolsSerializer
from authentication.models import ConnectionToken
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from perms.serializers.permission import ActionChoicesField
from users.models import User
from perms.serializers.permission import ActionChoicesField
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ..models import ConnectionToken
__all__ = [
'ConnectionTokenSecretSerializer',
@ -53,21 +56,33 @@ class _ConnectionTokenAccountSerializer(serializers.ModelSerializer):
class _ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
""" Gateway """
account = ObjectRelatedField(
required=False, source='select_account', queryset=Account.objects,
attrs=('id', 'name', 'username', 'secret', 'secret_type')
)
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
class Meta:
model = Asset
model = Gateway
fields = [
'id', 'address', 'port',
# 'username', 'password', 'private_key'
'id', 'name', 'address', 'protocols', 'account'
]
class _ConnectionTokenACLCmdGroupSerializer(serializers.ModelSerializer):
""" ACL command group"""
class _ConnectionTokenCommandFilterACLSerializer(serializers.ModelSerializer):
command_groups = ObjectRelatedField(
many=True, required=False, queryset=CommandGroup.objects,
attrs=('id', 'name', 'type', 'content', 'ignore_case', 'pattern'),
label=_('Command group')
)
reviewers = ObjectRelatedField(
many=True, queryset=User.objects, label=_("Reviewers"), required=False
)
class Meta:
model = CommandGroup
model = CommandFilterACL
fields = [
'id', 'type', 'content', 'ignore_case', 'pattern'
'id', 'name', 'command_groups', 'action', 'reviewers', 'priority', 'is_active'
]
@ -87,7 +102,8 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
account = _ConnectionTokenAccountSerializer(read_only=True, source='account_object')
gateway = _ConnectionTokenGatewaySerializer(read_only=True)
platform = _ConnectionTokenPlatformSerializer(read_only=True)
acl_command_groups = _ConnectionTokenACLCmdGroupSerializer(read_only=True, many=True)
domain = ObjectRelatedField(queryset=Domain.objects, required=False, label=_('Domain'))
command_filter_acls = _ConnectionTokenCommandFilterACLSerializer(read_only=True, many=True)
actions = ActionChoicesField()
expire_at = serializers.IntegerField()
expire_now = serializers.BooleanField(label=_('Expired now'), write_only=True, default=True)
@ -97,8 +113,8 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
model = ConnectionToken
fields = [
'id', 'value', 'user', 'asset', 'account',
'platform', 'acl_command_groups', 'protocol',
'gateway', 'actions', 'expire_at', 'expire_now',
'platform', 'command_filter_acls', 'protocol',
'domain', 'gateway', 'actions', 'expire_at', 'expire_now',
'connect_method'
]
extra_kwargs = {

View File

@ -1,9 +1,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from authentication.models import ConnectionToken
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ..models import ConnectionToken
__all__ = [
'ConnectionTokenSerializer', 'SuperConnectionTokenSerializer',
]

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
#
from rest_framework import viewsets
from rest_framework_bulk import BulkModelViewSet
from orgs.mixins.api import OrgBulkModelViewSet
from common.mixins import CommonApiMixin
from ..models import AdHoc
from ..serializers import (
AdHocSerializer
@ -14,7 +14,9 @@ __all__ = [
]
class AdHocViewSet(OrgBulkModelViewSet):
class AdHocViewSet(CommonApiMixin, BulkModelViewSet):
serializer_class = AdHocSerializer
permission_classes = ()
model = AdHoc
def get_queryset(self):
return AdHoc.objects.filter(creator=self.request.user)

View File

@ -1,12 +1,13 @@
from rest_framework import viewsets
from rest_framework_bulk import BulkModelViewSet
from common.mixins import CommonApiMixin
from ops.models import Job, JobExecution
from ops.serializers.job import JobSerializer, JobExecutionSerializer
__all__ = ['JobViewSet', 'JobExecutionViewSet']
from ops.tasks import run_ops_job_execution
from orgs.mixins.api import OrgBulkModelViewSet
def set_task_to_serializer_data(serializer, task):
@ -15,13 +16,12 @@ def set_task_to_serializer_data(serializer, task):
setattr(serializer, "_data", data)
class JobViewSet(OrgBulkModelViewSet):
class JobViewSet(CommonApiMixin, BulkModelViewSet):
serializer_class = JobSerializer
model = Job
permission_classes = ()
def get_queryset(self):
query_set = super().get_queryset()
query_set = Job.objects.filter(creator=self.request.user)
if self.action != 'retrieve':
return query_set.filter(instant=False)
return query_set
@ -45,11 +45,10 @@ class JobViewSet(OrgBulkModelViewSet):
set_task_to_serializer_data(serializer, task)
class JobExecutionViewSet(OrgBulkModelViewSet):
class JobExecutionViewSet(CommonApiMixin, BulkModelViewSet):
serializer_class = JobExecutionSerializer
http_method_names = ('get', 'post', 'head', 'options',)
permission_classes = ()
model = JobExecution
def perform_create(self, serializer):
instance = serializer.save()
@ -57,7 +56,8 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
set_task_to_serializer_data(serializer, task)
def get_queryset(self):
query_set = super().get_queryset()
query_set = JobExecution.objects.filter(creator=self.request.user)
query_set = query_set.filter(creator=self.request.user)
job_id = self.request.query_params.get('job_id')
if job_id:
query_set = query_set.filter(job_id=job_id)

View File

@ -2,7 +2,9 @@ import os
import zipfile
from django.conf import settings
from rest_framework_bulk import BulkModelViewSet
from common.mixins import CommonApiMixin
from orgs.mixins.api import OrgBulkModelViewSet
from ..exception import PlaybookNoValidEntry
from ..models import Playbook
@ -17,7 +19,7 @@ def unzip_playbook(src, dist):
fz.extract(file, dist)
class PlaybookViewSet(OrgBulkModelViewSet):
class PlaybookViewSet(CommonApiMixin, BulkModelViewSet):
serializer_class = PlaybookSerializer
permission_classes = ()
model = Playbook

View File

@ -0,0 +1,34 @@
# Generated by Django 3.2.14 on 2022-12-05 08:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ops', '0027_auto_20221024_1709'),
]
operations = [
migrations.RenameField(
model_name='job',
old_name='owner',
new_name='creator',
),
migrations.RemoveField(
model_name='adhoc',
name='org_id',
),
migrations.RemoveField(
model_name='job',
name='org_id',
),
migrations.RemoveField(
model_name='jobexecution',
name='org_id',
),
migrations.RemoveField(
model_name='playbook',
name='org_id',
),
]

View File

@ -4,15 +4,15 @@ import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from common.db.models import JMSBaseModel
from common.utils import get_logger
from orgs.mixins.models import JMSOrgBaseModel
__all__ = ["AdHoc"]
logger = get_logger(__file__)
class AdHoc(JMSOrgBaseModel):
class AdHoc(JMSBaseModel):
class Modules(models.TextChoices):
shell = 'shell', _('Shell')
winshell = 'win_shell', _('Powershell')
@ -26,7 +26,6 @@ class AdHoc(JMSOrgBaseModel):
creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True)
comment = models.CharField(max_length=1024, default='', verbose_name=_('Comment'), null=True, blank=True)
@property
def row_count(self):
if len(self.args) == 0:

View File

@ -11,12 +11,12 @@ from celery import current_task
__all__ = ["Job", "JobExecution"]
from common.db.models import JMSBaseModel
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner
from ops.mixin import PeriodTaskModelMixin
from orgs.mixins.models import JMSOrgBaseModel
class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
class Job(JMSBaseModel, PeriodTaskModelMixin):
class Types(models.TextChoices):
adhoc = 'adhoc', _('Adhoc')
playbook = 'playbook', _('Playbook')
@ -40,7 +40,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
timeout = models.IntegerField(default=60, 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"))
owner = 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)
assets = models.ManyToManyField('assets.Asset', verbose_name=_("Assets"))
runas = models.CharField(max_length=128, default='root', verbose_name=_('Runas'))
runas_policy = models.CharField(max_length=128, choices=RunasPolicies.choices, default=RunasPolicies.skip,
@ -96,7 +96,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
ordering = ['date_created']
class JobExecution(JMSOrgBaseModel):
class JobExecution(JMSBaseModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
task_id = models.UUIDField(null=True)
status = models.CharField(max_length=16, verbose_name=_('Status'), default='running')

View File

@ -5,11 +5,11 @@ from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
from common.db.models import JMSBaseModel
from ops.exception import PlaybookNoValidEntry
from orgs.mixins.models import JMSOrgBaseModel
class Playbook(JMSOrgBaseModel):
class Playbook(JMSBaseModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'), null=True)
path = models.FileField(upload_to='playbooks/')

View File

@ -4,11 +4,10 @@ from __future__ import unicode_literals
from rest_framework import serializers
from common.drf.fields import ReadableHiddenField
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import AdHoc
class AdHocSerializer(BulkOrgResourceModelSerializer, serializers.ModelSerializer):
class AdHocSerializer(serializers.ModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
row_count = serializers.IntegerField(read_only=True)
size = serializers.IntegerField(read_only=True)

View File

@ -3,11 +3,10 @@ from rest_framework import serializers
from common.drf.fields import ReadableHiddenField
from ops.mixin import PeriodTaskSerializerMixin
from ops.models import Job, JobExecution
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
owner = ReadableHiddenField(default=serializers.CurrentUserDefault())
class JobSerializer(serializers.ModelSerializer, PeriodTaskSerializerMixin):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
run_after_save = serializers.BooleanField(label=_("Run after save"), read_only=True, default=False, required=False)
class Meta:
@ -15,7 +14,7 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
read_only_fields = ["id", "date_last_run", "date_created", "date_updated", "average_time_cost",
"run_after_save"]
fields = read_only_fields + [
"name", "instant", "type", "module", "args", "playbook", "assets", "runas_policy", "runas", "owner",
"name", "instant", "type", "module", "args", "playbook", "assets", "runas_policy", "runas", "creator",
"use_parameter_define",
"parameters_define",
"timeout",
@ -27,10 +26,12 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
class JobExecutionSerializer(serializers.ModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = JobExecution
read_only_fields = ["id", "task_id", "timedelta", "time_cost", 'is_finished', 'date_start', 'date_created',
'is_success', 'task_id', 'short_id', 'job_type']
'is_success', 'task_id', 'short_id', 'job_type', 'creator']
fields = read_only_fields + [
"job", "parameters"
]

View File

@ -12,7 +12,7 @@ def parse_playbook_name(path):
return file_name.split(".")[-2]
class PlaybookSerializer(BulkOrgResourceModelSerializer, serializers.ModelSerializer):
class PlaybookSerializer(serializers.ModelSerializer):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
path = serializers.FileField(required=False)