mirror of https://github.com/jumpserver/jumpserver
t po v3
:wqMerge branch 'v3' of github.com:jumpserver/jumpserver into v3pull/9248/head
commit
1b1c91bab0
|
@ -4,7 +4,6 @@ from .automations import *
|
|||
from .category import *
|
||||
from .domain import *
|
||||
from .favorite_asset import *
|
||||
from .gathered_user import *
|
||||
from .label import *
|
||||
from .mixin import *
|
||||
from .node import *
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from assets.models import GatheredUser
|
||||
|
||||
from ..serializers import GatheredUserSerializer
|
||||
from ..filters import AssetRelatedByNodeFilterBackend
|
||||
|
||||
|
||||
__all__ = ['GatheredUserViewSet']
|
||||
|
||||
|
||||
class GatheredUserViewSet(OrgModelViewSet):
|
||||
model = GatheredUser
|
||||
serializer_class = GatheredUserSerializer
|
||||
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
||||
|
||||
filterset_fields = ['asset', 'username', 'present', 'asset__address', 'asset__name', 'asset_id']
|
||||
search_fields = ['username', 'asset__address', 'asset__name']
|
|
@ -1,8 +1,9 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import get_logger
|
||||
from assets.const import AutomationTypes
|
||||
from assets.const import AutomationTypes, Source
|
||||
from orgs.utils import tmp_to_org
|
||||
from .filter import GatherAccountsFilter
|
||||
from ...models import GatheredUser
|
||||
from ..base.manager import BasePlaybookManager
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
@ -26,20 +27,33 @@ class GatherAccountsManager(BasePlaybookManager):
|
|||
result = GatherAccountsFilter(host).run(self.method_id_meta_mapper, result)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def bulk_create_accounts(asset, result):
|
||||
account_objs = []
|
||||
account_model = asset.accounts.model
|
||||
account_usernames = set(asset.accounts.values_list('username', flat=True))
|
||||
with tmp_to_org(asset.org_id):
|
||||
accounts_dict = {}
|
||||
for username, data in result.items():
|
||||
comment = ''
|
||||
d = {'asset': asset, 'username': username, 'name': username, 'source': Source.COLLECTED}
|
||||
if data.get('date'):
|
||||
comment += f"{_('Date last login')}: {data['date']}\n "
|
||||
if data.get('address'):
|
||||
comment += f"{_('IP last login')}: {data['address'][:32]}"
|
||||
d['comment'] = comment
|
||||
accounts_dict[username] = d
|
||||
for username, data in accounts_dict.items():
|
||||
if username in account_usernames:
|
||||
continue
|
||||
account_objs.append(account_model(**data))
|
||||
account_model.objects.bulk_create(account_objs)
|
||||
|
||||
def on_host_success(self, host, result):
|
||||
info = result.get('debug', {}).get('res', {}).get('info', {})
|
||||
asset = self.host_asset_mapper.get(host)
|
||||
org_id = asset.org_id
|
||||
if asset and info:
|
||||
result = self.filter_success_result(host, info)
|
||||
with tmp_to_org(org_id):
|
||||
GatheredUser.objects.filter(asset=asset, present=True).update(present=False)
|
||||
for username, data in result.items():
|
||||
defaults = {'asset': asset, 'present': True, 'username': username}
|
||||
if data.get('date'):
|
||||
defaults['date_last_login'] = data['date']
|
||||
if data.get('address'):
|
||||
defaults['ip_last_login'] = data['address'][:32]
|
||||
GatheredUser.objects.update_or_create(defaults=defaults, asset=asset, username=username)
|
||||
self.bulk_create_accounts(asset, result)
|
||||
else:
|
||||
logger.error("Not found info".format(host))
|
||||
|
|
|
@ -13,3 +13,14 @@ class SecretType(TextChoices):
|
|||
SSH_KEY = 'ssh_key', _('SSH key')
|
||||
ACCESS_KEY = 'access_key', _('Access key')
|
||||
TOKEN = 'token', _('Token')
|
||||
|
||||
|
||||
class AliasAccount(TextChoices):
|
||||
ALL = '@ALL', _('All')
|
||||
INPUT = '@INPUT', _('Manual input')
|
||||
USER = '@USER', _('Dynamic user')
|
||||
|
||||
|
||||
class Source(TextChoices):
|
||||
LOCAL = 'local', _('Local')
|
||||
COLLECTED = 'collected', _('Collected')
|
||||
|
|
|
@ -126,17 +126,6 @@ class LabelFilterBackend(filters.BaseFilterBackend):
|
|||
return queryset
|
||||
|
||||
|
||||
class AssetRelatedByNodeFilterBackend(AssetByNodeFilterBackend):
|
||||
def filter_node_related_all(self, queryset, node):
|
||||
return queryset.filter(
|
||||
Q(asset__nodes__key__istartswith=f'{node.key}:') |
|
||||
Q(asset__nodes__key=node.key)
|
||||
).distinct()
|
||||
|
||||
def filter_node_related_direct(self, queryset, node):
|
||||
return queryset.filter(asset__nodes__key=node.key).distinct()
|
||||
|
||||
|
||||
class IpInFilterBackend(filters.BaseFilterBackend):
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
ips = request.query_params.get('ips')
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 3.2.16 on 2022-12-27 09:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0118_auto_20221227_1504'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='account',
|
||||
name='source',
|
||||
field=models.CharField(default='local', max_length=30, verbose_name='Source'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='GatheredUser',
|
||||
),
|
||||
]
|
|
@ -7,7 +7,6 @@ from .gateway import *
|
|||
from .domain import *
|
||||
from .node import *
|
||||
from .utils import *
|
||||
from .gathered_user import *
|
||||
from .favorite_asset import *
|
||||
from .account import *
|
||||
from .backup import *
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from simple_history.models import HistoricalRecords
|
||||
|
||||
from common.utils import lazyproperty
|
||||
from ..const import AliasAccount, Source
|
||||
from .base import AbsConnectivity, BaseAccount
|
||||
|
||||
__all__ = ['Account', 'AccountTemplate']
|
||||
|
@ -40,11 +41,6 @@ class AccountHistoricalRecords(HistoricalRecords):
|
|||
|
||||
|
||||
class Account(AbsConnectivity, BaseAccount):
|
||||
class AliasAccount(models.TextChoices):
|
||||
ALL = '@ALL', _('All')
|
||||
INPUT = '@INPUT', _('Manual input')
|
||||
USER = '@USER', _('Dynamic user')
|
||||
|
||||
asset = models.ForeignKey(
|
||||
'assets.Asset', related_name='accounts',
|
||||
on_delete=models.CASCADE, verbose_name=_('Asset')
|
||||
|
@ -55,6 +51,7 @@ class Account(AbsConnectivity, BaseAccount):
|
|||
)
|
||||
version = models.IntegerField(default=0, verbose_name=_('Version'))
|
||||
history = AccountHistoricalRecords(included_fields=['id', 'secret', 'secret_type', 'version'])
|
||||
source = models.CharField(max_length=30, default=Source.LOCAL, verbose_name=_('Source'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Account')
|
||||
|
@ -89,12 +86,12 @@ class Account(AbsConnectivity, BaseAccount):
|
|||
@classmethod
|
||||
def get_manual_account(cls):
|
||||
""" @INPUT 手动登录的账号(any) """
|
||||
return cls(name=cls.AliasAccount.INPUT.label, username=cls.AliasAccount.INPUT.value, secret=None)
|
||||
return cls(name=AliasAccount.INPUT.label, username=AliasAccount.INPUT.value, secret=None)
|
||||
|
||||
@classmethod
|
||||
def get_user_account(cls, username):
|
||||
""" @USER 动态用户的账号(self) """
|
||||
return cls(name=cls.AliasAccount.USER.label, username=cls.AliasAccount.USER.value)
|
||||
return cls(name=AliasAccount.USER.label, username=AliasAccount.USER.value)
|
||||
|
||||
def get_su_from_accounts(self):
|
||||
""" 排除自己和以自己为 su-from 的账号 """
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orgs.mixins.models import JMSOrgBaseModel
|
||||
|
||||
__all__ = ['GatheredUser']
|
||||
|
||||
|
||||
class GatheredUser(JMSOrgBaseModel):
|
||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset"))
|
||||
username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username'))
|
||||
present = models.BooleanField(default=True, verbose_name=_("Present"))
|
||||
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login"))
|
||||
ip_last_login = models.CharField(max_length=39, default='', verbose_name=_("IP last login"))
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.asset.name
|
||||
|
||||
@property
|
||||
def ip(self):
|
||||
return self.asset.address
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('GatherUser')
|
||||
ordering = ['asset']
|
||||
|
||||
def __str__(self):
|
||||
return '{}: {}'.format(self.asset.name, self.username)
|
|
@ -6,7 +6,6 @@ from .label import *
|
|||
from .node import *
|
||||
from .gateway import *
|
||||
from .domain import *
|
||||
from .gathered_user import *
|
||||
from .favorite_asset import *
|
||||
from .account import *
|
||||
from .platform import *
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from assets.const import SecretType
|
||||
from assets.const import SecretType, Source
|
||||
from assets.models import Account, AccountTemplate, Asset
|
||||
from assets.tasks import push_accounts_to_assets
|
||||
from common.drf.fields import ObjectRelatedField, LabeledChoiceField
|
||||
|
@ -74,6 +74,7 @@ class AccountAssetSerializer(serializers.ModelSerializer):
|
|||
|
||||
class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
|
||||
asset = AccountAssetSerializer(label=_('Asset'))
|
||||
source = LabeledChoiceField(choices=Source.choices, label=_("Source"), read_only=True)
|
||||
su_from = ObjectRelatedField(
|
||||
required=False, queryset=Account.objects, allow_null=True, allow_empty=True,
|
||||
label=_('Su from'), attrs=('id', 'name', 'username')
|
||||
|
@ -83,7 +84,7 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer):
|
|||
model = Account
|
||||
fields = BaseAccountSerializer.Meta.fields \
|
||||
+ ['su_from', 'version', 'asset'] \
|
||||
+ ['template', 'push_now']
|
||||
+ ['template', 'push_now', 'source']
|
||||
extra_kwargs = {
|
||||
**BaseAccountSerializer.Meta.extra_kwargs,
|
||||
'name': {'required': False, 'allow_null': True},
|
||||
|
|
|
@ -67,6 +67,7 @@ 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=_('Account'))
|
||||
|
||||
class Meta:
|
||||
model = Asset
|
||||
|
@ -85,6 +86,7 @@ class AssetSerializer(BulkOrgResourceSerializerMixin, WritableNestedModelSeriali
|
|||
extra_kwargs = {
|
||||
'name': {'label': _("Name")},
|
||||
'address': {'label': _('Address')},
|
||||
'nodes_display': {'label': _('Node path')},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||
from common.drf.fields import ObjectRelatedField
|
||||
from ..models import GatheredUser, Asset
|
||||
|
||||
|
||||
class GatheredUserSerializer(OrgResourceModelSerializerMixin):
|
||||
asset = ObjectRelatedField(queryset=Asset.objects, label=_('Asset'))
|
||||
|
||||
class Meta:
|
||||
model = GatheredUser
|
||||
fields_mini = ['id']
|
||||
fields_small = fields_mini + [
|
||||
'username', 'ip_last_login', 'present', 'name',
|
||||
'date_last_login', 'date_created', 'date_updated'
|
||||
]
|
||||
fields_fk = ['asset', 'ip']
|
||||
fields = fields_small + fields_fk
|
||||
read_only_fields = fields
|
||||
extra_kwargs = {
|
||||
'name': {'label': _("Hostname")},
|
||||
'ip': {'label': 'IP'},
|
||||
}
|
|
@ -23,7 +23,6 @@ router.register(r'labels', api.LabelViewSet, 'label')
|
|||
router.register(r'nodes', api.NodeViewSet, 'node')
|
||||
router.register(r'domains', api.DomainViewSet, 'domain')
|
||||
router.register(r'gateways', api.GatewayViewSet, 'gateway')
|
||||
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
||||
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
||||
router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup')
|
||||
router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution')
|
||||
|
@ -51,7 +50,6 @@ urlpatterns = [
|
|||
name='account-secret-history'),
|
||||
|
||||
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
|
||||
# path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
||||
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
|
||||
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
|
||||
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
||||
|
|
|
@ -17,12 +17,10 @@ MODELS_NEED_RECORD = (
|
|||
"LoginAssetACL",
|
||||
"LoginConfirmSetting",
|
||||
# assets
|
||||
'Asset', 'Node', 'AdminUser', 'SystemUser', 'Domain', 'Gateway', 'CommandFilterRule',
|
||||
'Asset', 'Node', 'Domain', 'Gateway', 'CommandFilterRule',
|
||||
'CommandFilter', 'Platform', 'Label',
|
||||
# applications
|
||||
'Application',
|
||||
# account
|
||||
'AuthBook',
|
||||
'Account',
|
||||
# orgs
|
||||
"Organization",
|
||||
# settings
|
||||
|
@ -36,8 +34,7 @@ MODELS_NEED_RECORD = (
|
|||
# rbac
|
||||
'Role', 'SystemRole', 'OrgRole', 'RoleBinding', 'OrgRoleBinding', 'SystemRoleBinding',
|
||||
# xpack
|
||||
'License', 'Account', 'SyncInstanceTask', 'ChangeAuthPlan',
|
||||
'GatherUserTask', 'Interface',
|
||||
'License', 'Account', 'SyncInstanceTask', 'Interface',
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a533ee3b36cdd61fe239be159cc80362e5cd358f134857987029bde8e4c1231e
|
||||
size 119530
|
||||
oid sha256:93524bfafbdfcb59e20920619c934b1df7b040573e98d1f1f481c73126110630
|
||||
size 119578
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-12-26 20:28+0800\n"
|
||||
"POT-Creation-Date: 2022-12-27 12:11+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -1308,12 +1308,16 @@ msgstr "プロトコル"
|
|||
msgid "Address"
|
||||
msgstr "アドレス"
|
||||
|
||||
#: assets/serializers/asset/common.py:156
|
||||
#: assets/serializers/asset/common.py:89
|
||||
msgid "Node path"
|
||||
msgstr "ノードパスです"
|
||||
|
||||
#: assets/serializers/asset/common.py:157
|
||||
#, fuzzy
|
||||
msgid "Platform not exist"
|
||||
msgstr "アプリが存在しません"
|
||||
|
||||
#: assets/serializers/asset/common.py:172
|
||||
#: assets/serializers/asset/common.py:173
|
||||
#, fuzzy
|
||||
msgid "Protocol is required: {}"
|
||||
msgstr "プロトコル重複: {}"
|
||||
|
@ -7443,9 +7447,6 @@ msgstr "コミュニティ版"
|
|||
#~ msgid "Apps amount"
|
||||
#~ msgstr "アプリの量"
|
||||
|
||||
#~ msgid "Nodes amount"
|
||||
#~ msgstr "ノード量"
|
||||
|
||||
#~ msgid "Login mode display"
|
||||
#~ msgstr "ログインモード表示"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7496f27202b5dcd9d677a70f9efc86c30b74fc306d6b5fa7202238c15c845971
|
||||
size 105814
|
||||
oid sha256:8ea4225734c8f9df88bb72379ccd48b50d833766fbde3ce64cacf5eb6f87082b
|
||||
size 105853
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-12-26 20:28+0800\n"
|
||||
"POT-Creation-Date: 2022-12-27 12:11+0800\n"
|
||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -1246,11 +1246,15 @@ msgstr "协议组"
|
|||
msgid "Address"
|
||||
msgstr "地址"
|
||||
|
||||
#: assets/serializers/asset/common.py:156
|
||||
#: assets/serializers/asset/common.py:89
|
||||
msgid "Node path"
|
||||
msgstr "节点路径"
|
||||
|
||||
#: assets/serializers/asset/common.py:157
|
||||
msgid "Platform not exist"
|
||||
msgstr "平台不存在"
|
||||
|
||||
#: assets/serializers/asset/common.py:172
|
||||
#: assets/serializers/asset/common.py:173
|
||||
msgid "Protocol is required: {}"
|
||||
msgstr "协议是必填的: {}"
|
||||
|
||||
|
@ -7217,9 +7221,6 @@ msgstr "社区版"
|
|||
#~ msgid "Apps amount"
|
||||
#~ msgstr "应用数量"
|
||||
|
||||
#~ msgid "Nodes amount"
|
||||
#~ msgstr "节点数量"
|
||||
|
||||
#~ msgid "Login mode display"
|
||||
#~ msgstr "认证方式名称"
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ class DefaultCallback:
|
|||
'failed': 'failed',
|
||||
'running': 'running',
|
||||
'pending': 'pending',
|
||||
'timeout': 'timeout',
|
||||
'unknown': 'unknown'
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)'),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -198,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,
|
||||
|
@ -238,7 +238,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):
|
||||
|
|
|
@ -15,7 +15,7 @@ from .serializers import (
|
|||
from users.models import User, UserGroup
|
||||
from assets.models import (
|
||||
Asset, Domain, Label, Node,
|
||||
CommandFilter, CommandFilterRule, GatheredUser
|
||||
CommandFilter, CommandFilterRule
|
||||
)
|
||||
from perms.models import AssetPermission
|
||||
from orgs.utils import current_org, tmp_to_root_org
|
||||
|
@ -28,8 +28,7 @@ logger = get_logger(__file__)
|
|||
# 部分 org 相关的 model,需要清空这些数据之后才能删除该组织
|
||||
org_related_models = [
|
||||
User, UserGroup, Asset, Label, Domain, Node, Label,
|
||||
CommandFilter, CommandFilterRule, GatheredUser,
|
||||
AssetPermission,
|
||||
CommandFilter, CommandFilterRule, AssetPermission,
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from rest_framework.exceptions import PermissionDenied, NotFound
|
|||
|
||||
from assets.utils import KubernetesTree
|
||||
from assets.models import Asset, Account
|
||||
from assets.const import AliasAccount
|
||||
from assets.api import SerializeToTreeNodeMixin
|
||||
from authentication.models import ConnectionToken
|
||||
from common.utils import get_object_or_none, lazyproperty
|
||||
|
@ -157,7 +158,7 @@ class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
|
|||
raise NotFound('Account is not found')
|
||||
account = accounts[0]
|
||||
if account.username in [
|
||||
Account.AliasAccount.INPUT, Account.AliasAccount.USER
|
||||
AliasAccount.INPUT, AliasAccount.USER
|
||||
]:
|
||||
return token.input_secret
|
||||
else:
|
||||
|
|
|
@ -13,6 +13,7 @@ from common.utils import date_expired_default
|
|||
from common.utils.timezone import local_now
|
||||
|
||||
from perms.const import ActionChoices
|
||||
from assets.const import AliasAccount
|
||||
|
||||
__all__ = ['AssetPermission', 'ActionChoices']
|
||||
|
||||
|
@ -38,7 +39,7 @@ class AssetPermissionQuerySet(models.QuerySet):
|
|||
|
||||
def filter_by_accounts(self, accounts):
|
||||
q = Q(accounts__contains=list(accounts)) | \
|
||||
Q(accounts__contains=Account.AliasAccount.ALL.value)
|
||||
Q(accounts__contains=AliasAccount.ALL.value)
|
||||
return self.filter(q)
|
||||
|
||||
|
||||
|
@ -127,7 +128,7 @@ class AssetPermission(JMSOrgBaseModel):
|
|||
"""
|
||||
asset_ids = self.get_all_assets(flat=True)
|
||||
q = Q(asset_id__in=asset_ids)
|
||||
if Account.AliasAccount.ALL not in self.accounts:
|
||||
if AliasAccount.ALL not in self.accounts:
|
||||
q &= Q(username__in=self.accounts)
|
||||
accounts = Account.objects.filter(q).order_by('asset__name', 'name', 'username')
|
||||
if not flat:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from assets.models import Account
|
||||
from assets.const import AliasAccount
|
||||
from .permission import AssetPermissionUtil
|
||||
|
||||
__all__ = ['PermAccountUtil']
|
||||
|
@ -44,21 +45,21 @@ class PermAccountUtil(AssetPermissionUtil):
|
|||
cleaned_accounts_expired = defaultdict(list)
|
||||
|
||||
# @ALL 账号先处理,后面的每个最多映射一个账号
|
||||
all_action_bit = alias_action_bit_mapper.pop(Account.AliasAccount.ALL, None)
|
||||
all_action_bit = alias_action_bit_mapper.pop(AliasAccount.ALL, None)
|
||||
if all_action_bit:
|
||||
for account in asset_accounts:
|
||||
cleaned_accounts_action_bit[account] |= all_action_bit
|
||||
cleaned_accounts_expired[account].extend(
|
||||
alias_expired_mapper[Account.AliasAccount.ALL]
|
||||
alias_expired_mapper[AliasAccount.ALL]
|
||||
)
|
||||
|
||||
for alias, action_bit in alias_action_bit_mapper.items():
|
||||
if alias == Account.AliasAccount.USER:
|
||||
if alias == AliasAccount.USER:
|
||||
if user.username in username_account_mapper:
|
||||
account = username_account_mapper[user.username]
|
||||
else:
|
||||
account = Account.get_user_account(user.username)
|
||||
elif alias == Account.AliasAccount.INPUT:
|
||||
elif alias == AliasAccount.INPUT:
|
||||
account = Account.get_manual_account()
|
||||
elif alias in username_account_mapper:
|
||||
account = username_account_mapper[alias]
|
||||
|
|
Loading…
Reference in New Issue