mirror of https://github.com/jumpserver/jumpserver
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
parent
9ef5f17d5e
commit
7842e3e5ab
|
@ -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 *
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from assets.const import GATEWAY_NAME
|
||||
from .common import Asset
|
||||
|
||||
__all__ = ['Host']
|
||||
|
||||
|
||||
class Host(Asset):
|
||||
pass
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 *
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
from .account import *
|
||||
from .template import *
|
||||
from .backup import *
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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',
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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/')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue